Skip to content

chore: upgrade calendar dependencies and add the date foundation - #894

Open
Shreyag02 wants to merge 1 commit into
rfc-005-calendar-previewfrom
chore/calendar-deps-upgrade
Open

chore: upgrade calendar dependencies and add the date foundation#894
Shreyag02 wants to merge 1 commit into
rfc-005-calendar-previewfrom
chore/calendar-deps-upgrade

Conversation

@Shreyag02

Copy link
Copy Markdown
Contributor

PR 1 of 7 in the RFC 005 stack — RFC 005: Calendar Rewrite CalendarPreview (docs/rfcs/005-calendar-preview.md).

This PR ships no UI. lib/ lands tested before any component exists to use it, per review point 5 on #890 and the phase 0 exit criteria in the RFC's Plan.

Dependencies

Bumped in packages/raystack only. Nothing else in the manifest moves — tanstack, dnd-kit, prosemirror, culori, class-variance-authority and prism-react-renderer are untouched.

Package From To
react-day-picker ^9.6.7 ~10.0.1
@base-ui/react ~1.6.0 ~1.7.0
@base-ui/utils ~0.3.1 ~0.3.2 — pinned exactly by Base UI 1.7.0
dayjs ^1.11.20 ^1.11.23

dayjs stays in the manifest: the old family imports it until components/calendar/ is deleted at phase 6.

react-day-picker 10 needs no source change

Verified against the installed v10 types rather than the changelog:

  • The 16 @deprecated v8-era props and the Button CustomComponents entry are gone; packages/raystack references none of them. The Button: hits in the tree are Toolbar.Button, CodeBlock.CopyButton and friends — unrelated components.
  • All five component slots the family overrides still exist in v10 — PreviousMonthButton, NextMonthButton, Dropdown, DayButton, MonthGrid — as do all twenty classNames keys it sets.
  • The mode/required union is unchanged from 9.6.7, so there is no selection-type churn, as the RFC's Dependencies predicted.
  • The existing calendar suite — 97 tests across 6 files — passes against v10 with components/calendar/ untouched.

One addition beyond the four bumps

date-fns (^4.1.0) and @date-fns/tz (^1.5.0) are now explicit dependencies.

The RFC notes they already ship transitively via react-day-picker, and they do — in the published tarball's runtime graph. But that is not enough to import them here: pnpm's isolated node_modules does not resolve a transitive dependency from our own source (confirmed — require.resolve('date-fns') from packages/raystack/components fails without the declaration), and nodeExternals({ deps: true }) in rollup.config.mjs only externalises what the manifest declares, so an undeclared import would also get inlined into the bundle. Both are declared at ranges that satisfy react-day-picker's dependency and Base UI's optional peer, and the lockfile still resolves a single instance of each.

What lands

packages/raystack/components/calendar-preview/
├── date-adapter.ts     # the only module that calls a date library
├── lib/scale.ts        # periodOf, anchorOf, convertScale, isAvailable
├── lib/parse.ts        # typed string -> { date, scale }
└── __tests__/          # scale, parse, date-adapter

No component, part, hook, CSS, story or barrel export — nothing is wired into packages/raystack/index.tsx yet.

date-adapter.ts

One module owns every date-library call, so the plugin set is a single fact in one place and the import-order-dependent dayjs.extend() failure class has nowhere to live. No extend() is added anywhere in this PR.

Values cross lib/ as DayKeys — 'YYYY-MM-DD', timeless. Their lexicographic order is chronological, so comparing a day against a bound needs no library call and cannot drift by a timezone. dayKey() takes an optional timeZone, which is where @date-fns/tz earns its place: it is the call that stops a grid rendered at a timeZone from keying its cells a day off, the shape of the current family's tooltip/dateInfo bug.

lib/scale.ts

The model verbatim from The Scale Model:

  • Conversion, one rule, every direction. Take the anchor date, find the period of the target scale containing it, emit that period's start when leading and its end when trailing.
  • Availability tests the date the period would produce, not the period start. With min = 2026-07-15 and trailing, Jul 2026 (emits 31 Jul) and Q3 2026 (emits 30 Sep) are available while H1 2026 (emits 30 Jun) is disabled — each asserted individually, alongside a test that all three periods start before the bound, so only the produced-date rule separates them. A second test asserts the two rules coincide whenever trailingValue is false.
  • Month ends are calendar-correct. February 2028 trailing is 2028-02-29; February 2100 is 2100-02-28 and February 2000 is 2000-02-29, so the century rule is covered in both directions.
  • halfYear is H1 Jan–Jun, H2 Jul–Dec — ours to derive, since no date library has it.

