Skip to content

[calendar] Preserve keyboard focus through viewport remounts#4462

Draft
atomiks wants to merge 11 commits intomui:masterfrom
atomiks:codex/calendar-motion-focus-demo
Draft

[calendar] Preserve keyboard focus through viewport remounts#4462
atomiks wants to merge 11 commits intomui:masterfrom
atomiks:codex/calendar-motion-focus-demo

Conversation

@atomiks
Copy link
Copy Markdown
Contributor

@atomiks atomiks commented Mar 27, 2026

This preserves keyboard focus when Calendar.Viewport remounts month content to keep interrupted transitions restartable.

The fix moves the pending cross-month focus target into shared calendar state so the newly mounted day grid can complete the handoff after its item map is rebuilt. The docs Motion example can keep its original structure, and the wording update to Motion remains.

Changes

  • Preserve pending keyboard focus targets across Calendar.Viewport remounts in the calendar source.
  • Restore focus after the new day-grid item map commits for PageUp/PageDown and arrow-key month wrapping.
  • Add viewport-backed keyboard regression tests for cross-month navigation.
  • Keep the Motion wording update in the calendar docs.

Copy link
Copy Markdown
Contributor Author

atomiks commented Mar 27, 2026

Codex Review (GPT-5.4)

This remains a bug-fix PR with secondary tests, docs, and react-ui changes. I re-reviewed the full rebased branch against freshly fetched upstream/master, including the latest follow-up commit, and I do not have any remaining branch-relevant findings.

1. Remaining Bugs / Issues (None)

2. Root Cause & Patch Assessment

The core fix is still in the right layer. SharedCalendarStore owns the pending cross-month focus request, and useSharedCalendarDayGridBody fulfills that request after the new grid mounts instead of trying to patch focus back in at the demo level.

The latest follow-up also closes the deferred controlled-update gap without adding much extra machinery. The next-tick timeout is gone, the handoff now survives asynchronous visibleDate updates, and stale requests are only abandoned once focus has already moved outside the original day grid. That keeps the controlled path aligned with the real interaction model instead of guessing based on timing.

3. Pattern Consistency Assessment

Area Assessment Notes
Source vs demo ownership Good The behavioral fix stays in packages/react/src/calendar/... instead of baking calendar-specific focus hacks into the docs demos
Native fixture alignment Improved The native e2e fixture reuses the real docs demo instead of maintaining a parallel CSS copy
Unit/browser harness scope Improved The package-side animated viewport harness is intentionally synthetic, which avoids future drift from docs animation styling
Focus handoff cleanup Improved Deferred controlled updates are now handled by DOM-state checks on the mounted grid rather than a timeout guess

4. Edge Cases Worth Noting

Case Status Notes
Controlled visibleDate rejection Handled The stale pending-focus path is still cleared before later external month changes
Deferred controlled visibleDate updates Handled The handoff now survives async parent updates and is covered by a dedicated regression test
Offset day grids Handled The viewport remount path is covered for offset grids as well as the main month
Interrupted keyboard navigation Handled The follow-up keypress during an active animated transition is covered in both source tests and e2e

5. Test Coverage Assessment

The regression coverage is in good shape now and it is validating the right failure modes.

Meaningful validation I checked on the rebased branch:

  • pnpm test:jsdom CalendarDayGridBody --no-watch
  • pnpm test:chromium CalendarDayGridBody --no-watch
  • pnpm exec vitest run --project e2e test/e2e/index.test.ts -t '<Calendar />'
  • pnpm typescript

The most important addition is the deferred controlled viewport regression in CalendarDayGridBody.keyboard.test.tsx, which now exercises the lifecycle case that was previously only covered for synchronous parents.

6. Previous Review Issues Status

Previous issue Status Notes
Animated viewport harness drifted from the real native demo Resolved Native e2e reuses the real docs demo, and the package test harness is intentionally generic rather than a stale copy
Motion e2e depended on fixed sleeps Resolved The sleeps were replaced with state-based waits for focus and mounted-body count
Deferred controlled visibleDate updates could clear the handoff too early Resolved The timeout cleanup is gone, and the deferred controlled path is now covered by a dedicated regression test

Merge Confidence Score

Overall merge confidence is 4/5. I do not see a remaining branch-relevant bug in the current calendar fix, and the regression coverage now covers the deferred controlled path that was previously missing. I would be comfortable merging this version.

@atomiks atomiks added type: bug It doesn't behave as expected. docs Improvements or additions to the documentation. examples Relating to /examples. labels Mar 27, 2026 — with ChatGPT Codex Connector
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 27, 2026

commit: cce7159

@mui-bot
Copy link
Copy Markdown

mui-bot commented Mar 27, 2026

Bundle size report

Bundle Parsed size Gzip size
@base-ui/react 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 27, 2026

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit cce7159
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/69c754cc1dfd9400082558a9
😎 Deploy Preview https://deploy-preview-4462--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@atomiks atomiks changed the title [calendar] Fix motion demo keyboard focus [calendar] Preserve keyboard focus through viewport remounts Mar 27, 2026
@atomiks atomiks added type: regression A bug, but worse, it used to behave as expected. scope: temporal Changes related to the Temporal components. type: bug It doesn't behave as expected. and removed type: bug It doesn't behave as expected. examples Relating to /examples. docs Improvements or additions to the documentation. type: regression A bug, but worse, it used to behave as expected. labels Mar 27, 2026
@atomiks atomiks force-pushed the codex/calendar-motion-focus-demo branch from 4528c87 to cce7159 Compare March 28, 2026 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: temporal Changes related to the Temporal components. type: bug It doesn't behave as expected.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants