Skip to content

v1.1.2 fix: close scheduler issues #16/#19/#20 + add tests & demo - #26

Merged
baymac merged 10 commits into
mainfrom
baymac/close-open-issues
May 29, 2026
Merged

v1.1.2 fix: close scheduler issues #16/#19/#20 + add tests & demo#26
baymac merged 10 commits into
mainfrom
baymac/close-open-issues

Conversation

@baymac

@baymac baymac commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the open scheduler bugs and adds the testing/demo infrastructure to prove and showcase the fixes.

Bug fixes (Jotai scheduler)

  • Gives error to "0 1/4 * * *"  #190 1/4 * * * (plain n/m step) no longer reports "Incorrect syntax". Step validation is consolidated into isValidStepPart, which branches on what precedes the slash (*, x-y, or plain n). The generate{Minute,Hour,DayOfMonth} parsers now also populate the range-start atom for the plain n/m case.
  • Changing cron prop does not rerender component #20 — Changing the controlled cron prop now re-renders correctly. The writer derives the period from the highest non-* segment and sets it unconditionally, so broadening (30 9 * * 1* * * * *) resets a now-stale period instead of staying stuck. A round-trip guard (currentDerived) prevents a manual field/period edit from snapping back.
  • Fix closing of few autocomplete after selection #16 — Single-value autocompletes close after selection. blurOnSelect is forced on single selects (they are rendered as multiple Autocompletes, whose re-render churn otherwise left the popup open).
  • Infinite loop fix — The two opposing prop↔atom sync effects are collapsed into one effect that propagates only the side that actually changed, fixing the "Maximum update depth exceeded" ping-pong when the initial cron prop differed from the atom default. CronExp now writes the debounced value so debouncing affects the value, not just timing.

Testing infrastructure

  • Vitest unit suite (src/utils.test.ts, src/selector.test.ts) + browser suite (src/scheduler.browser.test.tsx) via @vitest/browser + Playwright Chromium.
  • GitHub Actions test.yml: lint → typecheck → build → coverage, with coverage artifact upload.

Demo migration

  • Storybook replaced with a TanStack-router Vite demo app under demo/, wired for Vercel preview deployments (vercel.json).

Test Coverage

All 13 changed code paths have tests (100%). 118 tests passing (78 utils + 25 selector + 15 browser), including regression tests for #16, #18, #19, #20 and a localized-label (#17) case.

Pre-Landing Review

No issues found. Logic surface reviewed (selector.ts, utils.ts, Scheduler.tsx, CronExp.tsx, CustomSelect.tsx); typecheck passes, Biome lint clean, build succeeds. The single-effect two-way binding converges in one render and is robust to an unstable setCron prop.

Design Review

Design review (lite): clean. Changes are behavioral, not visual — no AI slop, no mechanical CSS issues.

Test plan

  • All Vitest tests pass (118 tests, 0 failures) — unit + browser
  • tsc --noEmit passes
  • biome lint . clean
  • yarn build succeeds (esm + cjs + d.ts)

🤖 Generated with Claude Code

baymac and others added 6 commits May 29, 2026 10:21
#19 — `0 1/4 * * *` was rejected. The `/`-branch in the minute/hour/
day-of-month validators routed a plain `n/m` step into REGEX_EVERY_HYPEN
(which requires a hyphen range) and failed. Extract a shared isValidStepPart
that branches on the content before the slash: `*` -> REGEX_ALL,
`x-y` -> REGEX_EVERY_HYPEN, plain `n` -> REGEX_EVERY. Also set the parsed
range start/end for the plain `n/m` form in generate*.

#20 — changing the `cron` prop to a broader expression left the period stuck
on the previous (narrower) value. The writer only ever bumped the period up;
now it derives the period from the highest non-`*` segment and sets it
unconditionally, guarded by a round-trip check so a manual period selection on
default fields does not snap back down.

