Skip to content

feat: add DatePicker component - #25

Open
patrickkuei wants to merge 1 commit into
masterfrom
feat/date-picker
Open

feat: add DatePicker component#25
patrickkuei wants to merge 1 commit into
masterfrom
feat/date-picker

Conversation

@patrickkuei

Copy link
Copy Markdown
Owner

Closes #13

Summary

  • Adds a DatePicker component: a cyberpunk-styled single-date picker for booking forms, filters, and scheduling UI.
  • API decisions: value/onValueChange (controlled) or defaultValue (uncontrolled) manage the selected Date; minDate/maxDate bound selectable days, rendering out-of-range cells dimmed and disabled rather than hiding them. The trigger is a read-only text <input> reusing Input's exact variant/size class logic (primary/secondary/danger/ghost) so it looks identical to other form inputs. The popover reuses DropdownMenu's click-outside/staged open-close mechanics (same timing, same transition classes) rather than reinventing overlay plumbing, and matches Modal/Select's neon-bordered surface treatment. The calendar grid follows the WAI-ARIA date-picker pattern (role="grid"/"row"/"columnheader"/"gridcell") with a roving-tabindex focused cell — arrow keys move a day/week at a time and can cross month boundaries, Home/End jump to the edges of the focused week, PageUp/PageDown step a month, and the view always renders a fixed 6-week grid so row count never jumps between months.
  • Deferred scope: per the issue's own suggestion ("single-date selection to start, range mode can be a follow-up"), this ships single-date selection only. Range (two-date) selection is a natural follow-up once this is proven out — flagging it explicitly rather than blocking this PR on it.

Changes

  • Added DatePicker (src/components/DatePicker.tsx) — value/defaultValue/onValueChange, minDate/maxDate, label, placeholder, variant, responsive size, disabled, helperText, error, formatDate, ariaLabel, className, id. No new runtime dependencies — all date math (month/week arithmetic, grid building) is hand-rolled.
  • Exported DatePicker/DatePickerProps from src/components/index.ts.
  • Stories (DatePicker.stories.tsx): Default, Primary/Secondary/Ghost variants, WithBoundedRange (14-day window demo), sizes (Small/Medium/Large), WithError, Disabled, Controlled, and an AllVariants render story (cyberpunk copy throughout, e.g. "Neural Sync Window", "Extraction Window", "Cold Storage Timestamp").
  • Tests (DatePicker.test.tsx, 19 cases): renders trigger without crashing, aria-haspopup/aria-expanded/aria-controls wiring, opens on click, formatted value display, day selection (uncontrolled + controlled, without self-managed state), minDate/maxDate disabling out-of-range days, aria-current="date" on today, ArrowDown opening the calendar, Arrow key grid navigation (day and week steps), PageUp/PageDown month stepping, Enter selection, Escape close + focus return, click-outside close, disabled blocks opening, custom formatDate, error state + aria-invalid, responsive size.
  • Wired DatePicker into scripts/generate-manifest.js (CATEGORY_MAP → Forms, alongside Select/RadioGroup; DOC_SUMMARIES) and regenerated docs via npm run docs:generate — diff reviewed, only DatePicker-related lines changed.
  • Added a CHANGELOG entry under [Unreleased].

Test plan

  • npm run lint / type-check / test -- --project=unit all pass (314 tests, including 19 new for DatePicker)
  • Storybook: Default + variants + sizes + disabled + AllVariants render correctly
  • Keyboard nav + screen reader smoke test

Generated by Claude Code

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.

New component: DatePicker

2 participants