Files
skills/docs/ui/react/react-ui/src/components/calendar/calendar.md
2026-05-29 08:20:56 +01:00

7.5 KiB

Calendar

(no submodules)

- calendar.test-fixtures — Wave-3h local test fixture…
  • calendar.test-fixtures — Wave-3h local test fixture that augments the chassis test i18n instance with the react-ui.calendar.* keyset. The chas…

  • calendar.variants — calendarVariants is the cva config that powers the outer wrapper of <Calendar>. The recipe is intentionally minimal…

  • calendar.test-fixtures — Wave-3h local test fixture…

  • calendar.test-fixtures — Wave-3h local test fixture that augments the chassis test i18n instance with the react-ui.calendar.* keyset. The chas…

  • calendar.variants — calendarVariants is the cva config that powers the outer wrapper of <Calendar>. The recipe is intentionally minimal…

  • calendar.test-fixtures — Wave-3h local test fixture…

  • calendar.test-fixtures — Wave-3h local test fixture that augments the chassis test i18n instance with the react-ui.calendar.* keyset. The chas…

  • calendar.variants — calendarVariants is the cva config that powers the outer wrapper of <Calendar>. The recipe is intentionally minimal…

  • calendar.test-fixtures — Wave-3h local test fixture…

  • calendar.test-fixtures — Wave-3h local test fixture that augments the chassis test i18n instance with the react-ui.calendar.* keyset. The chas…

  • calendar.variants — calendarVariants is the cva config that powers the outer wrapper of <Calendar>. The recipe is intentionally minimal…

  • calendar.test-fixtures — Wave-3h local test fixture that augments the chassis test i18n instance with the react-ui.calendar.* keyset. The chas…

  • calendar.variants — calendarVariants is the cva config that powers the outer wrapper of <Calendar>. The recipe is intentionally minimal…

Purpose

<Calendar> is the chassis date-grid primitive — a thin wrapper over react-day-picker (v9) that adds the chassis cva recipe, theme-aware lucide chevrons for previous/next-month navigation, locale-driven aria-label overrides, and a labelled outer wrapper. The chassis derives the active locale from useTranslation() so date formatting and accessible labels stay in lock-step with the rest of the app surface.

The chassis exposes the upstream surface verbatim through Omit<DayPickerProps, "className" | "locale"> — every supported mode (single, multiple, range), the selected / onSelect controlled API, numberOfMonths, showOutsideDays, weekStartsOn, disabled, fromMonth/toMonth, the modifiers system, and the rest of the upstream API documented at https://daypicker.dev. Two upstream props are intercepted: className (so it merges with the chassis recipe via cn()) and locale (so the chassis can drive it from useI18n() rather than the consumer wiring it manually). For the rare case where a single calendar must diverge from the active app locale, pass a localeOverride (a Partial<DayPickerLocale> per the upstream API).

Features

  • Wraps react-day-picker v9 with zero additional state — fully controlled or uncontrolled per the upstream API.
  • Three modes — single / multiple / range — selected via the upstream mode prop.
  • Locale-aware: the chassis reads the active locale from useTranslation() so the calendar stays in sync with the app surface.
  • aria-label on the outer wrapper resolves through the active i18n via labelKey (defaults to react-ui.calendar.label).
  • Previous / next month navigation buttons get translated aria-labels via previousMonthKey / nextMonthKey.
  • Lucide-iconography for navigation chevrons (ChevronLeftIcon / ChevronRightIcon) wired into react-day-picker's components.Chevron slot.
  • Forwards refs to the outer wrapper <div>.
  • All other upstream props pass through unchanged.

Usage: Basic Example

import { Calendar } from "@suaveplan/react-ui";
import { useState } from "react";

export function ScheduleCalendar() {
    const [date, setDate] = useState<Date | undefined>(new Date());
    return <Calendar mode="single" selected={date} onSelect={setDate} />;
}

Usage: Advanced Example

import { Calendar } from "@suaveplan/react-ui";
import type { DateRange } from "react-day-picker";
import { useState } from "react";

export function BookingCalendar() {
    const [range, setRange] = useState<DateRange | undefined>();
    const today = new Date();
    return (
        <Calendar
            mode="range"
            selected={range}
            onSelect={setRange}
            numberOfMonths={2}
            showOutsideDays
            disabled={{ before: today }}
            weekStartsOn={1}
            previousMonthKey="react-ui.calendar.previousMonth"
            nextMonthKey="react-ui.calendar.nextMonth"
        />
    );
}

API Reference

<Calendar>

forwardRef<HTMLDivElement, CalendarProps> — renders an outer wrapper <div role="group" aria-label={…}> containing the <DayPicker> instance.

CalendarProps

Prop Type Default Description
mode "single" | "multiple" | "range" undefined Selection mode — drives the selected / onSelect shape per the upstream API.
labelKey TranslationKey "react-ui.calendar.label" aria-label for the outer wrapper.
previousMonthKey TranslationKey "react-ui.calendar.previousMonth" aria-label for the previous-month navigation button.
nextMonthKey TranslationKey "react-ui.calendar.nextMonth" aria-label for the next-month navigation button.
localeOverride Partial<DayPickerLocale> undefined Override the date-fns locale for this calendar instance. When unset, react-day-picker falls back to its bundled enUS.
every other DayPickerProps field upstream upstream Forwarded as-is. See https://daypicker.dev.

Implementation Notes

  • The chassis intentionally does NOT expose the upstream locale prop directly — localeOverride is a renamed escape hatch that documents the chassis's intent to drive locale from useTranslation() by default.
  • react-day-picker ships its own classnames for the grid, day buttons, and navigation chrome via its internal classNames map. The chassis only scopes the OUTER container — overriding the inner classnames belongs in a theme variant override.
  • Keyboard navigation (arrow keys, Home/End, Enter/Space, PageUp/Down) is supplied entirely by react-day-picker — the chassis does not wrap or intercept it.
  • The chevron icons are passed via components.Chevron rather than classNames so the chassis can swap them for theme-provided lucide / radix icons in a future Wave 5d theme-icon-pack pass.
  • react-day-picker renders its day grid as either a <table> (default) or a [role="grid"] element depending on the upstream version — the chassis does not assume one or the other in tests, hitting both via the grid selector.
  • When migrating from a previous <Calendar> consumer (e.g. an upstream shadcn block), the only required change is to drop the locale prop — the chassis derives it automatically.
  • Per the brief reconciliation note: the chassis casts the unreachable ?? "default" fallback for (i18n as { readonly locale: string }).locale is NOT needed here because the locale is read indirectly via useTranslation() rather than from i18n.locale.