Skip to content

fix(Calendar): focus first day when no selected or today cell on initial focus#2676

Open
nakagam3 wants to merge 1 commit into
unovue:v2from
nakagam3:fix/calendar-initial-focus-fallback
Open

fix(Calendar): focus first day when no selected or today cell on initial focus#2676
nakagam3 wants to merge 1 commit into
unovue:v2from
nakagam3:fix/calendar-initial-focus-fallback

Conversation

@nakagam3

@nakagam3 nakagam3 commented Jun 5, 2026

Copy link
Copy Markdown

TL;DR — Initial keyboard focus lands nowhere in every calendar/picker component when no date is selected and today is outside the visible month. Root cause: the focus fallback queries [data-reka-calendar-day], an attribute no component renders. Fix points it at the existing cell marker. One-line change + regression test, non-breaking.

🔗 Linked issue

No issue was filed — I hit this while integrating Calendar in a downstream project. The root cause, reproduction and verification are all below, so this PR is self-contained; happy to open a tracking issue first if you'd prefer.

❓ Type of change

  • 🐞 Bug fix (a non-breaking change that fixes an issue)

📚 Description

handleCalendarInitialFocus (packages/core/src/shared/date/utils.ts) resolves initial focus in three steps: selected day → today → first day. The third fallback queries for [data-reka-calendar-day]:

const firstDay = calendar.querySelector<HTMLElement>('[data-reka-calendar-day]')

No component renders that attribute. Day cells expose data-reka-calendar-cell-trigger (and the Month/Year picker variants expose their own data-reka-*-cell-trigger). A repo-wide grep finds data-reka-calendar-day referenced only on this one line and assigned nowhere — so the fallback always matches null.

Impact: when neither a selected day nor today exists in the visible view, initial focus lands nowhere. The simplest reproduction is an unselected calendar whose displayed month does not contain today (e.g. opened on a past month). This affects every consumer of handleCalendarInitialFocus: CalendarRoot, RangeCalendarRoot, DatePicker, DateRangePicker, MonthPicker, YearPicker, MonthRangePicker, YearRangePicker.

Fix

-  const firstDay = calendar.querySelector<HTMLElement>('[data-reka-calendar-day]')
+  const firstDay = calendar.querySelector<HTMLElement>('[data-value]:not([data-outside-view]):not([data-disabled])')
  • data-value is present on every calendar/picker cell trigger, so one selector fixes all the components above.
  • :not([data-outside-view]) skips spill-over days from the previous/next month (the attribute only exists on Calendar/RangeCalendar, so it is a harmless no-op for the Month/Year pickers).
  • :not([data-disabled]) skips cells that cannot receive focus — disabled cells render without a tabindex, so without this the fallback would target an unfocusable cell and focus would still land nowhere.
  • This mirrors the selector reka-ui already uses internally for cell navigation, e.g. CalendarCellTrigger.vue and RangeCalendarCellTrigger.vue both query `[data-value='${...}']:not([data-outside-view])`.

Verification (TDD)

Added a regression test to Calendar/Calendar.test.ts (in the calendar - edge cases block) that renders an unselected calendar with a placeholder month containing no today, asserts neither [data-selected] nor [data-today] exists, then calls handleCalendarInitialFocus and expects the first in-view day to receive focus.

  • Before fix: the test fails — focus stays on <body> because the old selector matches nothing.
  • After fix: the test passes — focus lands on the first in-view, non-disabled day.

All Calendar-family suites pass (Calendar, RangeCalendar, DatePicker, DateRangePicker, MonthPicker, YearPicker, MonthRangePicker, YearRangePicker), and eslint is clean.

📝 Checklist

  • I have linked an issue or discussion. (No prior issue exists — see the Linked issue section above.)
  • My change is non-breaking and requires no documentation update (internal fix, no public API/props/type change).
  • I have added a regression test covering the fix.

Summary by CodeRabbit

  • Tests

    • Added test coverage for calendar initial focus behavior when no date is selected.
  • Bug Fixes

    • Improved calendar focus handling to correctly select the first available date when "today" is not visible.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ccfa6221-e6c0-4027-b457-d3d8474112ec

📥 Commits

Reviewing files that changed from the base of the PR and between 091d650 and 96fbba9.

📒 Files selected for processing (2)
  • packages/core/src/Calendar/Calendar.test.ts
  • packages/core/src/shared/date/utils.ts

📝 Walkthrough

Walkthrough

The PR refines calendar initial focus behavior by updating the selector in handleCalendarInitialFocus to be more specific—targeting [data-value] elements while excluding disabled and out-of-view dates. A new test validates this refinement by verifying focus is placed on the correct date when today is not visible.

Changes

Calendar initial focus handling

Layer / File(s) Summary
Initial focus selector refinement
packages/core/src/shared/date/utils.ts
handleCalendarInitialFocus selector changes from generic [data-reka-calendar-day] to [data-value]:not([data-outside-view]):not([data-disabled]) to select only focusable dates within the current view.
Initial focus behavior test
packages/core/src/Calendar/Calendar.test.ts
New test imports handleCalendarInitialFocus and verifies it correctly focuses the first focusable date when no date is selected and "today" is outside the visible calendar range.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • unovue/reka-ui#2423: Adjusts focusable day selection logic in CalendarCellTrigger and RangeCalendarCellTrigger through tabindex and firstFocusableDate computations, complementing this PR's refinement of the fallback focus selector.

Suggested reviewers

  • epr3

Poem

🐰 A calendar's eye finds its way,
No "today" in sight? That's okay!
With selectors refined, the focus now flows,
To the first focusable date that the test shows. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: addressing initial focus behavior in Calendar when neither a selected nor today cell exists, and it aligns with the primary change of fixing the handleCalendarInitialFocus selector.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ial focus

The third fallback branch of handleCalendarInitialFocus queried for
`[data-reka-calendar-day]`, an attribute that no component renders (day cells
expose `data-reka-calendar-cell-trigger` and friends instead). As a result,
when neither a selected day nor today is present in the visible view (e.g. an
unselected calendar whose displayed month does not contain today), initial
focus landed nowhere.

Switch the fallback to `[data-value]:not([data-outside-view]):not([data-disabled])`.
`data-value` is shared by every calendar/picker cell trigger, so the fix covers
Calendar, RangeCalendar, DatePicker, DateRangePicker and the Month/Year (Range)
pickers alike, and it matches the selector reka-ui already uses internally for
cell navigation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nakagam3 nakagam3 force-pushed the fix/calendar-initial-focus-fallback branch from b2353e8 to 96fbba9 Compare June 5, 2026 15:10
@nakagam3 nakagam3 marked this pull request as ready for review June 5, 2026 16:20
@pkg-pr-new

pkg-pr-new Bot commented Jun 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2676

commit: 96fbba9

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.

2 participants