Skip to content

fix(calendar): merge consumer classNames and formatters with defaults - #905

Open
rashmi09-sys wants to merge 1 commit into
cosscom:mainfrom
rashmi09-sys:fix/calendar-merge-classnames-formatters
Open

rashmi09-sys wants to merge 1 commit into
cosscom:mainfrom
rashmi09-sys:fix/calendar-merge-classnames-formatters

Conversation

@rashmi09-sys

Copy link
Copy Markdown

Problem

Calendar silently drops two kinds of consumer customisation:

  1. classNames for parts without a coss default are ignored. Only the 20 parts coss styles are merged, so classes for any other DayPicker part (root, weekdays, selected, month_grid, focused, footer, …) never reach DayPicker. The docs describe classNames as "Custom class names for calendar parts", and passing the same object to DayPicker directly applies all of them.
  2. Any formatters prop replaces the default month formatter. Passing an unrelated formatter (e.g. formatWeekdayName) switches the month dropdown from "Jan" back to "January", undoing the short labels from fix(calendar): format month dropdown with the DayPicker locale #890. Passing formatMonthDropdown: undefined with a dropdown caption throws TypeError: formatters.formatMonthDropdown is not a function.

Reproduction

<Calendar
  mode="single"
  selected={new Date(2026, 9, 14)}
  classNames={{ weekdays: "bg-amber-100", selected: "ring-2 ring-red-600", month_grid: "outline-dashed" }}
/>
// main: none of the three classes reach the DOM

<Calendar
  captionLayout="dropdown"
  formatters={{ formatWeekdayName: (d) => ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"][d.getDay()] }}
/>
// main: month options read "January, February, …" instead of "Jan, Feb, …"

Or run the new tests against main's calendar.tsx: bun test packages/ui/test/components/calendar.test.tsx → 4 of 8 fail.

Root cause

Object.keys(defaultClassNames).reduce(…, { ...defaultClassNames }) // consumer-only keys are never copied
formatters: { formatMonthDropdown }, ...props                        // props.formatters replaces the whole object

Fix

  • Seed the existing classNames reduce with the consumer's classes, so parts without a coss default keep them. Parts coss styles still merge through cn, unchanged.
  • Destructure formatters and spread it over the default, so an unrelated formatter keeps formatMonthDropdown and a consumer formatMonthDropdown still wins.
  • Treat a key set to undefined as not provided (small local withoutUndefined helper). Without it, classNames={{ weekdays: undefined }} would clear DayPicker's own rdp-weekdays class, which main keeps, and an undefined formatter would still throw.

Changed: both calendar.tsx copies (via ui:sync), public/r/calendar.json (via registry:build) and a new test file. registry:build also rewrites ~30 unrelated particle JSON files that have already drifted on main; they are left out, as in #890. No new dependencies, no API or docs changes.

Tests

New packages/ui/test/components/calendar.test.tsx (8 tests, same bun:test + dynamic-import style as the existing component tests):

Test main This branch
keeps classes for parts that have no coss default fail pass
keeps custom classes in range mode fail pass
keeps short month labels when another formatter is passed fail pass
uses the default when formatters or one formatter is undefined fail (throws) pass
merges custom classes with coss defaults of styled parts pass pass
lets a conflicting utility override the default (text-lg over text-sm) pass pass
uses the defaults for undefined, {} and undefined values pass pass
lets the consumer replace the default month formatter pass pass

The last four guard existing behaviour. Other suites mock cn as a plain join and Bun module mocks persist across files, so this file restores the real clsx + tailwind-merge cn for itself; it passes in 78 randomized file orders.

CI commands run locally: bun install --frozen-lockfile, bunx biome check --linter-enabled=false ., bun run lint, bun run typecheck and bun run test (49 pass, 0 fail) all pass, as do turbo run build --filter=ui and registry:validate-deps. The full bun run build did not complete locally: the origin app failed fetching Google Fonts in a network-restricted environment, so www and the examples app were not built; CI will cover them.

Compatibility

  • All 34 existing Calendar and Date Picker particles render byte-identical markup on main and this branch, as do default, dropdown-caption and range calendars.
  • Intended change: classes consumers already pass for previously ignored parts now apply. As with DayPicker itself, a class for an unstyled part replaces DayPicker's own rdp-* class for that part.

Visual evidence

Rendered in Chromium with coss globals.css compiled by Tailwind v4: classes on weekdays, selected and month_grid (as above, with a ring offset and outline colour for visibility), plus formatWeekdayName and a dropdown caption:
calendar-before-after

AI assistance

Investigated, implemented and tested with Claude Code (Claude Opus 5.5); I reviewed the change and the evidence. The commit carries a Co-Authored-By trailer.

Calendar built its classNames from its own default keys only, so a class
for any DayPicker part without a coss default (root, weekdays, selected,
month_grid, ...) was silently dropped. Passing any formatters also
replaced the default formatMonthDropdown, so the month dropdown switched
from short to long names.

Start the classNames merge from the consumer's classes and spread
consumer formatters over the default. Parts coss styles merge exactly as
before; a key set to undefined counts as not provided.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011axzxSXXTqZnzgb8feHHdD
@vercel

vercel Bot commented Oct 19, 2026

Copy link
Copy Markdown

@rashmi09-sys is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@vercel
vercel Bot temporarily deployed to Preview – coss-com-origin October 19, 2026 09:08 Inactive
@vercel

vercel Bot commented Oct 19, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated
coss-com-origin Skipped Skipped Oct 19, 2026 9:08am UTC

Request Review

This branch was previously deployed

1 inactive deployment
Preview – coss-com-origin — d5d908ae Deployed Oct 19, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant