Skip to content

Keep focus in the grid when a controlled month change removes the focused day - #3012

Closed
raphyabak wants to merge 2 commits into
gpbl:mainfrom
raphyabak:fix/focus-lost-on-controlled-month-change
Closed

Keep focus in the grid when a controlled month change removes the focused day#3012
raphyabak wants to merge 2 commits into
gpbl:mainfrom
raphyabak:fix/focus-lost-on-controlled-month-change

Conversation

@raphyabak

Copy link
Copy Markdown

Summary

Fixes #3009.

When the month prop changes under external control (e.g. an app updates both month and selected together, such as after undoing a date selection), the previously focused day can be removed from the DOM entirely. useFocus's focusedDay state kept referring to that removed day, so no day had the focused modifier after the update, DayButton never called .focus() on anything, and focus silently fell back to <body> — breaking keyboard continuity (arrow keys stop moving between days) for any controlled calendar whose value can change externally.

// Focus a day in March, then press J:
setMonth(november);
setSelected(november);
// November is displayed and November 15 is selected, but
// document.activeElement is <body>, and arrow keys do nothing.

Fix

Added an effect in useFocus that detects when focusedDay is no longer among the currently displayed calendar.days and moves focus to calculateFocusTarget's result instead — the same helper already used for autoFocus, which prioritizes the selected day, then today, then the first focusable day. Focus is left untouched when it wasn't in the grid to begin with, per the issue's expected behavior.

Test plan

  • Added two tests under DayPicker.test.tsx:
    • Focus a day in the initial month, then a controlled month+selected update (with onSelect wired up, so the selection is genuinely controlled): asserts focus lands on the newly selected day rather than <body>.
    • Same update without ever focusing a day first: asserts focus stays on <body> (i.e. the fix doesn't grab focus that wasn't in the grid).
  • Verified the regression: reverting the source change turns the first new test from passing into failing, with document.activeElement on <body> — exactly the reported bug.
  • Full package suite passes: npx jest packages/react-day-picker — 492 passing.
  • biome check and tsc --noEmit both pass on the changed files.

…used day

Fixes gpbl#3009.

When the month prop changes under external control (e.g. after undoing
a selection changes both month and selected), the previously focused
day can be removed from the DOM. useFocus's focusedDay state kept
referring to that removed day, so no day had the "focused" modifier
after the update, DayButton never called .focus() on anything, and
focus fell back to <body>, breaking keyboard continuity.

Add an effect that detects when focusedDay is no longer among the
displayed days and moves focus to calculateFocusTarget's result
instead, which already prioritizes the selected day, then today, then
falls back to the first focusable day. Focus is left alone when it
wasn't in the grid to begin with.
@raphyabak
raphyabak requested a review from gpbl as a code owner August 26, 2026 07:33
@changeset-bot

changeset-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dce26e1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
react-day-picker Patch
@daypicker/react Patch
@daypicker/buddhist Patch
@daypicker/ethiopic Patch
@daypicker/hebrew Patch
@daypicker/hijri Patch
@daypicker/persian Patch

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

@raphyabak

Copy link
Copy Markdown
Author

Closing this — #3009 was already fixed and merged independently via #3010. Thanks for taking care of it!

@raphyabak raphyabak closed this Aug 28, 2026
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.

Focus is lost when a controlled month change removes the focused day

1 participant