Skip to content

fix(DateInput, DateTimeInput): the sheet's calendar pages like UIDatePicker - #8200

Draft
vjeux wants to merge 23 commits into
meta:mainfrom
vjeux:uikit/c22-month-pager
Draft

vjeux wants to merge 23 commits into
meta:mainfrom
vjeux:uikit/c22-month-pager

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Batch: B4 · DateInput calendar month pager

Stack: #8200 → #8206

User need

Under a finger, the sheets' calendar scrolls natively, so a 300 pt flick can cross two months. A mouse keeps the scroller.

Problem and solution fit

Under a finger, MonthPager pages the months on the shared pager (usePager) with UIDatePicker's measured values:

  • Release. One month by UIKit's paging rule (300 pt/s, or past half); the ends rubber-band.
  • Title. It changes at half a month, during the drag and the settle.
  • Arrows. One month per tap on the shared pager's animated scroll; a second tap retargets.
  • Sheet split. At 10 px of travel either way, the drag is the grid's if it moved 10 px sideways, else the sheet's (UIKit's measured rule, useOwnScrollGesture's 'pager' mode).
  • Trackpad. A two-finger swipe pans and pages at the release.
  • Reduce Motion. Steps and settles cross-fade (200 ms).
  • Keys. PageUp/PageDown move a month of the active calendar, Shift a year, on every pointer; only the shown month is tabbable.
  • Shared code. The pager renders MonthScroller.tsx's month pane and grid focus.

Current specification

spec:AST-094 FR2, FR17 and FR36 (settled Oct 25). component:DateInput PR2 and AR8 already state the pager and the keys.

For the owner: main's component:DateInput still says neither is built: AR8's "Implementation gap: PageUp and PageDown do not page by a month…" and PR2's "Implementation gap: under a finger the touch calendar moves through the native month scroller…", with their Current-state bullets. This removes those gap lines, cites the new tests in the two verification rows, and adds MonthPager's markup to the Compatibility/readiness row and Compatibility class. It also adds the Current-state bullet "Arrow step motion": an arrow's step plays the pager's 300 ms sinusoid, not the motion spec:AST-094 FR45 asks for, the pager's own gap under FR4. Its Ownership boundary names the month grid parts that MonthScroller.tsx exports. It needs /approve-spec at this head.

Public delta and caller burden

  • No public API change (internal: see the API delta section).
  • Batch size: 2,380 added lines, 1,491 of them in test files. The pager, its sheet split and their proofs are one intent.

Writing rubric results

Over the removed gap lines, the two verification rows, the Compatibility/readiness row, the Compatibility class and the Ownership boundary line: R1 narration, R2 research, R4 rejected alternatives, R5 change references, R6 open questions: none. R3 evidence: test names only.

Scope

  • One capability is added.
  • Unrelated fixes, cleanup, and policy changes were removed or split.
  • Consumer docs, focused tests, and migration evidence ship with the feature.

Testing

This head, f34d87f, merges main 4245f38 into 75d06f9, which merges main d9d7b10 into f26df1b. Git merged every file with no conflict. Main changed one file this pull request edits, motion/MEASUREMENTS.md, and both sides' lines stay. Main also changed the gesture code under the pager and the sheet: a tap that wobbles at a scroll edge (#10829) and the menu press (#8078). This pull request's own diff against main keeps its size, +2,380/−477.

  • At f34d87f: pnpm typecheck 0, after pnpm build as CI runs it; pnpm lint:strict, check:repo included, 0 errors, and no warning in this pull request's files; Changeset coverage passes; pnpm review:pre 0 findings; the whole vitest ui project, 992 files, 24,446 tests passed. Every DateInput and Calendar interaction spec, against Storybook built at this head, at one worker: Chromium 48 passed, 4 skipped. WebKit through webkit-container.mjs with CI's four CPUs: 45 passed, 7 skipped. The DateInput and Calendar accessibility contracts: 111 passed.

Below both merges, f26df1b is docs only: DateInput's Ownership boundary names the month grid parts that MonthScroller.tsx exports to MonthPager. Below that, a675cf8 rewrites six comments in this pull request's files, with no code change. One named two pull requests, and five ran past the INV29 sentence limit.

1e91935 merges main f27380a into e0fc1d6. Git merged every file with no conflict. Main's looping minute wheel (#10953) also edits motion/uikit/datePicker.ts, and both its lines and this pull request's stay.

  • At f26df1b: pnpm check:repo 0, knowledge and changesets included; Changeset coverage passes; pnpm review:pre 0 findings; the whole vitest ui project, 991 files, 24,387 tests passed.
  • At a675cf8, the same code before the record line: pnpm typecheck 0; pnpm lint:strict 0 errors, and no warning in this pull request's files.
  • Every DateInput and Calendar interaction spec, against Storybook built from a675cf8's tree, at one worker: Chromium 48 passed, 4 skipped. WebKit through webkit-container.mjs with CI's four CPUs: 45 passed, 7 skipped.
  • The DateInput and Calendar accessibility contracts: 111 passed.
  • At 1e91935: the whole ui project, 24,387 passed; Chromium 48 passed, 4 skipped; accessibility 111 passed.

e0fc1d6 applied the review of new internal modules. MonthPane, monthGridKeyTarget and useMonthGridFocus move back into MonthScroller.tsx, and MonthPager imports them from there. No behavior changes: the tests that read MonthPane.tsx read MonthScroller.tsx again, as on main. The paragraphs below name the files as they were at those heads. At e0fc1d6, before the merge: the whole vitest ui project, 24,103 passed; Chromium 48 passed, 4 skipped; WebKit 45 passed, 7 skipped; accessibility 111 passed.

The head before, b5a487a, merges main 18af3dc into 90051f2, then drops the month track's data attribute. Git merged every file with no conflict. One case needed a hand edit in the merge. Main's #10924 adds a Tab case to MonthScroller.interaction.browser.spec.ts that opens the calendar through its mouse helper. This pull request narrowed that helper to one argument. So the merge passes the Reduced Motion setting alone and keeps the Locator type the case's rest helper needs.

data-month-track was a new public data attribute, and only the browser proofs read it. The pager already holds its track through trackRef, so the attribute goes. The proofs reach the track as the months viewport's only child. The mouse case tells the snap scroller from the pager by its overflow. Every other data attribute this pull request writes is one main already ships on the month scroller: data-scroller, data-month, data-date and data-outside.

  • At b5a487a: check:knowledge 0; check:changesets 0; pnpm -F @astryxdesign/core typecheck 0; pnpm lint:strict 0 errors.
  • The DateInput, DateTimeInput and Calendar unit tests: 1,327 passed.
  • Every DateInput, Calendar and motion interaction spec, against Storybook built at b5a487a, at one worker: Chromium 202 passed, 4 skipped. WebKit through webkit-container.mjs with CI's four CPUs: 189 passed, 17 skipped.
  • The DateInput and Calendar accessibility contracts: 111 passed.

90051f2 merges main c5cc8f4 into 7ea993f. Main changed five files this pull request edits, and git merged each with no conflict: MonthScroller.tsx (#10610's shared steer), TouchDateField.tsx, TouchDateTimeField.tsx and DateInputTouch.test.tsx (the sheet's resting content and the button text size), and MEASUREMENTS.md. The merge adds no edit of its own, so this pull request's own diff against main is unchanged, line for line. Main also changed shared code under the pager and the sheet since 7ea993f: the spring retarget (#10607), motion stylesheet reads (#10728), SnapPaging's settle sampling (#10568) and the sheet's resting content (#8558).

  • At 90051f2: check:knowledge 0; check:changesets 0; pnpm -F @astryxdesign/core typecheck 0; CI=true pnpm lint 0 errors; the DateInput, DateTimeInput, TimeInput, Calendar, SnapPaging, hooks, motion and utils unit tests: 4,762 passed. MonthYearWheels, MonthPager, MonthScroller, MonthScrollerSteps, Wheel, WheelActivation and CalendarTodayHydration against Storybook built at 90051f2: Chromium 39 passed, 4 skipped; WebKit (--cpus=4, LP_NUM_THREADS=4) 36 passed, 7 skipped.
  • On Android at 90051f2: the flicks pass, normal 7/7 and 4× CPU 5/5 (Evidence).

7ea993f merges main 3918a54 into 6f62fc8. Main's #10400 makes the sheet's calendar wait for today, and edits the month pane inside MonthScroller.tsx. This pull request moved that pane into MonthPane.tsx, so those two edits land there: today is PlainDate | null while it is not known yet, and a null today marks no day. Every other file is git's own merge.

  • At 7ea993f: check:knowledge 0; check:changesets 0; pnpm -F @astryxdesign/core typecheck 0; CI=true pnpm lint 0 errors; the DateInput, DateTimeInput, TimeInput, Calendar, SnapPaging and hooks unit tests: 2,552 passed. MonthYearWheels, MonthPager, MonthScroller, MonthScrollerSteps and fix(DateInput, DateTimeInput): the sheet's calendar waits for today, so hydration matches #10400's CalendarTodayHydration against Storybook built at that head: Chromium 27 passed, 2 skipped; WebKit (--cpus=4, LP_NUM_THREADS=4) 28 passed, 1 skipped.
  • On Android at 7ea993f: the emulator set DEC-27 asks for passes (Evidence).

6f62fc8 restacks the earlier head feca481's 13 commits onto main ed2fd36, after #8209's squash 9d55076. It drops the three merges (cb69fb2, c6b74ea, 1baf567), which carried only main and #8209. Its diff against main equals the earlier head's own diff, line for line.

  • At 6f62fc8: check:knowledge 0; check:changesets 0; pnpm -F @astryxdesign/core typecheck 0; CI=true pnpm lint 0 errors; Prettier clean over this pull request's files; the DateInput, DateTimeInput, Calendar, motion, TimeInput, utils and hooks unit tests: 4,617 passed. MonthYearWheels, MonthPager, MonthScroller and MonthScrollerSteps against Storybook built at that head: Chromium 23 passed, 2 skipped; WebKit (--cpus=4, LP_NUM_THREADS=4) 24 passed, 1 skipped.

The earlier head feca481 merged #8209's merge with main 24d845b into the head before it, c6b74ea, with no hand edit, then added one test commit for main's changes:

The pre-merge head 9797905 has twelve commits on #8209's head a98a089: the earlier head ea7ceae's nine, rebased past #9643/#9842 (see "Changed by the rebase onto #9643/#9842" below), then three test commits: the Hebrew paging case, the trackpad cases on fake timers, and the toggle proof's spy.

  • The record bullet "Arrow step motion" now reads "not the motion spec:AST-094 FR45 asks for", which holds under main's FR45 and under the rewrite docs(AST-094, AST-057): motion guidance; gesture springs; swipe-row returns #9915 proposes.
  • "The month/year toggle leaves the calendar's days alone" spied on useCalendarDays through ../Calendar, but MonthPane imports it from ../Calendar/hooks/useCalendarDays, so the case could not fail. It now spies on that module, and first checks that opening the sheet records calls. With MonthPane unmemoized, the old case passes and the new one fails with 3 calls (commits 82842b2 and e9c1933, not part of the stack).
  • At 9797905: check:knowledge 0; check:changesets 0; check:motion-roles 0; pnpm -F @astryxdesign/core typecheck 0; CI=true pnpm lint 0 errors; the DateInput, TimeInput, DateTimeInput, Calendar, utils and hooks unit tests: 3,500 passed; pnpm review:pre 0.
  • At abedba6, this code before the record's wording and the toggle proof's spy: pnpm install --frozen-lockfile 0; MonthPager, MonthScroller, MonthScrollerSteps and MonthYearWheels against Storybook built there: Chromium 23 passed, 2 skipped; WebKit (--cpus=4, LP_NUM_THREADS=4), twice: 48 passed, 2 skipped.
  • The earlier proofs' positive controls (d23979b, 7ba2833 and 7c7d33a, commits not part of the stack) are unchanged by the rebase.

Stacked above, #8206 defers the month/year toggle's inert swap. In WebKit at 6404e3d, the first frame after a title tap comes 3–6× sooner. It takes 34 ms (DateInput) and 21 ms (DateTimeInput), against 97 ms and 131 ms. The longest frame gap in the fade's first 300 ms grows: DateInput 93/111 against 75/51 ms (open/close), DateTimeInput 155 against 123 ms (open). DateTimeInput's figures predate its opaque plate.

Public and internal API delta

Public: none. No package export, public prop, type or default changes, and no new data-* attribute.

Internal: one new module in packages/core/src/DateInput/, and three changes. The touch sheets render the new pager, and the mouse calendar keeps MonthScroller.

  • MonthPager, a new component re-exported from datePickerTouch.ts. It takes MonthScrollerProps: <MonthPager {...monthScrollerProps} />. Why: under a finger, both sheets page the calendar like UIDatePicker, one month per flick. Existing modules considered, and why they don't fit: MonthScroller and Carousel scroll natively with scroll-snap, so a flick can cross two pages. MonthPager builds on the shared usePager instead.
  • MonthScroller.tsx exports MonthPane, MonthPaneProps, monthGridKeyTarget, useMonthGridFocus, MonthGridFocusOptions and MonthGridFocus: import {MonthPane, useMonthGridFocus} from './MonthScroller';. On main the pane and its key handling are private to it. Why: the pager renders the same month grid, with the same focus and keys.
  • useOwnScrollGesture's OwnedAxis gains 'pager': useOwnScrollGesture(viewportRef, 'pager');. Why: a drag on the pager splits with the sheet as UIKit's does, at 10 pt of travel.
  • The datePicker table (motion/uikit/datePicker.ts) gains calendar.pager and calendar.chevron: datePicker.calendar.pager.release // 'paging'. Why: it records UIKit's measured paging, and the pager and its tests follow it.
  • The calendar under a finger keeps the existing data-scroller="months", data-month, data-date and data-outside, now set by MonthPager and MonthPane.
  • Recorded: component:DateInput names MonthPager in PR2 and the Compatibility/readiness row. Its Ownership boundary names MonthPane, monthGridKeyTarget and useMonthGridFocus, the month grid parts MonthScroller.tsx exports.

Size: +2,380/−477 lines (4245f38..f34d87f); product code (packages/core/src, no tests) +753/−204. At app level (gzip, a production app with DateInput and DateTimeInput): JS at start +232 B, all JS +6,518 B, CSS unchanged.

Evidence

real-device iOS: the run on an iPhone passed at 5025fd7. This head merges main's change to the gesture code under the pager, so the run on an iPhone is taken again at this head, with #8206's: pending. Android: the Android emulator set DEC-27 asks for (normal, Remove animations, 4× CPU) passed at 7ea993f. Its flicks passed again at 90051f2 (below). A second Android emulator set passes all six claims at b5a487a (normal, 4× CPU, animations removed). The set at this head is pending.

Android, at 90051f2 (the flicks). Chrome 133 with Gboard on the Android 16 emulator, against that head's own deployment (its version.json checked), on the core-dateinput--bottom-sheet-calendar story, at normal speed and at 4× CPU, with each take begun on a quiet host. A fast flick, released at 0.8 px/ms or more, pages exactly one month back: normal 7/7, 4× CPU 5/5. The validity rules are the ones below. One 4× CPU take rested 61 ms before its lift (180 ms from down to lift), so it is a resting finger, not a flick.

Android, at 7ea993f (the Android emulator set DEC-27 asks for). Chrome 133 with Gboard on the Android 16 emulator, against that head's own deployment (its version.json checked), on the core-dateinput--bottom-sheet-calendar story; the Japanese case is on calendar-system-presentations. Each claim ran at normal speed, at 4× CPU and with the system's Remove animations on (checked in the page). The sheet opens from the calendar button, and the field never takes focus, so the on-screen keyboard stays hidden in every take.

Claim normal 4× CPU Remove animations
A slow drag of 1/3 of the width springs back, with no page pass 2/2 pass 2/2 pass 2/2
› pages one month per tap pass 3/3 taps pass 2/2 pass 2/2
A slow drag past half pages one month back pass 2/2 pass 2/2 pass 2/2
A fast flick (released at 0.8 px/ms or more) pages one month back pass 7/7 pass 5/5 —
A mostly-down drag moves only the sheet: the month stays, the sheet stays open pass 2/2 pass 2/2 pass 2/2
Japanese calendar: each swipe pages one month pass 2/2 — —

A take counts only when its gesture is valid. The 1/3 drag takes 400 ms or more (a faster one pages on its release velocity, as UIDatePicker does). A flick lasts 150 ms or less from down to lift, and lifts 40 ms or less after its last move: a finger still for longer before its lift releases at rest (spec:AST-100 FR2). The normal and 4× CPU flicks are from a second run on a quiet host. In the first run, one normal take at 2.11 px/ms rested 118 ms before its lift (179 ms from down to lift), so it was a resting finger, not a flick. The two valid Remove-animations flicks released under 0.8 px/ms, and both paged. One normal tap, stretched by the host's load into a 1.18 s press, is a long press, not a tap, and is not counted.

At this pull request's preview, the deploy of 3704441, which has this head's own paging code (later commits add tests and merges with main, and the merge at 7ea993f adds #10400's null-today guard to the pane): iOS 26.2 Simulator, Mobile Safari, iPhone 17 Pro. The preview carries no touch overlay, so each row names its stroke; a stroke reaches the page every 16 ms. I reviewed every frame against the paging rules above and the earlier recordings below; each row gives its largest frame gap during motion. Persian and Hebrew need Temporal, which this Safari lacks, so the calendar row uses the Japanese calendar; the Hebrew months are the unit test's.

Stroke this PR
300 pt flick in 0.1 s: one month; the title turns at half (gaps ≤ 20 ms)
30 % of a month in 1.2 s: back to October (one 32 ms gap, in the return)
70 % in 2.8 s: pages to November; the title turns at half (gaps ≤ 20 ms)
Japanese calendar, 70 % in 2.8 s: 令和8年12月 to 令和9年1月, the era year turning with the month (one 28 ms gap, at the start)
Next, twice: one month per tap; the title turns at the tap (gaps ≤ 27 ms)

Earlier, against main; the motion is unchanged since.

Path before this PR
300 pt flick
30 % (stays), then 70 % (pages)
Next, twice
35°, then 60°
Main thread blocked 600 ms
Reduce Motion

Changed by the rebase onto #9643/#9842

Main renders each month pane in the active calendar (#9643/#9842). This head keeps that in the shared pane and in month paging:

  • MonthPane takes the monthModel and reads its month through monthAt, useCalendarDays with the calendar, formatCalendarMonthLabel, isInCalendarMonth and formatCalendarDate, as main's scroller pane did.
  • monthGridKeyTarget moves PageUp and PageDown by the calendar's months, and with Shift by its years (calendarShiftMonths, calendarShiftYears): a Hebrew leap year has 13 months. useMonthGridFocus indexes months through the model.
  • DateInputTouch.calendar.test.tsx "pages the keyboard focus by calendar months, and by calendar years with Shift": PageUp from 1 Nisan 5784 lands in Adar II; Shift+PageUp from 10 Shevat 5785 lands on 10 Shevat 5784, 13 months back across Adar I. Gregorian month arithmetic fails the first case, and twelve calendar months for Shift fail the second.
  • No record of this pull request renumbers; PR2 and AR8 read as before, and the AR8 row cites the new case. The DateInput FR renumbering is fix(DateInput, DateTimeInput): under a finger the month/year wheels move the date like UIDatePicker's #8209's.

Rule-47 fix in this pull request's tests

DateInputTouch.test.tsx's two trackpad cases now run on fake timers: the gesture host ends a trackpad swipe 220 ms after its last wheel, and an 8 ms real-timer step let a runner stall end it early.

  • Evidence commits (not part of the stack): a real 300 ms stall halfway turns the old case red, 3 of 3 (March, not April); the same stall leaves the fake-timer case green.
  • Controls: 1,000 ms of fake time halfway still ends the swipe short (red), and a 205 px slow swipe still pages (red). 300 ms of fake time stays April, because the release at 220 ms still carries the swipe's velocity past UIKit's 300 pt/s.

Review notes

  • Follow-ups, outside this pull request: the doc comment withLayoutAsync left above beforeAll in DateInputTouch.test.tsx, and MonthScroller's coarse-pointer Reduced Motion dip, which can no longer run now that the pager handles touch.

@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 11, 2026 2:30pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 24, 2026
@astracat-bot

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
❓ Needs maintainer input (for maintainers only) Nov 11, 2026, 3:41 PM UTC

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current CI run concluded skipped. Current analysis is unavailable.


Generated by PR Enrichment workflow | View current CI run

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review waits on an engineering owner: one month per release with the 45° sheet split, the reworded draft PR2 in DateInput.spec.md and the new draft spec:AST-094 have no current spec authority, so an engineering owner needs to approve or reject them by commenting /approve-spec 39408932bf15c4b71cf42bb94ee91e9fe674496f on this pull request, which the author may do as an engineering owner. Whatever that decision, please also make these changes:

  • Land the predecessor pull requests, or rebase onto them, so this one carries only its own commit.
  • Keep the usePager effects at lines 681-683 and 698-702 from calling onIndexChange, and name the system each synchronizes.
  • Correct the changeset's 45° line: drags between about 26.6° and 45° move neither the sheet nor the calendar.

[Automated review]

Comment thread packages/core/src/hooks/usePager.ts Resolved

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review waits on an engineering owner: one month per release with the 45° sheet split, the reworded draft PR2 in DateInput.spec.md and the new draft spec:AST-094 have no current spec authority, so an engineering owner needs to approve or reject them by commenting /approve-spec <full head SHA> on this pull request, which the author may do as an engineering owner. Whatever that decision, these changes are still needed:

  • Land the predecessor pull requests or rebase onto them, so this one carries only its own commit.
  • Keep the usePager effects at lines 681-683 and 698-702 from calling onIndexChange, and name the system each synchronizes.
  • Stop naming the release month at the lift in MonthScroller.tsx (lines 418-421), which turns the title earlier than the measured UIKit behavior and the changeset describe.
  • Page on a horizontal trackpad or wheel gesture again (MonthScroller.tsx line 106), and correct the changeset's 45° line, the two stale increment/timeIncrement comments and the citation of a missing AST-090 in spec:AST-094.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review waits on an engineering owner: one month per release with the 45° sheet split, the reworded draft PR2 in DateInput.spec.md and the new draft spec:AST-094 have no current spec authority, so an engineering owner needs to approve or reject them by commenting /approve-spec <full head SHA> on this pull request, which the author may do as an engineering owner. Whatever that decision, these changes are still needed:

  • Land the predecessor pull requests or rebase onto them, so this one carries only its own commit.
  • Keep the usePager effects at lines 681-683 and 698-702 from calling onIndexChange, and name the system each synchronizes.
  • Stop naming the release month at the lift in MonthScroller.tsx (lines 418-421), or document it, so the title, the changeset and the measured UIKit behavior agree.
  • Page on a horizontal trackpad or wheel gesture again (MonthScroller.tsx line 106), and correct the changeset's 45° line, the two stale increment/timeIncrement comments and the citation of a missing AST-090 in spec:AST-094.

[Automated review]

@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Parts of this PR re-implement behavior that already exists, or is being specified, as a shared part of core. Converging keeps one implementation per behavior:

This is about where the behavior lives, not about the PR's intent.

@vjeux

vjeux commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Agreed: one implementation.

@vjeux
vjeux force-pushed the uikit/c22-month-pager branch from 161f142 to 68b7c0c Compare October 25, 2026 02:22
@vjeux

vjeux commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Converged in 68b7c0c: MonthPager stacks on #8146 (49564e7) and imports its usePager, so the copy is gone. Touch keys on the resolver, as in #8179.

vjeux added 3 commits November 7, 2026 14:43
The two trackpad cases stepped their wheels on real timers, 8 ms and
40 ms apart. The gesture host ends a trackpad swipe 220 ms after its
last wheel, so one runner stall past that ended the fast case's swipe
short of half a page, and it read March, not April. Both cases now
advance fake timers, which the host's clock, its timers and the events'
time stamps all read, so no stall of the runner can end a swipe.

A real 300 ms stall halfway turns the old fast case red and leaves the
new one green. 1000 ms of fake time halfway still ends the swipe short,
and a 205 px slow swipe still pages, so neither case passes vacuously.
"the month/year toggle leaves the calendar's days alone" spied on
`useCalendarDays` through `../Calendar`, but `MonthPane` imports it
from `../Calendar/hooks/useCalendarDays`, so the spy saw no pane and
the case could not fail. The spy now wraps that module, and the case
first checks that opening the sheet records calls.

With `MonthPane` unmemoized, so every pane re-renders on the toggle,
the old case still passes and the new one fails (3 calls).
Under a primary finger the calendar is now MonthPager, which a scroll
event does not move, so meta#10231's swipe hand-off test never reached the
range end. It runs as the other native-scroller tests here do: on a
touchscreen laptop with the sheet pinned. MonthPager reports its
settled month to the same callback.

meta#10386 has an armed hold name the motion it judges. The pager's four
holds judge the track's translate, which a step and a settle move.
@cixzhang

cixzhang commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 6f62fc8

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the code and the record edits are ready, but the Android evidence the touch-presentation spec requires is still not at this head.

  • Regression: spec:AST-094 DEC-27 asks for the Android emulator run (Chrome with Gboard, animations at normal scale and removed, a slow-device profile) "at the exact head under review", and the description's run is from the earlier head, whose base predates the main pager, animator and sheet changes this head now sits on (#10545, #10370, #10369, #10463), so the run needs to be redone on this head's deployment and the description's Android section should name this head.

An unchanged diff of the pull request's own files does not carry the run, because the shared pager code under it changed. Non-blocking suggestions are in the review: the size line in the description, a pager case for the range-end arrow hand-off, the Reduced Motion dip MonthScroller can no longer reach, and a few comment rewrites.

[Automated review]

@vjeux

vjeux commented Nov 8, 2026

Copy link
Copy Markdown
Contributor Author

On the Android item: the description's Evidence section now carries the emulator run at this head, 6f62fc8, on its own deployment. Chrome with Gboard on the Android 16 emulator, at normal speed, at 4× CPU and with Remove animations on: every paging claim passes, and the Japanese calendar pages one month per swipe.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The month pager follows the current DateInput spec (PR2, AR8) and the touch-presentation spec, an engineering owner approved the record edits at this head, and the description now records the Android emulator run at this head that the last review asked for.

  • The one red check, Component accessibility contracts (and the test gate that reads it), is Table's focus-ring case, which main fixed in #10605 after this branch's base; updating the branch from main picks up that fix.

Non-blocking, for this pull request or a follow-up:

  • The description's size line still reads "added here when the build lands"; CI reports about +6.4 kB gzip for DateInput and DateTimeInput, and the reason for loading it statically is only in a review thread.
  • No test drives the range-end arrow hand-off through the pager: DateInputTouch.test.tsx:1185 runs it on the mouse scroller only.
  • MonthScroller's coarse-pointer Reduced Motion dip can no longer run, two comments in MonthPager.tsx (lines 64-69) restate the code, and DateInputTouch.test.tsx:194 carries an orphan doc comment.

[Automated review]

Main's meta#10400 makes the sheet's calendar wait for today, and edits the
month pane inside MonthScroller.tsx. This branch moved that pane into
MonthPane.tsx, so those two edits land there: today is PlainDate | null
while it is not known yet, and a null today marks no day.
@cixzhang

cixzhang commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 7ea993f

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the code and the record edits are unchanged since the last review, and an engineering owner approved the record edits at this head, but the Android evidence still has to be recorded for this head.

  • Regression: spec:AST-094 DEC-27 asks for the Android emulator run (Chrome with Gboard, at normal speed, with animations removed and on a slow-device profile) at the exact head under review, and the description records it for the previous head and says it is still to be checked at this one, while this head merges main's changes to usePager's settle, the gesture host's lift clock and the animator; please run that set on this head's deployment and add the results to the Evidence section.

The non-blocking notes still stand: the size line in the description, a pager case for the range-end arrow hand-off after a swipe, the unreachable coarse-pointer dip in MonthScroller, two comments in MonthPager.tsx, and the stray doc comment in DateInputTouch.test.tsx.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The month pager follows the current DateInput spec (PR2, AR8) and the touch-presentation spec, an engineering owner approved the record edits at this head, and the Android emulator run the touch-presentation spec asks for is now recorded for this head.

Non-blocking notes:

  • Miss: the PR2 verification row drops the "counts the visible-month reports per half-month" evidence gap, but no test counts the reports; keep the phrase or add a count of onVisibleMonthChange calls across one crossing.
  • Still open from earlier rounds: the description's size line, a pager case for the range-end arrow hand-off after a swipe, the unreachable coarse-pointer dip in MonthScroller, two comments in MonthPager.tsx, and the stray doc comment in DateInputTouch.test.tsx.

[Automated review]

| AR6, AR8, PR1 | `DateInputTouch.test.tsx` calendar-surface cases ("mounts a window of months around the visible one, not the whole century", "leaves exactly one day per month tab-reachable", "gives a spilled date no tab stop in the pane that borrows it", "moves keyboard focus by date, across the month boundary", "follows the reading direction under RTL: ArrowLeft moves to the next day", "moves a week at a time with the vertical arrows", "labels the columns with three-letter weekday names", "marks the selection and today", "pages the keyboard focus by month with PageUp/PageDown and by year with Shift, on every pointer", "under a finger: pages the keyboard focus by month with PageUp/PageDown and by year with Shift", "under a finger: only the month the pager shows takes a tab stop", "under a finger: gives a spilled date no tab stop, and the off-screen month none at all") and wheel cases ("is a single tab stop driven by the arrow keys", "shows exactly one footer action, and only the visible one is reachable"); `DateInputTouch.calendar.test.tsx` "pages the keyboard focus by calendar months, and by calendar years with Shift" | Touch calendar and wheels; RTL | A second Tab stop per month, a wrong arrow move, reversed RTL arrows, an unbounded month window, or more than one reachable footer action fails. Evidence gap: no test asserts Home, End, or Enter and Space on a day. | `audit:DateInput/accessibility` |
| AR7 | `apps/storybook/rtl-audit/targets.json` DateInput entry; `DateInputTouch.test.tsx` RTL scroll-offset cases | RTL | A changed audit target or RTL offset fails. | `audit:DateInput/rtl` |
| PR2 | `MonthScrollerSettle.test.tsx` cases; `DateInputTouch.test.tsx` "a rest position between two months" cases | Settle and re-snap | A scroller that never settles or corrects mid-gesture fails. Evidence gap: no test asserts listener, frame, and observer cleanup on unmount, drags a month pager, or counts the visible-month reports per half-month (the PR2 gap). | `audit:DateInput/behavior` |
| PR2 | `MonthScrollerSettle.test.tsx` cases; `DateInputTouch.test.tsx` "a rest position between two months" cases and "under a finger: MonthPager pages the months with the shared pager, each pane on its own layer"; `MonthPager.interaction.browser.spec.ts` "the title follows the drag, changing at half a month", "a flick released before half a month turns the title as the settle crosses half, not at the lift", "a slow release before half a month returns; past it, the next month" | Settle and re-snap | A scroller that never settles or corrects mid-gesture fails. Evidence gap: no test asserts listener, frame, and observer cleanup on unmount. | `audit:DateInput/behavior` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Miss (non-blocking): this row drops the evidence gap "counts the visible-month reports per half-month", but no test counts the reports. The cited case "the title follows the drag, changing at half a month" reads the title text on each side of the crossing, which a repeated report of the same month would still pass. Keep that phrase in the gap, or count onVisibleMonthChange calls across one half-month crossing.

Main changed five files this branch edits, and git merged each one with
no conflict: MonthScroller.tsx (meta#10610's shared steer), TouchDateField,
TouchDateTimeField and DateInputTouch.test.tsx (the sheet and button
fixes), and MEASUREMENTS.md. This merge adds no edit of its own.
@cixzhang

cixzhang commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 90051f2

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the code and the record edits are unchanged, an engineering owner's approval holds at this head, and the flick now has its Android run here, but the rest of the Android evidence is still from the previous head.

  • Regression: spec:AST-094 DEC-27 asks for every Android claim to be shown in the Android emulator (Chrome with Gboard) at the exact head under review, with animations at normal scale, with animations removed and on a slow-device profile, and at this head the description records only the flick (normal and 4× CPU), so please run the other rows (the slow third-of-a-page drag, the › taps, the slow drag past half, the mostly-down drag that moves only the sheet, the Japanese calendar) and the animations-removed takes on this head's deployment, since this head brings main's spring-retarget (#10607), stylesheet (#10728) and sheet-inset (#8558) changes under the pager.

The non-blocking notes still stand: the PR2 verification row's dropped evidence gap, the description's size line, a pager case for the range-end arrow hand-off, the unreachable coarse-pointer dip in MonthScroller, two comments in MonthPager.tsx, and the stray doc comment in DateInputTouch.test.tsx.

[Automated review]

The pager's track carried data-month-track, a new public data
attribute that only the browser proofs read. The pager already holds
the track through trackRef, so the attribute goes. The proofs reach
the track as the months viewport's only child, and the mouse case
tells the snap scroller from the pager by its overflow.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested: the month pager's code and record edits are settled, but the Android evidence the touch-presentation spec requires is not at this head yet.

  • Regression: spec:AST-094 DEC-27 needs every Android Chrome claim taken in Chrome with Gboard in the Android emulator at the exact head (normal animations, animations removed and a slow-device profile), the description says the set at this head is pending, and DEC-31 cannot carry the earlier takes because this head edits MonthPager.tsx and its proofs and its merge changed a file this pull request changes.

The record edits carry the spec gate's owner approval at this head, and dropping data-month-track leaves the pager with only data attributes that already ship. The smaller follow-ups (the PR2 verification row's dropped counting gap, the description's size line, a pager case for the range-end arrow hand-off, two source comments) stay in their review threads. CI has not run on this draft head.

Automated review (maintainer link)

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved pending CI: CI has not run on this head yet, because the pull request is a draft; it runs once the pull request is marked ready for review.

The month pager and the PageUp/PageDown paging follow the current DateInput and touch-presentation specs, the record edits carry an engineering owner's approval at this head, and the Android emulator set the touch-presentation spec requires is now recorded for this head. No blocking findings remain.

Non-blocking follow-ups, each already in its review thread:

  • The PR2 verification row drops the "counts the visible-month reports" evidence gap, but no test counts the reports yet.
  • The description's size line is still a placeholder; the measured size and the reason the touch pager loads statically live only in a review-thread reply.
  • No unit case drives the range-end arrow hand-off through the pager, and two source comments in MonthPager.tsx could be tightened.

Automated review (maintainer link)

MonthPane, monthGridKeyTarget and useMonthGridFocus move back into
MonthScroller.tsx, and MonthPager imports them from there. No
behavior changes.
A Reduced Motion case's comment named two pull requests. It now names
only the animator default it relies on. Five comment sentences ran
past 25 words, or past 20 for an instruction. Each now reads as two or
more sentences, with the same meaning.
MonthScroller.tsx now exports MonthPane, monthGridKeyTarget and
useMonthGridFocus, with their types, and MonthPager imports them.
DateInput's record names them in its Ownership boundary, as the one
month grid both calendars render.
@cixzhang

Copy link
Copy Markdown
Contributor

/approve-spec f26df1b

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request is reviewed as part of its stack; see the review on #8206.

Automated review (maintainer link)

@cixzhang

Copy link
Copy Markdown
Contributor

/approve-spec f34d87f

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request is reviewed as part of its stack; see the review on #8206.

Automated review (maintainer link)

This branch was successfully deployed

1 active deployment
Preview — f34d87f7 Deployed Nov 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants