Skip to content

fix(date-field): flag typed/shortcut input invalid when outside min/max or unavailable#390

Merged
IzumiSy merged 3 commits into
mainfrom
claude/date-field-invalid-out-of-range
Jul 16, 2026
Merged

fix(date-field): flag typed/shortcut input invalid when outside min/max or unavailable#390
IzumiSy merged 3 commits into
mainfrom
claude/date-field-invalid-out-of-range

Conversation

@interacsean

Copy link
Copy Markdown
Contributor

Summary

Fixes a pre-existing bug (spotted in the vite example's "Future dates only" DatePicker): a date entered by typing — or landed via a keyboard shortcut — outside the allowed range was silently accepted. minValue/maxValue were only consulted by the shortcut clamp, and isDateUnavailable only gated calendar selection; the typed value had no validation at all.

The field now flags such a value invalid (aria-invalid on the segments + a built-in message) while still emitting it through onChange — matching how the calendar already behaves and Adobe React ARIA's validation contract (which flags invalid rather than clamping text entry).

This is independent of the DateRangePicker work in #388 — it lives in the shared field engine (use-date-field-state.ts) and back-fixes DateField and DatePicker. The DateRangePicker inherits the same hook and will get a small wiring commit on #388.

Changes

  • applyShortcut no longer clamps. A shortcut target (e.g. y → 1 Jan) outside [minValue, maxValue] is now surfaced as invalid instead of being coerced to the boundary — consistent with typing. The open-popover (calendar) path still clamps roving focus, since you can't type there.
  • isDateUnavailable drives field validation (both DateField and DatePicker), not just calendar selection — so weekends or specific blackout dates typed into the field are flagged too.
  • useDateFieldState returns { isInvalid, invalidReason } (range vs unavailable). date-field.tsx ORs it into the derived invalid state and shows a localized default message (en/ja) that a consumer errorMessage overrides.
  • The value still flows through onChange — we flag, we don't block or clamp — so forms can decide what to do with it.

Testing

  • pnpm --filter @tailor-platform/app-shell test — 1354 passing. New/updated coverage in date-field.test.tsx:
    • typed date before minValue → invalid + still emits (with the built-in message)
    • invalid flag clears once the typed date returns to range
    • typed unavailable date (isDateUnavailable) → invalid
    • applyShortcut out of [min,max] → invalid, not clamped (DateField + DatePicker); the open-popover shortcut still clamps roving focus (unchanged)
  • pnpm type-check (9/9) and pnpm lint clean.

Note: keyboard-driven manual repro isn't feasible through the preview harness (the iframe won't hold focus for synthetic key events), so the behavior is verified by the RTL suite above, which drives the real key handlers via userEvent.keyboard.

Follow-up

Once this merges, #388 rebases onto it and gains a two-line wiring (pass isDateUnavailable to each end's field state + OR isInvalid into the range picker's derived invalid state) so the range picker's typed ends validate the same way.

🤖 Generated with Claude Code

interacsean and others added 2 commits July 16, 2026 15:19
…teUnavailable

Typing a date (or landing one via a keyboard shortcut) outside the allowed
range was silently accepted — minValue/maxValue were only consulted by the
shortcut clamp, and isDateUnavailable only gated calendar selection. The field
now flags such a value invalid (aria-invalid + a built-in message) while still
emitting it through onChange, matching the calendar's behavior and react-aria's
validation contract.

- Remove the applyShortcut clamp: a shortcut target outside [min,max] is
  surfaced as invalid rather than coerced to the boundary, consistent with
  typing. The open-popover (calendar) path still clamps roving focus.
- Honour isDateUnavailable in field validation (DateField + DatePicker), not
  just calendar selection.
- useDateFieldState now returns { isInvalid, invalidReason }; date-field.tsx
  ORs it into the derived invalid state and shows a localized default message
  (en/ja) that a consumer errorMessage overrides.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@interacsean
interacsean marked this pull request as ready for review July 16, 2026 06:01
@interacsean
interacsean requested a review from a team as a code owner July 16, 2026 06:01
@IzumiSy

IzumiSy commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

/review

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

API Design Review completed successfully!

API design review complete for PR #390 — no issues found. Approved. Changes fix date validation for typed/shortcut input, correctly wire isDateUnavailable to field state, and keep new types (DateFieldInvalidReason) internal. No breaking public API surface changes.

@IzumiSy
IzumiSy merged commit 6e61cc2 into main Jul 16, 2026
4 checks passed
@IzumiSy
IzumiSy deleted the claude/date-field-invalid-out-of-range branch July 16, 2026 07:18
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.

2 participants