Skip to content

fix(Calendar): a two-month calendar keeps max's month in the last pane - #11202

Merged
cixzhang merged 2 commits into
meta:mainfrom
Varshavia:fix/calendar-two-month-max
Nov 12, 2026
Merged

cixzhang merged 2 commits into
meta:mainfrom
Varshavia:fix/calendar-two-month-max

Conversation

@Varshavia

Copy link
Copy Markdown
Contributor

Fixes #9357

User impact

Anyone using a two-month Calendar, or a DateRangeInput (two months by default), with max in the month the calendar opens on. The common case is max set to today: the calendar opens on [this month, next month], every day of next month is disabled, and the previous month, which has selectable days, is hidden behind a Previous click. The same happens when the selected value starts in max's month.

Problem and solution fit

getInitialFocusDate shifted the panes back only when today was after max. When today, or the value's start, was on or before max in the same month, it returned that date unchanged, so the second pane landed wholly past max. Half the calendar then shows nothing a user can pick, and DateRangeInput passes no focusDate, so an app cannot correct it. The fix applies the existing shift to these two cases as well.

Expected behavior and authority

Smallest restoration

One helper in getInitialFocusDate.ts, used for the value and the in-window today paths. It moves the first pane back so the last pane is max's month, never before min's month. Unchanged: an explicit focusDate, a value past max, a one-month calendar, and the existing today-after-max branch. No API, visual, or navigation change.

One point for review: the value path used to be left as given. A value that starts in max's month now opens in the second pane; its month is still shown.

Evidence

  • Reproduction before the change: without the fix, 5 of the new getInitialFocusDate cases fail (max is today, max later in the same month, min in the previous month, value in max's month, year boundary).
  • Result after the change: getInitialFocusDate.test.ts 26/26; a two-month Calendar with max = today (21 Aug 2026) shows "July 2026 – August 2026" with July 31 enabled.
  • Representative unchanged path: max in a later month still opens on today; the one-month layout still opens on today; focusDate in max's month is honored as given.
  • Regression test or other mutation-sensitive proof: 11 new unit cases in getInitialFocusDate.test.ts, 2 render cases in Calendar.test.tsx.

Scope

Components: Calendar, DateRangeInput

  • One defect is restored.
  • Separable contradictory or unsettled tagalongs were removed or split.
  • No new public API, visual representation, default, or interaction model is hidden under the bug-fix label.
  • Public text and artifacts contain no internal Meta context.

Testing

  • pnpm vitest run packages/core/src/Calendar packages/core/src/DateRangeInput packages/core/src/DateInput — 33 files, 1095 tests pass.
  • pnpm check:repo (pre-commit) passes.

@vercel

vercel Bot commented Nov 11, 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 12, 2026 1:08am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 12, 2026, 1:31 AM UTC

@github-actions github-actions Bot added community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge labels Nov 11, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 11, 2026
@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 2e0da24

Storybook · Sandbox · built from this PR's head

Modified components

Component Size LoC Visual · A11y · RTL
DateTimeInput 215.4 kB (+109 B) · 18.7 kB CSS (0) — — · — · —
DateRangeInput 97.5 kB (+107 B) · 12.5 kB CSS (0) — — · — · —
Calendar · Storybook 74.4 kB (+102 B) · 10.6 kB CSS (0) 1,798 (+45) ✓ · ✓ · ✓
2 more components
Component Size LoC Visual · A11y · RTL
PowerSearch 299.2 kB (+95 B) · 24.7 kB CSS (0) — — · — · —
DateInput 192.7 kB (+93 B) · 17.0 kB CSS (0) — — · — · —

Package size: @astryxdesign/core 7.36 MB packed (+868 B); no new dependencies.

Touched components checked; add ci:run-visual or ci:run-rtl to run the full suites.


Generated by PR Enrichment workflow | View full report

@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 on hold for an engineering owner's decision, and no code change is needed for it.

The Calendar spec (component:Calendar) does not yet decide which month a two-month calendar opens on. This change picks a rule for two cases: today in max's month, and a selected value that starts in max's month. The second case also ends the current rule that the value's month always comes first. Approval needs an engineering owner to provide or confirm the governing spec for both cases. They record it by commenting /approve-spec <full head SHA> here, and the author can do so only if they are an engineering owner.

CI has not run yet. A maintainer adds the ci:run label so it runs.

Automated review (maintainer link)

Comment on lines +66 to +69
* The same shift applies when the value or today is inside the window but in
* `max`'s month (`max` set to today is the common case): the month stays in
* view, in the last pane, and the panes before it show months that still have
* selectable days.

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.

Non-blocking: this is one 46-word sentence with three topics, and INV29 asks for at most 25 words per descriptive sentence, one topic each. Suggested:

 * The same shift applies when the value or today is in `max`'s month.
 * That month then shows in the last pane. The earlier panes show months
 * with selectable days.

Comment on lines +137 to +141
* Moves the opening month back when `date`'s month is shown but a later pane
* would lie wholly past `max`: the last pane lands on `max`'s month, and the
* first pane never goes before `min`'s month. A date whose panes already end
* by `max`'s month, or whose own month is past `max`'s, is returned unchanged,
* so its month always stays in view.

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.

Non-blocking: these sentences run 35 and 26 words, and the second is passive. INV29 asks for at most 25 words, active voice. Suggested:

 * Moves the first pane back so that no pane lies wholly past `max`. The last
 * pane then shows `max`'s month. The first pane never goes before `min`'s
 * month. A date in an earlier month, or past `max`'s month, comes back
 * unchanged.

Comment on lines +176 to +177
// `min` is closer than the full shift: go back only as far as its month,
// and not at all when the date's own month is already there or earlier.

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.

Non-blocking: Calendar renders only one or two panes, and with two panes latestFirstPane is before minMonth only when min is in max's month, so month equals minMonth and this always returns date. The minDate branch and this 29-word instruction cover a layout that cannot occur. Consider returning date here, or shortening the comment to "min's month limits the shift."

@cixzhang

Copy link
Copy Markdown
Contributor

/approve-spec 44bac6f

@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. A maintainer adds the ci:run label so CI runs; the label takes effect on the next push, or a close and reopen.

An engineering owner approved the new opening-month rule for both cases (today in max's month, and a selected value that starts in max's month), and the tests pin both shifts and every unchanged path.

Optional, non-blocking:

  • The new comments in getInitialFocusDate.ts would read better as one short sentence per fact; the inline suggestions on those lines still apply.
  • The focusDate docs in Calendar.tsx and Calendar.doc.mjs still say an unset focusDate opens on the selected date or today; a note on the two-month rule would keep them accurate.

Automated review (maintainer link)

@cixzhang cixzhang added ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) ci:run-a11y Run the accessibility suite on this PR; write access, next push (AST-030 FR12) labels Nov 11, 2026
@cixzhang
cixzhang marked this pull request as draft November 11, 2026 19:09
@cixzhang
cixzhang marked this pull request as ready for review November 11, 2026 19:10
@github-actions github-actions Bot removed the needs:code-review High-risk change (new package/component/API) — needs human code review before merge label Nov 11, 2026
@cixzhang

