cleanplate 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/form-controls/Stepper.d.ts +14 -3
- package/dist/components/form-controls/Stepper.d.ts.map +1 -1
- package/dist/components/form-controls/date/DatePickerFooter.d.ts.map +1 -1
- package/dist/components/form-controls/date/DatePickerPanel.d.ts.map +1 -1
- package/dist/components/form-controls/date/MonthPickerGrid.d.ts +13 -0
- package/dist/components/form-controls/date/MonthPickerGrid.d.ts.map +1 -0
- package/dist/components/form-controls/index.d.ts +1 -1
- package/dist/components/form-controls/index.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.es.css +1 -1
- package/dist/index.es.js +4 -4
- package/dist/index.js +4 -4
- package/docs/FormControls.md +2 -2
- package/docs/Stepper.md +2 -0
- package/llms.txt +7 -6
- package/package.json +2 -2
- package/docs/superpowers/plans/2026-05-10-date-picker.md +0 -955
- package/docs/superpowers/plans/2026-05-10-select-floating-ui.md +0 -122
- package/docs/superpowers/specs/2026-05-10-date-picker-design.md +0 -196
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
# Select rebuild — Frozen requirements & implementation plan
|
|
2
|
-
|
|
3
|
-
> **Frozen as of:** 2026-05-10
|
|
4
|
-
> **Process:** Implement one numbered phase per session (or PR slice); pause for Storybook review before starting the next. Work stays on the current feature branch.
|
|
5
|
-
|
|
6
|
-
**Goal:** Replace `Select` with a Floating UI–based control: desktop portalled dropdown with collision-aware positioning; mobile-first bottom sheet for the panel; parity with your written spec plus the decisions below.
|
|
7
|
-
|
|
8
|
-
**Tech stack:** React 18, existing SCSS (`FormControls.module.scss`), `@floating-ui/react` (must be a **runtime dependency** for the published package—not only devDependency).
|
|
9
|
-
|
|
10
|
-
**Architecture (short):** Reference element = composite trigger (`button`/`div` pattern matching a11y plan). Floating panel = `FloatingPortal` → `FloatingFocusManager` for desktop; overlay + sheet panel for narrow viewports sharing the same inner list/search shell where possible.
|
|
11
|
-
|
|
12
|
-
**Testing gate:** Existing Storybook form-controls **Select** story (expand with variants as phases land).
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Frozen requirements
|
|
17
|
-
|
|
18
|
-
### Locked from your original spec
|
|
19
|
-
|
|
20
|
-
- **Modes:** `mode: 'single' | 'multi'` (multi shows removable chips when selected; overflow `+N` after `triggerMaxItems`).
|
|
21
|
-
- **Data:** Static `Option[]`, or async when `options === null` and `onSearch` is provided.
|
|
22
|
-
- **Options:** Shape as in your doc (`value`, `label`, optional `group`, `icon`, `avatar`, `meta`, `disabled`).
|
|
23
|
-
- **Portal:** Dropdown content renders through a portal (`document.body` or `#root`-safe target if we add optional `portalRoot` prop—**frozen default:** `document.body`).
|
|
24
|
-
- **Clear:** Trigger shows × whenever there is a selection; clears all and closes panel.
|
|
25
|
-
- **Search:** Debounced `searchDebounce` (async); search row at top of panel (clear control on search input when non-empty)—matches your inspirations.
|
|
26
|
-
- **Panel (multi):** Select all row with checked / unchecked / **indeterminate** when some visible options are selected; respects disabled options and `maxSelect`.
|
|
27
|
-
- **`maxSelect`:** Enforced in multi only; capped state is visibly disabled before tap; single mode **no-op** (frozen).
|
|
28
|
-
- **Keyboard (desktop dropdown):** ↓ / ↑ move active option; Enter toggles selection; Escape closes and returns focus to trigger; Tab closes (match spec table).
|
|
29
|
-
- **States:** Idle, open (+ search focus behavior per phase notes), loading, error, empty, disabled.
|
|
30
|
-
- **Forms:** Preserve hidden/native submission story where applicable (`name` prop); extend as needed for multi value encoding.
|
|
31
|
-
|
|
32
|
-
### Former “open items” — now decided
|
|
33
|
-
|
|
34
|
-
| Item | Decision |
|
|
35
|
-
|------|----------|
|
|
36
|
-
| `maxSelect` in single mode | **No-op** (ignored). |
|
|
37
|
-
| Bottom sheet animation | **CSS-only:** translateY from 100% → 0, **240ms**, `cubic-bezier(0.22, 1, 0.36, 1)`; backdrop fades **160ms**. (Tunable in SCSS only.) |
|
|
38
|
-
| Flip / collision (desktop) | **Yes:** `flip` + `shift` + **`size`** so max height clamps to viewport; scroll inside list. |
|
|
39
|
-
| Error retry | **Manual “Retry”** only after failure (invokes same `onSearch` with last query). No automatic retry loops. |
|
|
40
|
-
| Uncontrolled mode | **Out of scope for v1.** API is controlled: `value` + `onChange`. (Optional `defaultValue` is a future enhancement, not part of this rebuild.) |
|
|
41
|
-
|
|
42
|
-
### Visual / UX defaults (aligned to your screenshots)
|
|
43
|
-
|
|
44
|
-
- Multi trigger: chips with per-chip dismiss; comma-joined label text is replaced by chips + overflow.
|
|
45
|
-
- Single trigger: plain text label of selected option (no chip), or placeholder.
|
|
46
|
-
- Checkbox-style row affordance for multi list; highlight row on hover/focus-visible.
|
|
47
|
-
- Icons: **`icon`** uses **Material Symbols names** like the rest of CleanPlate **`Icon`** (your doc said Tabler—**frozen correction:** match existing **`Icon`** / `cleanplate` pattern in this repo, unless you revert this in review).
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## Dependencies
|
|
52
|
-
|
|
53
|
-
- Move **`@floating-ui/react`** from `devDependencies` to **`dependencies`** in `package.json` before shipping the component (can land in Phase 1 PR).
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## Implementation phases (one todo each — review after)
|
|
58
|
-
|
|
59
|
-
### Phase 1 — Floating UI desktop shell
|
|
60
|
-
|
|
61
|
-
- Portal + `useFloating` with `flip`, `shift`, `offset`, `size`, `whileElementsMounted` / `autoUpdate`.
|
|
62
|
-
- Replace in-flow dropdown positioning; dismiss on outside press and Escape (`useDismiss`).
|
|
63
|
-
- **Scope limit:** Static options, existing simple single + multi behaviour, no chips yet beyond current approximation if multi still uses text summary briefly.
|
|
64
|
-
- Storybook: Select still runnable; note known gaps.
|
|
65
|
-
|
|
66
|
-
### Phase 2 — API & types freeze
|
|
67
|
-
|
|
68
|
-
- Export `Option` type; deprecate or alias `SelectOption` → `Option` for compat.
|
|
69
|
-
- Add `mode` prop; **`isMulti` maps to `mode`** for one release or deprecate `isMulti` with console warning once (pick minimal churn—frozen approach: **`mode` canonical**, keep `isMulti` as undocumented alias forwarding to `mode` OR remove if you prefer breaking change—default **canonical `mode`** + **`isMulti` deprecated** shim).
|
|
70
|
-
- Storybook controls updated.
|
|
71
|
-
|
|
72
|
-
### Phase 3 — Multi trigger chips + overflow
|
|
73
|
-
|
|
74
|
-
- Render first `triggerMaxItems` selections as removable chips; `+N` for remainder.
|
|
75
|
-
- Clear (×) and chevron behaviours per spec.
|
|
76
|
-
|
|
77
|
-
### Phase 4 — Search (sync + async wiring)
|
|
78
|
-
|
|
79
|
-
- Search field in panel; **sync:** filters `options` locally, no debounce on static path (debounce still applies only to `onSearch` path per your table).
|
|
80
|
-
- **Async:** when `options === null`, debounced `onSearch(q)`.
|
|
81
|
-
|
|
82
|
-
### Phase 5 — Async states
|
|
83
|
-
|
|
84
|
-
- Loading / error / empty UI in list slot; Retry on error only.
|
|
85
|
-
|
|
86
|
-
### Phase 6 — Groups + rich options
|
|
87
|
-
|
|
88
|
-
- `groups` sections; icons/avatars/meta; disabled options skipped in keyboard nav and not selectable.
|
|
89
|
-
|
|
90
|
-
### Phase 7 — Panel bulk actions & `maxSelect`
|
|
91
|
-
|
|
92
|
-
- Select all / clear all in panel (multi); indeterminate logic for “visible” selectable set respect `disabled` rows.
|
|
93
|
-
- Cap styling and blocked clicks.
|
|
94
|
-
|
|
95
|
-
### Phase 8 — Keyboard completion
|
|
96
|
-
|
|
97
|
-
- Arrow navigation through options; Enter to toggle/select; Tab/Escape per frozen table; roving tabindex or Floating UI list patterns.
|
|
98
|
-
|
|
99
|
-
### Phase 9 — Mobile bottom sheet
|
|
100
|
-
|
|
101
|
-
- Breakpoint: **`max-width: 768px`** (match common `md` boundary; overrides if you expose `breakpoints` prop later—**frozen: internal constant** first).
|
|
102
|
-
- Sheet uses focus trap compatible with Floating UI dismissal; reuse inner list markup.
|
|
103
|
-
|
|
104
|
-
### Phase 10 — A11y + Storybook showcases
|
|
105
|
-
|
|
106
|
-
- `aria-expanded`, `aria-controls`, listbox/combobox roles consistent across modes.
|
|
107
|
-
- Hidden input / form submission validated for multi.
|
|
108
|
-
- Stories: static single/multi, async, groups, chips overflow, maxSelect, error, empty, sheet (viewport toggle).
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## Review checkpoint protocol
|
|
113
|
-
|
|
114
|
-
After each phase: run **Storybook** (`npm run storybook`), exercise **Select** story (and phase-specific knobs), confirm no regressions in **All controls showcase**, then approve next phase.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Out of scope (explicit)
|
|
119
|
-
|
|
120
|
-
- List virtualization (`react-window`, etc.).
|
|
121
|
-
- Uncontrolled/defaultValue API.
|
|
122
|
-
- Auto-retry on fetch errors.
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
# Date picker (`Date` form control) — design spec
|
|
2
|
-
|
|
3
|
-
**Status:** Approved (chat sign-off 2026-05-10)
|
|
4
|
-
**Scope:** Replace existing three-`Select` `Date` field with calendar date picker per product requirements.
|
|
5
|
-
**Stack:** React 18+, `date-fns` (granular imports), `@floating-ui/react` (already in package).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Summary
|
|
10
|
-
|
|
11
|
-
The public **`Date`** export from form controls becomes a **calendar-based date picker**: read-only trigger field, floating **popover** on desktop and **bottom sheet** on mobile (same breakpoint and shell pattern as `Select.tsx`), **Cancel / OK** staged commit, full constraint props, and accessibility per the requirements document. This is a **breaking API** change from the previous day/month/year string model.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 2. Goals and non-goals
|
|
16
|
-
|
|
17
|
-
**In scope** (aligned with `datepicker-requirements.md` and wireframe):
|
|
18
|
-
|
|
19
|
-
- Single date selection; **staging** in the panel; **OK** commits (fires `onChange`, updates display); **Cancel** / Escape / outside dismiss / overlay tap **revert** to last committed value only.
|
|
20
|
-
- Month grid (7 columns, configurable `weekStartsOn`); **today** distinct from **selected**; **outside-month** cells visible, dimmed; click outside-month **navigates** to that month and **stages** that date.
|
|
21
|
-
- Month and year **chevrons** plus **dropdown** subviews; navigation **clamped** by `minDate` / `maxDate`.
|
|
22
|
-
- Constraints: `minDate`, `maxDate`, `disabledDates`, `disabledDaysOfWeek`.
|
|
23
|
-
- Locale via `date-fns` `Locale` and `weekStartsOn`.
|
|
24
|
-
- Keyboard and ARIA (`grid` / `gridcell`, `aria-selected`, `aria-disabled`, trigger `aria-expanded` / `aria-controls`, focus restoration on close).
|
|
25
|
-
- Responsive: desktop popover (**280px** width, flip/shift, 8px viewport padding); mobile sheet (rounded top corners, dim overlay, no swipe-dismiss), body scroll lock while open.
|
|
26
|
-
|
|
27
|
-
**Explicitly out of scope** for this component (unchanged from requirements):
|
|
28
|
-
|
|
29
|
-
- Range / dual calendar, time picker, typed free-form parsing, timezone UI, gestures, inline-only calendar embedding.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## 3. Breaking changes
|
|
34
|
-
|
|
35
|
-
Previous behaviour:
|
|
36
|
-
|
|
37
|
-
- Three `Select` fields; `onChange` emitted **`dd-mm-yyyy`** string; `defaultValue` used `"--"` split pattern.
|
|
38
|
-
|
|
39
|
-
New behaviour:
|
|
40
|
-
|
|
41
|
-
- `value` / `defaultValue`: **`Date | null`** (calendar date in **local** timezone; comparisons use **local calendar midnight** consistently — document in implementation and JSDoc).
|
|
42
|
-
- `onChange`: **`(date: Date | null) => void`**.
|
|
43
|
-
- Clearing via trigger **commits `null` immediately** (closes panel if open).
|
|
44
|
-
|
|
45
|
-
**Migration:** Consumers must replace string state with `Date` (or derive strings at form submit). Optionally use hidden `name` field (see below) for native forms.
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 4. Architecture (Approach 2)
|
|
50
|
-
|
|
51
|
-
**Facade + module folder** — keeps a single public **`Date`** / **`DateProps`** while splitting implementation for testability and maintainability.
|
|
52
|
-
|
|
53
|
-
| Area | Responsibility |
|
|
54
|
-
|------|----------------|
|
|
55
|
-
| `form-controls/Date.tsx` | Public API, field chrome (label, error, `isFluid`, `dataTestId`, ids), wires shell + panel. |
|
|
56
|
-
| `form-controls/date/calendar-matrix.ts` | Builds visible calendar cells / weeks using `date-fns` granular imports only. |
|
|
57
|
-
| `form-controls/date/date-constraints.ts` | `isDisabled`, nav bounds, weekday / blackout rules vs `min`/`max`. |
|
|
58
|
-
| `form-controls/date/use-date-picker-state.ts` | `open`, committed vs staged value, view mode (`calendar` \| `month` \| `year`), actions. |
|
|
59
|
-
| `form-controls/date/DatePickerPanel.tsx` | Header, grid, footer **Cancel** / **OK**. |
|
|
60
|
-
| `form-controls/date/MonthDropdownView.tsx` | Scrollable month list; clamp to allowed months. |
|
|
61
|
-
| `form-controls/date/YearDropdownView.tsx` | Scrollable year list; clamp to allowed years. |
|
|
62
|
-
|
|
63
|
-
Optional later extractions if files grow: shared **`ScrollPickerList`**; shared **responsive shell** hook extracted from duplication with `Select` (only if duplication hurts — not required in first PR).
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## 5. Tree-shaking notes
|
|
68
|
-
|
|
69
|
-
- **Runtime:** Always import **`date-fns`** as **named submodule imports** (e.g. `import { format } from 'date-fns/format'` or equivalent tree-shake-friendly entry — follow project ESLint / bundler conventions).
|
|
70
|
-
- **Package layout:** The published library currently uses a **single Rollup entry** (`src/index.js`) that imports `FormControls`; consumers importing the main entry may still include form controls as a group. **True** “only pay for Date” at app level may require a **future** `package.json` **`exports`** subpath (out of scope for this design unless explicitly added in the same effort). This spec still requires **no unnecessary eager imports** inside the date module.
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 6. Responsive shell (mirror `Select`)
|
|
75
|
-
|
|
76
|
-
- **Breakpoint:** `(max-width: 768px)` matches `Select.tsx` (`SELECT_MOBILE_SHEET_MEDIA`).
|
|
77
|
-
- **Desktop:** `FloatingPortal` + `useFloating` with `offset`, `flip`, `shift`; popover **width 280px** (not input width); `placement` overridable via prop (default `bottom-start`); `autoUpdate` while open.
|
|
78
|
-
- **Mobile:** Fixed bottom sheet, dim backdrop, `translateY` enter/exit, `transitionend` + timeout fallback, **`document.body.style.overflow = 'hidden'`** while open (same rationale as Select).
|
|
79
|
-
- **Dismiss:** Outside press / overlay tap behave as **Cancel** (discard staged).
|
|
80
|
-
|
|
81
|
-
SCSS timings and z-index should **reuse or align with** `--cp-select-*` tokens / class patterns in `FormControls.module.scss` to avoid visual regressions and drift.
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## 7. Behaviour details
|
|
86
|
-
|
|
87
|
-
- **Opening:** Copies **committed → staged**. If no committed value, stage **today** clamped into allowed range, or first allowed day of **`displayedMonth`** if today invalid (implementation picks one deterministic rule — prefer **clamp today**, else **first enabled day of month** visible).
|
|
88
|
-
- **OK:** Applies staged → committed; `onChange(committed)`; close; restore focus per a11y section.
|
|
89
|
-
- **Cancel / Escape / outside dismiss:** Discard staged; close; no `onChange` unless clearing was instantaneous (clear is separate).
|
|
90
|
-
- **Clear:** **Instant** commit `null`, `onChange(null)`, close if open.
|
|
91
|
-
- **Disabled:** No open when `isDisabled`; nav controls and dropdown options respect disabled state for out-of-range or invalid targets.
|
|
92
|
-
- **Hidden input:** If `name` is provided, render `<input type="hidden" name={name}>` with value **`YYYY-MM-DD`** when committed value exists, otherwise empty string.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## 8. Accessibility
|
|
97
|
-
|
|
98
|
-
- Trigger: **`aria-expanded`**, **`aria-controls`** referencing panel id; labelled via `label` + `id` pattern consistent with form controls.
|
|
99
|
-
- Mobile sheet container: **`role="dialog"`**, **`aria-modal="true"`**; desktop popover remains focusable-managed surface (exact `role` may be `dialog` or documented pattern consistent with axe rules — implementation verifies with axe in Storybook or CI when available).
|
|
100
|
-
- Grid: **`role="grid"`**, cells **`role="gridcell"`**; **`aria-selected`** on selection; **`tabIndex={-1}`** vs roving tabindex strategy as implemented; **disabled** cells **`aria-disabled="true"`** and not in tab order.
|
|
101
|
-
- **Today** vs **selected**: not color-only (e.g. subtle ring or textual “today” available to SR via `aria-label` on cell).
|
|
102
|
-
- **Focus:** On open, move focus into panel; on close return focus to trigger. **Tab** cycles within the picker while open (**lightweight in-house Tab loop** first; introduce `focus-trap-react` only if manual handling fails review or tests).
|
|
103
|
-
|
|
104
|
-
Keyboard behaviour matches requirements doc:
|
|
105
|
-
|
|
106
|
-
| Key | Behaviour |
|
|
107
|
-
|-----|-----------|
|
|
108
|
-
| Tab / Shift+Tab | Cycle within open picker (within surface only while open). |
|
|
109
|
-
| Enter / Space on trigger | Open picker. |
|
|
110
|
-
| Arrows | Move between enabled days / within grid semantics. |
|
|
111
|
-
| Enter / Space on day | Stage date. |
|
|
112
|
-
| Page Up / Down | Previous / next month. |
|
|
113
|
-
| Home / End | First / last day of **currently displayed** month (enabled handling if some days disabled). |
|
|
114
|
-
| Escape | Cancel and close |
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## 9. Props (public API)
|
|
119
|
-
|
|
120
|
-
Compatible with refined requirements (`datepicker-requirements.md`) with naming aligned to existing form controls where useful:
|
|
121
|
-
|
|
122
|
-
| Prop | Type | Notes |
|
|
123
|
-
|------|------|--------|
|
|
124
|
-
| `value` | `Date \| null` | Controlled. |
|
|
125
|
-
| `defaultValue` | `Date \| null` | Uncontrolled initial. |
|
|
126
|
-
| `onChange` | `(date: Date \| null) => void` | Fires on **OK** or **instant clear**. |
|
|
127
|
-
| `placeholder` | `string` | Default `Select date`. |
|
|
128
|
-
| `dateFormat` | `string` | Default `MMM dd, yyyy`; use `date-fns` `format`. |
|
|
129
|
-
| `id`, `name` | `string` | `name` enables hidden ISO field. |
|
|
130
|
-
| `minDate`, `maxDate` | `Date` | Inclusive bounds on calendar dates. |
|
|
131
|
-
| `disabledDates` | `Date[]` | Normalized to date-only compare. |
|
|
132
|
-
| `disabledDaysOfWeek` | `number[]` | `0` Sun … `6` Sat. |
|
|
133
|
-
| `locale` | `Locale` | `date-fns` locale. |
|
|
134
|
-
| `weekStartsOn` | `0 \| … \| 6` | Default `0`. |
|
|
135
|
-
| `clearable` | `boolean` | Default `true`. |
|
|
136
|
-
| `disabled` | `boolean` | Align prop name with other controls (`isDisabled` deprecated alias optional — **YAGNI**: use `disabled` only if we standardize; else keep `isDisabled` for consistency with `Select` — **decision: keep `isDisabled`** to match existing `Date.tsx` and `Select.tsx` until a global rename). |
|
|
137
|
-
| `readOnly` | `boolean` | Per requirements. |
|
|
138
|
-
| `label`, `error` | `string` | Match existing field patterns. |
|
|
139
|
-
| `isFluid`, `dataTestId`, `isRequired` | | Preserve from current `Date`. |
|
|
140
|
-
| `popoverPlacement` | `Placement` | `@floating-ui/react`. |
|
|
141
|
-
| `onOpen`, `onClose` | `() => void` | |
|
|
142
|
-
|
|
143
|
-
**Note:** Requirements used `disabled`; existing components use **`isDisabled`**. This spec **standardizes on `isDisabled`** for continuity with `Select` / prior `Date` until a library-wide rename.
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## 10. Testing (TDD)
|
|
148
|
-
|
|
149
|
-
**Tooling:** Add **Vitest**, **@testing-library/react**, **jsdom** (+ any minimal config for TSX path aliases).
|
|
150
|
-
|
|
151
|
-
**Order:**
|
|
152
|
-
|
|
153
|
-
1. **Unit:** `calendar-matrix.ts`, `date-constraints.ts` — edge cases (leap years, DST boundaries treated as local date-only, boundary min/max months, weekdays).
|
|
154
|
-
2. **Hook / state:** `use-date-picker-state` — open/close, commit/cancel, month/year navigation clamps, staged vs committed.
|
|
155
|
-
3. **RTL:** OK vs Cancel vs outside-click vs Escape; disabled day interaction; clear button commits `null`; optional snapshot of header rendering.
|
|
156
|
-
|
|
157
|
-
Stories (Storybook): desktop vs mobile layouts, constrained DOB scenario, weekday blackout — scheduled after core tests pass.
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
## 11. Dependencies
|
|
162
|
-
|
|
163
|
-
| Package | Role |
|
|
164
|
-
|---------|------|
|
|
165
|
-
| `date-fns` | **New** dependency — formatting, arithmetic, locale. |
|
|
166
|
-
| `@floating-ui/react` | Existing — positioning. |
|
|
167
|
-
|
|
168
|
-
No headless UI library.
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## 12. Files to touch (implementation preview)
|
|
173
|
-
|
|
174
|
-
- Replace `src/components/form-controls/Date.tsx`.
|
|
175
|
-
- Add `src/components/form-controls/date/*` as above.
|
|
176
|
-
- Extend `FormControls.module.scss` for date picker (tokens aligned with select sheet/dropdown).
|
|
177
|
-
- `package.json` — add `date-fns`, test deps, `test` script.
|
|
178
|
-
- Storybook stories under existing form-controls stories pattern.
|
|
179
|
-
- **Changelog / migration note** for breaking `Date` API (where the project publishes changes).
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
## 13. Self-review checklist (pre-implementation)
|
|
184
|
-
|
|
185
|
-
- [x] No unresolved “TBD” in behaviour: focus strategy starts in-house Tab loop; clamp rule for empty-open documented in §7.
|
|
186
|
-
- [x] `isDisabled` vs `disabled` contradiction resolved (**use `isDisabled`**).
|
|
187
|
-
- [x] Timezone stance: **local calendar date only** for v1.
|
|
188
|
-
- [x] Scope fits one implementation plan with optional follow-up for `package.json` exports.
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## 14. References
|
|
193
|
-
|
|
194
|
-
- Product requirements (authoritative checklist): companion doc `datepicker-requirements.md` (user-provided).
|
|
195
|
-
- Visual: wireframe `assets/Screenshot_2026-05-10_at_3.54.33_PM-29e15a23-18c8-40cb-a7e3-2941980c2d4a.png`.
|
|
196
|
-
- Behavioural reference: `src/components/form-controls/Select.tsx` (responsive shell).
|