lib/parse.ts

Reads a typed string as a date and a scale together: 20/05/2027 → day, May 2027 → month, Q4 → quarter, H1 2026 → halfYear, 2025 → year. 2027-05-20 is also accepted so a stored value round-trips.

Year inference for a bare Q4, H1 or May: the period resolves inside the reference year — the calendar year of referenceDate, defaulting to now — and the rule never rolls forward. Q1 typed in December 2026 is Q1 2026, not Q1 2027. A "next occurrence" rule would make the same typed string mean different years depending on the day it was typed, so Q1 would change meaning across midnight on 31 December and a stored value would not agree with the string that produced it after a reload. The rule is documented on the function and tested from both the first and last day of a year.

Recognition is pinned by a regex before any date maths runs, so near-misses are rejected rather than coerced — including 20/05/27, the two-digit year that dayjs' customParseFormat reads as year 27. 26 rejection cases are asserted, each named.

Testing

  • lib/scale.ts, lib/parse.ts and date-adapter.ts at 100% statement, branch, function and line coverage — leap years, month-end snapping, period boundaries at both edges, day→year→day round trips, availability at both edges against min and max, and all 25 scale-to-scale conversion directions at both edges.
  • 254 new tests. pnpm --filter @raystack/apsara test is green: 2961 passed, 1 skipped (pre-existing data-view/debug.test.tsx).
  • pnpm --filter @raystack/apsara build is clean.
  • biome check clean on the new directory.
  • tsc --noEmit reports the same 6 pre-existing errors as the base branch — data-table, data-view, menu and popover — and none in calendar/ or calendar-preview/. Confirmed by installing the base branch's package.json and lockfile and re-running.

🤖 Generated with Claude Code

Phase 0 of RFC 005. Moves the calendar dependencies to the versions the
rewrite targets and lands `lib/` — the scale maths and input parsing —
tested, before any UI exists to use it.

Dependencies, in `packages/raystack` only:

- `react-day-picker` ^9.6.7 -> ~10.0.1
- `@base-ui/react` ~1.6.0 -> ~1.7.0, which pins `@base-ui/utils` at 0.3.2
- `dayjs` ^1.11.20 -> ^1.11.23, kept because the old family still imports
  it; it goes with `components/calendar/` at phase 6

The v10 major needs no source change. It drops the 16 `@deprecated` v8-era
props and the `Button` component slot, none of which this package
references, and the `mode`/`required` union is unchanged from 9.6.7 — so
the four component slots the family overrides (`PreviousMonthButton`,
`NextMonthButton`, `Dropdown`, `DayButton`, `MonthGrid`) and all twenty
`classNames` keys still resolve. The existing 97 calendar tests pass
against it untouched.

`date-fns` and `@date-fns/tz` become explicit dependencies. They already
reached the published tarball through react-day-picker, but pnpm's
isolated layout does not resolve a transitive dependency from our own
source, and `nodeExternals({ deps: true })` only externalises what the
manifest declares. Importing them undeclared would break both.

`date-adapter.ts` is the one module allowed to call a date library, so the
plugin set is a single fact in one place and the import-order-dependent
`dayjs.extend()` failure class has nowhere to live. No `extend()` is added
anywhere. Values cross `lib/` as `DayKey`s — `'YYYY-MM-DD'`, timeless —
whose lexicographic order is chronological, so comparing a day against a
bound needs no library call and cannot drift by a timezone.

`lib/scale.ts` implements the model verbatim: conversion takes the anchor
date, finds the period of the target scale containing it, and emits that
period's start when leading or its end when trailing; availability tests
the date the period would produce, not the period start, so July 2026 and
Q3 2026 are selectable in an end field bounded at 15 July while H1 2026 is
not. Month ends are calendar-correct — February 2028 trailing is
2028-02-29, February 2100 is 2100-02-28.

`lib/parse.ts` reads a typed string as a date and a scale together. A bare
`Q4`, `H1` or `May` resolves inside the reference year and never rolls
forward, so the same string does not change meaning across midnight on 31
December; the rule is documented on the function and tested at both ends
of a year.

100% statement, branch, function and line coverage on all three modules.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
apsara Ready Ready Preview Sep 3, 2026 11:07pm UTC

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 9524b056-d8cd-434e-9113-51dc522519d7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@raystack/apsara@894

commit: 8964c2b

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.

1 participant