UI: Add Calendar and RangeCalendar, moved from components private APIs - #81337
UI: Add Calendar and RangeCalendar, moved from components private APIs#81337youknowriad wants to merge 2 commits into
Calendar and RangeCalendar, moved from components private APIs#81337Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
5574710 to
8c559f2
Compare
|
Size Change: +81.2 kB (+1.04%) Total Size: 7.9 MB 📦 View Changed
|
I'll create an issue for that, thanks! It's a super weird coincidence because @mcsf pinged just a bit earlier than your comment noticing your UI issue, which seems to be for Safari. |
Moves the private `DateCalendar` / `DateRangeCalendar` out of `@wordpress/components` and into `@wordpress/ui` as the public `Calendar` and `RangeCalendar`, rewritten to follow that package's conventions. DataViews was their only consumer, and it is a bundled package: unlocking a `@wordpress/components` private API from it crashes at module-eval time when two copies of `@wordpress/private-apis` exist in one runtime. The calendars were blocked on "still stabilizing in `@wordpress/components`", but `@wordpress/ui` is 0.x and documents itself as experimental, so it can absorb a component that is still settling. - Styles move from SCSS with `$components-color-*` to a CSS module on `--wpds-*` tokens, in the `wp-ui` cascade layers. The class map is passed to `react-day-picker`'s `classNames`, so the calendar can no longer be double-styled by a stale `wp-components` stylesheet. - Month navigation renders `Button`, chevrons render `Icon` with `@wordpress/icons`, via `react-day-picker`'s `components` overrides. - The root supports the `render` prop and forwards refs, like every other `@wordpress/ui` component. - `react-day-picker` moves from `@wordpress/components` to `@wordpress/ui`. - `TZDate` is no longer re-exported; `@wordpress/ui` exports components only. Consumers use `@date-fns/tz`. It had no production usage. Part of #81230: the `date` control no longer unlocks anything, and `datetime` keeps a single unlock for `ValidatedInputControl`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8c559f2 to
aee49c3
Compare
| ## Accessibility | ||
|
|
||
| - The calendar renders with `role="application"` by default. Set `role="dialog"` when it is presented inside a dialog. | ||
| - Use `autoFocus` to move focus to the selected day (or today) after a user action, such as opening a popover that contains the calendar. |
There was a problem hiding this comment.
This line might have been sufficient for a prop description, but reads a bit too broad ("after a user action") for an accessibility best practice section. @ciampo I feel like the only time autofocus is allowed is when a popover first opens where the main purpose is to show the calendar? Anything else?
- Fix the `render` re-render test, which asserted nothing: clicking a button with no handler never re-rendered the wrapper, so the identity check passed trivially. Use RTL's `rerender` with a fresh `render` element instead. - Mark both components `recommended` rather than `use-with-caution`; there are no problematic changes planned, which is the bar for that status. - Drop the Accessibility section from the Best Practices page. The `role="dialog"` advice is an oversimplification that is harmful if followed naively, and the remaining items are implementation notes already covered by the prop documentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Part of #81230. Alternative to #81324.
What?
Moves the private
DateCalendarandDateRangeCalendarout of@wordpress/componentsand into@wordpress/uias the publicCalendarandRangeCalendar, rewritten to follow that package's conventions: design tokens, CSS modules,renderprop support,@wordpress/uiprimitives for the month navigation, and Storybook docs.The DataViews
dateanddatetimecontrols import them from@wordpress/ui, so thedatecontrol no longer unlocks anything at all anddatetimekeeps a single unlock forValidatedInputControl.Why?
#81324 proposes vendoring the calendars into
@wordpress/dataviewsinstead. That unblocks the private-API removal, but as noted in the issue it comes with a hard rule that DataViews may never export them — so the next consumer copies the code again, and the code never improves.The blocker was "still stabilizing in
@wordpress/components". That is a constraint of the components package's rules —window.wpexposure and long-term backward compatibility — not of@wordpress/ui, which is0.xand documents itself as experimental: "an early implementation subject to drastic and breaking changes". If@wordpress/uican't absorb a component that has shipped and been in use for a year, the 0.x posture isn't buying anything.The rewrite is also smaller than it looks. The only coupling to
@wordpress/componentswasuseControlledValue(52 lines) —react-day-pickerowns the entire DOM, so there were no components-package components to swap out.How?
$components-color-*andbase-stylesvariables are replaced with--wpds-*. Most mappings are value-exact ($grid-unit-40→--wpds-dimension-size-md= 32px,$radius-small→--wpds-border-radius-sm= 2px,$font-size-medium→--wpds-typography-font-size-md= 13px,gray-700→--wpds-color-foreground-content-neutral-weak=#707070).style.module.cssin thewp-uicascade layers, and the class map is handed toreact-day-picker'sclassNamesprop. This is less code than the global BEM strings it replaces, and it structurally removes the double-styling hazard that DataViews: VendorDateCalendarandDateRangeCalendarfrom components #81324 works around by renaming the class prefix.@wordpress/uiprimitives. The month navigation buttons renderButton(minimal/neutral/compact) and the chevrons renderIconwith@wordpress/icons, throughreact-day-picker'scomponentsoverrides. This deletes ~30 lines of CSS and picks up DS hover / active /data-disabledstyling.renderprop + ref forwarding. The root goes throughuseRender, wired via a context so thecomponentsobject stays referentially stable (a new component type would remount the calendar and drop focus on every render).react-day-pickermoves from@wordpress/componentsto@wordpress/ui, which also declaresdate-fnsdirectly.TZDateis no longer re-exported —@wordpress/uiexports components and nothing else. Consumers use@date-fns/tz. It had no production usage; only stories, tests and READMEs referenced it.Best PracticesMDX page; the prop tables are now JSDoc rendered by Storybook's ArgTypes.Intentional visual changes
Three deltas come from mapping to semantically correct tokens rather than preserving computed values:
#1e1e1e→#2d2d2d, hover#1e1e1e. On trunk the default and hover backgrounds were both#1e1e1e, so the hover state was a no-op;--wpds-color-background-interactive-neutral-strong{,-active}fixes that.#fcfcfctext on a#8d8d8dbackground; now uses the design system's disabled pairing,#8d8d8don#e6e6e6.font-weighton the weekday cells, so the browser default forth(bold) applied even though the calendar root already sets the default weight. Spotted by @mirka in review, who reads it as a bug fix — agreed.Points for reviewers
@wordpress/ui. Base UI 1.6 has no calendar or date-picker primitive, soreact-day-picker(which bringsdate-fns,@date-fns/tz,date-fns-jalali,@tabby_ai/hijri-converter) is the only option. This is the main decision to sign off on.use-recommended-componentsallowlist.CalendarandRangeCalendarare added, otherwise the DataViews import fails lint. That list is owned by the components team.Calendar/RangeCalendarfollows React Aria and matches theButton/IconButtonsibling-export pattern in this package. Easy to change ifDateCalendar/DateRangeCalendaris preferred.use-with-cautionin Storybook, with a note that the design and prop surface may still move — the "some components are more early than others" idea from the issue discussion, made explicit.Testing Instructions
Unit tests:
npm run test:unit packages/ui/src/calendar— 127 tests (115 ported, 12 new forrender/ ref forwarding).npm run test:unit packages/ui packages/dataviews packages/components— all green.npm run build,npm run lint:js,npm run lint:css.In the editor — the
datetimecontrol rendersCalendar:RangeCalendarhas no surface in the stock editor: every date field Gutenberg registers istype: 'datetime', and onlytype: 'date'offers thebetweenoperator. To exercise it, use Storybook → DataViews/FieldTypes → DateComponent → Add filter → Date → Between, which renders the range calendar with the hover preview.Also worth checking in Storybook → Design System/Components/Calendar and RangeCalendar: the RTL toggle (layout and chevrons should flip), and the WordPress global CSS toggle (appearance should be unchanged).
Screenshots or screencast
Use of AI Tools
This PR, including this description, was written by Claude Code (Claude Opus 5), based on the discussion in #81230, and human-reviewed before submission.
🤖 Generated with Claude Code