#16 — single-value autocompletes could stay open after a selection because
re-render churn (recomputed range-time option arrays) kept the popup mounted.
Add blurOnSelect for single selects in CustomSelect to force them closed;
multi-selects still stay open for further picks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set up an end-to-end test stack:

- Vitest with two projects: `unit` (Node) for pure logic and `browser`
  (Playwright/Chromium) for rendering the real <Scheduler />. jsdom is not
  viable — MUI's Autocomplete infinite-loops under it.
- 114 tests: utils.test.ts (validators/helpers, incl. the #19 regression),
  selector.test.ts (derivation atoms + writer), scheduler.browser.test.tsx
  (end-to-end behaviour incl. the #19/#20/#16 fixes).
- V8 coverage report (text/html/json-summary) via `yarn coverage`
  (~88% statements / 87% branches / 93% functions).
- Storybook `.play` regression stories for #19 and #20.
- GitHub Actions workflow (.github/workflows/test.yml): lint, typecheck,
  build, Chromium install, coverage, artifact upload.
- Pin vite to 7.1.9 via resolutions to avoid a dual Vite instance pulled by
  Vitest. Document the test setup in the README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove Storybook entirely (config, stories, build artifacts, deps, scripts)
and replace it with a small TanStack Router + Vite SPA in demo/ that imports
the library straight from ../src via a Vite alias, so the demo always reflects
the working tree with no build step.

Vercel builds the demo (vercel.json: install/build in demo/, output demo/dist)
so every branch and PR gets a live preview URL via the native Git integration.

- delete .storybook/, src/stories/, storybook-static, debug-storybook.log
- drop @storybook/* + storybook devDeps; swap dev/storybook/build-storybook
  scripts for demo/demo:install/demo:build
- demo/: TanStack Router SPA (Admin toggle, en/zh_CN locale switch, presets,
  live cron + cronstrue readout); StrictMode omitted because the Scheduler's
  unmount-time atom reset wipes the initial cron prop under double-mount
- README: document the demo app and one-time Vercel setup

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the two opposing prop<->atom sync effects with one effect that
propagates only the side that actually changed, fixing the "Maximum update
depth exceeded" ping-pong when the initial `cron` prop differs from the atom
default. Use the debounced value in CronExp so debouncing affects the written
value, not just timing. Force blurOnSelect on single-value autocompletes so
they close after selection despite re-render churn.

Add browser regression tests for time-range autocomplete close (#16),
disabled-input legibility and multi-select chips (#18), and localized labels.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…urce

The demo aliases material-ui-cron to ../src, whose imports (notably
cronstrue/i18n) resolve against the repo-root node_modules. The previous
installCommand only installed demo/ deps, so Vercel failed with
'Rollup failed to resolve import cronstrue/i18n'. Install root deps first.
baymac and others added 3 commits May 29, 2026 17:23
The published tarball was shipping src/*.test.*, jest-dom.d.ts and a
Playwright screenshot PNG. Exclude them so only the library source +
dist ship to consumers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switching the locale left the selected values (period, at/every, week,
month) one language behind: pick Chinese and the period still read
"day"; switch back to English and it read "天". The shared Jotai atoms
held option objects whose label was localized at write time, and the
"reset on unmount" effect — which carried currentLocale + the atom
setters in its dependency array — fired its cleanup on every locale
change, rewriting the atoms with the *previously captured* locale's
defaults.

- Make the reset effect truly unmount-only: empty deps, read the latest
  locale through a ref so cleanup still restores the right defaults.
- Add an effect that, when the locale changes, re-maps each selected
  value onto the matching option in the new locale (option values are
  locale-stable; only labels translate), preserving the user's selection
  and refreshing its label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renders the scheduler, switches en -> zh_CN -> en, and asserts the
selected period translates with the locale instead of lagging a step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@baymac
baymac merged commit 5bdb344 into main May 29, 2026
2 checks passed
@baymac
baymac deleted the baymac/close-open-issues branch May 30, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant