fix(date-picker): keep input focus when open is controlled - #3234
Open
midub wants to merge 1 commit into
Open
Conversation
Clicking the date picker input opens the calendar and then moves focus
into the calendar grid, but only when `open` is controlled.
`focusActiveCell` skips the focus move when the triggering event carries
`src: "input.click"`. Under a controlled `open`, the `OPEN` event is
short-circuited by `isOpenControlled`; the consumer flips the prop and the
`open` watcher re-dispatches through `toggleVisibility` as
`{ type: "CONTROLLED.OPEN", previousEvent: event }`. The origin then only
survives at `event.previousEvent.src`, so the guard never matches.
Read the origin through `previousEvent`, matching `getOpenChangeReason` in
combobox and `isInteractOutsideEvent` in this machine.
🦋 Changeset detectedLatest commit: 01200c0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 86 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 Description
Clicking the date picker's text input opens the calendar and then moves DOM focus into the calendar grid — but only when
openis controlled. Focus is pulled out from under the user while they are typing a date. With uncontrolledopen, thesame click correctly leaves focus in the input.
⛳️ Current behavior (updates)
The input's click handler sends the open event tagged with its origin:
and
focusActiveCelluses that tag to skip pulling focus into the grid:Under a controlled
openthatOPENevent never reaches the state transition.isOpenControlledshort-circuits it toinvokeOnOpen, the consumer flips theopenprop, and thewatchonprop("open")re-dispatches throughtoggleVisibility:CONTROLLED.OPENin bothidleandfocusedruns["resetView", "focusFirstSelectedDate", "focusActiveCell"]. By thenevent.srcisundefined— the origin only survives atevent.previousEvent.src— so the guard never matches and focusis always taken.
The guard is correct; the event it inspects is the wrapper rather than the cause.
🚀 New behavior
focusActiveCellreads the origin throughpreviousEvent, so a controlled open initiated by an input click is treatedthe same as an uncontrolled one:
This matches how the rest of the codebase already reads event fields across the controlled boundary —
getOpenChangeReasonin
combobox.machine.tsuses exactly(event.previousEvent || event).src, andisInteractOutsideEventin this samemachine already reads
event.previousEvent?.type.focusActiveCellwas the only place readingevent.srcwithout thefallback.
Opening via the calendar trigger button still moves focus into the grid, so keyboard/a11y behaviour is unchanged.
Scope notes
CONTROLLED.*wrapper:
focusActiveCellIfNeeded(if (!event.focus) return) only fires from thefocusedValuewatcher andfocusis only ever set by
TABLE.*keyboard events, andfocusFirstInputElement(if (event.focus === false) return) onlyruns on
VALUE.CLEAR. Both fail closed, so neither steals focus on the controlled path.toggleVisibility/previousEventconvention (combobox, select, cascade-select,menu, popover, editable, tooltip) already unwrap
previousEventwhere they read across it, so this looks like aone-off rather than a class of bug.
💣 Is this a breaking change (Yes/No):
No.
📝 Additional Information
Repro: a controlled-
opendate picker withopenOnClick— added asexamples/next-ts/pages/date-picker/controlled.tsx,since the existing
open-controlexample hardcodesopen={false}and so cannot reach theCONTROLLED.OPENpath.Tests: added
datepicker [controlled open + openOnClick]toe2e/date-picker.e2e.tscovering both directions —input click under controlled
openkeeps focus in the input, and trigger click still focuses the active cell. The firsttest fails on
mainand passes with this change; the second passes both before and after.