Skip to content

feat(date-picker): DateRangePicker + RangeCalendar + DataTable range filter (pp-1373)#388

Open
interacsean wants to merge 3 commits into
mainfrom
claude/date-picker-range-pp-1373
Open

feat(date-picker): DateRangePicker + RangeCalendar + DataTable range filter (pp-1373)#388
interacsean wants to merge 3 commits into
mainfrom
claude/date-picker-range-pp-1373

Conversation

@interacsean

Copy link
Copy Markdown
Contributor

Summary

Adds the two deferred-but-designed-for range components from the DatePicker proposal — DateRangePicker and RangeCalendar — with a { start, end } range value (exported as DateRange), and wires the range picker into the DataTable date between filter.

Planning issue: https://github.com/tailor-inc/platform-planning/issues/1373 (fast-follow of #1093; builds on the keyboard-shortcuts work merged in #364).

What's included

  • RangeCalendar (standalone) + DateRangePicker (segmented start/end inputs sharing one popover calendar), mirroring the Adobe React ARIA interaction spec:
    • first pick anchors the range and keeps the popover open; the highlight live-extends to the hovered/arrow-focused day; the second pick completes it and closes the popover
    • picking a day before the anchor swaps the endpoints (a calendar range is always ordered); a range typed in reverse is flagged invalid with a localized message rather than silently swapped
    • keyboard: Enter/Space anchors then commits (focus auto-advances one day after anchoring so plain arrows extend); Escape cancels an in-progress selection; contiguous-range clamping around unavailable dates
  • Engine refactor: the selection-agnostic grid/focus/keyboard core is extracted into useCalendarGridState, with a selection adapter so the single-value useCalendarState and the new useRangeCalendarState share everything but their selection model. The single-date Calendar/DatePicker behavior is unchanged.
  • DataTable date between filter now renders one DateRangePicker (a single shared range calendar) instead of two stacked date pickers. The committed { min, max } value shape is unchanged, so GraphQL/URL serialization and chip formatting are untouched. A reversed range can no longer be applied; switching a date filter to between seeds both bounds; a malformed URL date no longer crashes the editor.
  • Docs (docs/components/date-picker.md) and a changeset (minor).
  • Example: the vite-app Orders page is rebuilt as a DataTable over an async mock (useOrdersQuery, ~800 ms latency) so loading/pagination/filter behavior — including the new range filter on the Placed on date column — is demoable end to end.

Testing

  • pnpm type-check — green across all 9 packages
  • pnpm --filter @tailor-platform/app-shell test — 1383 passing (new suites: range-calendar.test.tsx, date-range-picker.test.tsx, plus expanded toolbar/calendar/date-field coverage)
  • pnpm lint — clean
  • Verified live in the vite example (two-click selection, hover preview, loading skeletons, and the between → range-calendar filter)

Notes for reviewers

  • Behavior change worth a look: emptying the range in a chip editor now disables Apply (removal is the chip's ✕) rather than silently removing the filter — this closes a bug where clearing one field could delete the filter while a date was still shown.
  • Scoped to a single-month calendar; a two-month pane would need Tab-containment/focus rework and isn't required by the issue.
  • Findings from an adversarial self-review (focus/hover desync, reversed-range handling, unavailable-date bounds, malformed-value crash) were fixed with regression tests before this PR.

🤖 Generated with Claude Code

interacsean and others added 2 commits July 16, 2026 17:26
…on in DataTable between filters

Adds the two deferred-but-designed-for range components from the DatePicker
proposal (pp#1373), mirroring react-aria's DateRangePicker interaction spec:

- useCalendarGridState: selection-agnostic grid/focus/keyboard engine extracted
  from useCalendarState (behaviour preserved), with a selection adapter so the
  single and range hooks share everything but their selection model.
- useRangeCalendarState + RangeCalendar: two-click anchor→commit selection,
  live hover/keyboard preview (focus follows the highlight), backwards-pick
  endpoint swap, Escape cancel, contiguous-range clamping around unavailable
  dates, and the pre-wired data-selection-start/end cell styling plus a new
  data-in-range band painted on the gridcell.
- DateRangePicker: start/end segmented inputs (cross-field arrow navigation,
  per-field blur normalization, QuickBooks shortcuts) in one labelled group
  with a single trigger and shared popover that closes only when the second
  pick completes the range; a range typed in reverse flags invalid with a
  localized message instead of silently swapping.
- DataTable date filters: the between operator now renders one DateRangePicker
  instead of two stacked DatePickers. The committed {min,max} value shape is
  unchanged (GraphQL/URL serialization untouched); reversed ranges can no
  longer be applied; switching a date filter to between seeds both bounds;
  malformed URL date values no longer crash the editor; an emptied range
  disables Apply (remove stays on the chip's ✕).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the static 3-row Table on the vite example Orders page with a
DataTable over ~22 mock orders, mirroring the Products demo:

- mock-orders.ts: an async useOrdersQuery hook (~800ms latency, loading
  window) that filters/sorts/paginates in memory, so the DataTable's
  loading state (skeleton rows) is exercised on every fetch.
- Filter/sort/pagination synced to the URL via useURLCollectionVariables.
- A `date`-typed "Placed on" column, so its between filter renders the new
  DateRangePicker — exercising this branch's feature end to end.
- Row click / "View details" action navigate to the order detail page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@interacsean
interacsean force-pushed the claude/date-picker-range-pp-1373 branch from ce883c5 to a20f5c0 Compare July 16, 2026 07:27
@interacsean
interacsean marked this pull request as ready for review July 16, 2026 07:39
@interacsean
interacsean requested a review from a team as a code owner July 16, 2026 07:39
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