Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Test

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
test:
name: Lint, typecheck, build & test
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile

# Lint only. The repo is not fully Biome-formatted yet, so `biome ci`
# (which also enforces formatting/import order) is not used here.
- name: Lint
run: npx biome lint .

- name: Type check
run: npx tsc --noEmit

- name: Build
run: yarn build

- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}

- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium

- name: Run tests with coverage
run: yarn coverage

- name: Upload coverage report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 14
11 changes: 8 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

# testing
/coverage
**/__screenshots__

# build
/dist
Expand All @@ -21,8 +22,12 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*

# storybook
storybook-static

# cache
.eslintcache
.gstack/

# demo app
/demo/node_modules
/demo/dist
/demo/.vercel
.vercel
6 changes: 4 additions & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
!/dist
/dist/**/stories
!/src
/src/**/stories
/src/**/*.test.*
/src/**/__screenshots__
/src/jest-dom.d.ts
/demo
30 changes: 0 additions & 30 deletions .storybook/main.js

This file was deleted.

3 changes: 0 additions & 3 deletions .storybook/preview.js

This file was deleted.

76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@

A React cron editor built with [material ui](https://material-ui.com/)

For **demo** and **usage** clone repo and run `yarn && yarn dev`.
For a **live demo**, run the demo app in [`demo/`](./demo):

```bash
yarn demo:install # one-time: install the demo app's deps
yarn demo # http://localhost:5173
```

The demo is a small [TanStack Router](https://tanstack.com/router) + Vite SPA that
imports the component straight from `src/`, so it always reflects the working tree.

![material-ui-cron demo](/docs/material-ui-cron-demo.png)

Expand Down Expand Up @@ -103,6 +111,72 @@ We are welcoming translation contributions from the community.
/>
```

## Testing

Tests run on [Vitest](https://vitest.dev/) and are split into two projects:

- **`unit`** — pure-logic tests that run in Node (cron validation in `utils.ts`,
the cron ⇄ field-atom derivations in `selector.ts`).
- **`browser`** — component tests that render the real `<Scheduler />` in a
headless Chromium via [Playwright](https://playwright.dev/). A real browser is
required: MUI's `Autocomplete` triggers an infinite update loop under jsdom.

Tests live next to the code they cover:

| File | Project | What it covers |
| --- | --- | --- |
| `src/utils.test.ts` | unit | every cron-part validator + helpers |
| `src/selector.test.ts` | unit | all cron-part derivation atoms and the writer |
| `src/scheduler.browser.test.tsx` | browser | end-to-end `<Scheduler />` behaviour |

### Commands

```bash
# Install the Chromium browser once (needed for the browser project)
npx playwright install chromium

yarn test # run the whole suite once (unit + browser)
yarn test:unit # run only the Node unit project
yarn test:browser # run only the Chromium browser project
yarn test:watch # watch mode
yarn coverage # run everything and emit a coverage report
```

`yarn coverage` uses the V8 provider and writes a report to `coverage/`
(`text` summary in the console, plus `html` and `json-summary`).

### Continuous integration

`.github/workflows/test.yml` runs on pushes and pull requests to `main`. It
lints (`biome lint`), type-checks (`tsc --noEmit`), builds, installs Chromium,
runs `yarn coverage`, and uploads the coverage report as a build artifact.

## Demo & deployment

The [`demo/`](./demo) app is deployed to Vercel, which builds a fresh preview for
**every branch and pull request** and comments the live URL on the PR.

The build is driven by [`vercel.json`](./vercel.json) at the repo root:

| Step | Command |
| --- | --- |
| Install | `yarn --cwd demo install` |
| Build | `yarn --cwd demo build` |
| Output | `demo/dist` |

### One-time Vercel setup

1. Create a Vercel project and link it to this GitHub repository
(Vercel dashboard → *Add New… → Project*, or `npx vercel link`).
2. Leave the **Root Directory** as the repo root — `vercel.json` already points
the build at `demo/`. (The demo aliases the library from `../src`, so the whole
repo must be present at build time.)
3. That's it. Vercel's Git integration produces a Preview Deployment for each push
to a branch / PR and a Production Deployment for `main`.

No secrets or GitHub Actions workflow are required — preview URLs come from
Vercel's native Git integration.

## Acknowledgement

This library was developed as a part of [Udaan](https://udaan.com/)'s Data
Expand Down
19 changes: 0 additions & 19 deletions debug-storybook.log

This file was deleted.

40 changes: 40 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# material-ui-cron · demo

A small [TanStack Router](https://tanstack.com/router) + [Vite](https://vitejs.dev/)
single-page app that demos the `material-ui-cron` component.

It imports the library **straight from the repo source** (`../src`) via a Vite
alias, so the demo always reflects the current working tree — no build or
`npm link` step required.

## Run locally

```bash
# from the repo root
yarn demo # installs nothing; runs `yarn --cwd demo dev`

# or directly
cd demo
yarn install
yarn dev # http://localhost:5173
```

Other scripts (run inside `demo/`):

```bash
yarn build # production build → demo/dist
yarn preview # preview the production build
yarn typecheck # tsc --noEmit
```

## Deployment (Vercel)

Preview deployments for every branch and pull request are produced by Vercel's
native Git integration. The build is configured by `vercel.json` at the repo
root:

- **Install:** `yarn --cwd demo install`
- **Build:** `yarn --cwd demo build`
- **Output:** `demo/dist`

See the root `README.md` → *Demo & deployment* for the one-time Vercel setup.
16 changes: 16 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Live demo of material-ui-cron — a React cron editor built with Material UI."
/>
<title>material-ui-cron · demo</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
33 changes: 33 additions & 0 deletions demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "material-ui-cron-demo",
"private": true,
"version": "0.0.0",
"type": "module",
"description": "Live demo app for the material-ui-cron library (TanStack Router + Vite).",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.4",
"@mui/material": "^7.3.4",
"@mui/system": "^7.3.3",
"@tanstack/react-router": "^1",
"cronstrue": "^3.3.0",
"jotai": "^2.15.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@types/node": "^24.7.1",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react": "^5.0.4",
"typescript": "^5.9.3",
"vite": "^7.1.9"
}
}
28 changes: 28 additions & 0 deletions demo/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { RouterProvider } from '@tanstack/react-router';
import { createRoot } from 'react-dom/client';
import { router } from './router';

const theme = createTheme({
palette: {
mode: 'light',
primary: { main: '#1565c0' },
},
});

const container = document.getElementById('root');
if (!container) {
throw new Error('Root container #root not found');
}

// NOTE: React.StrictMode is intentionally omitted. The Scheduler keeps its cron
// state in module-level Jotai atoms and resets them in an unmount effect, so
// StrictMode's mount→unmount→remount cycle wipes the initial `cron` prop before
// the first paint (the field would show `0 0 * * *` instead of the passed value).
createRoot(container).render(
<ThemeProvider theme={theme}>
<CssBaseline />
<RouterProvider router={router} />
</ThemeProvider>,
);
Loading
Loading