Copy link
Copy Markdown
Contributor

/approve-spec 2e0da24

@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. An engineering owner approved the new opening-month rule for this head, for both cases (today in max's month, and a selected value that starts in max's month), the tests pin both shifts and the unchanged paths, and CI passes.

Optional, non-blocking:

  • The new comments in getInitialFocusDate.ts would read better as one short sentence per fact; the earlier inline suggestions on those lines still apply.
  • The focusDate docs in Calendar.tsx and Calendar.doc.mjs still say an unset focusDate opens on the selected date or today; a note on the two-month rule would keep them accurate.
  • Newly live check: the description lists "a value past max" as unchanged, but a value past max inside max's own month now opens one month earlier, as the value rule says; "a value in a month after max's" would be exact.
Claims and files checked
  • Changeset: a two-month calendar whose max is in its opening month now shows that month in the second pane: verified (getInitialFocusDate.ts:91-99, :127-133; new unit cases).
  • Changeset: before, with max set to today or a value starting in max's month, it opened on that month and a fully disabled next month: verified against the base logic.
  • Changeset: the first pane never goes before min's month: verified (:172-178; "does not shift back past min").
  • Changeset: an explicit focusDate is shown as given: verified (:84-86).
  • Changeset: a one-month calendar is unchanged: verified (:151).
  • Description: DateRangeInput opens two months by default and passes no focusDate: verified (DateRangeInput.tsx:588, :950-963).
  • Description: the base shifted only when today was after max and otherwise returned today or the value's start: verified (base getInitialFocusDate.ts).
  • Description: the linked issue asks for the shift from today or the value, stopping at min's month: verified (the quote paraphrases the issue's expected behavior).
  • Description: one helper serves the value path and the in-window today path: verified (:92, :127).
  • Description: focusDate, a value past max, a one-month calendar and the today-after-max branch are unchanged: verified for a value in a month after max's; see the note above.
  • Description: no API, visual-style or navigation change: verified (no export, style or navigation code changes; the API report lists no change).
  • Description: 5 of the new unit cases fail without the fix: verified (5 of 11 fail against the base logic).
  • Description: 26 unit cases pass, with 11 new unit cases and 2 new render cases: verified (CI passes all 26 unit cases and the Calendar suite on this head).
  • Description: the max-is-today render case shows July–August 2026 with July 31 enabled: verified (the case passes in CI on this head).
  • Description: the Calendar, DateRangeInput and DateInput suites and the repository checks pass: verified on this head by CI's test, typecheck and lint jobs (the suite totals differ after the merge from main).
  • Files: getInitialFocusDate.ts (non-blocking comment notes), getInitialFocusDate.test.ts (clear), Calendar.test.tsx (clear), the Changeset (clear).

Automated review (maintainer link)

@cixzhang
cixzhang merged commit 0f4e2c3 into meta:main Nov 12, 2026
77 checks passed

This branch was successfully deployed

1 active deployment
Preview — 2e0da24d Deployed Nov 12, 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) ci:run-a11y Run the accessibility suite on this PR; write access, next push (AST-030 FR12) CLA Signed This label is managed by the Meta Open Source bot. community Authored by a community contributor (not on the eng/design team)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Calendar: a two-month calendar opens with its second month wholly past max when max is in the current month

2 participants