UI Dialog: Add Description, modal context, and misc improvements#77194
Conversation
- Add useDeprioritizedInitialFocus to Drawer.Popup so the close icon is not the first focused element when the drawer opens - Add data-wp-ui-drawer-close-icon attribute to Drawer.CloseIcon - Add container prop to Drawer.Popup and Dialog.Popup for cross-document portal rendering - Align Drawer.Action disabled/loading handling with Dialog.Action - Revert Card.Title changes (moved to #77187) - Extract Dialog improvements to separate PR (#77194) - Fix CHANGELOG placeholder PR number Made-with: Cursor
|
Size Change: 0 B Total Size: 7.75 MB ℹ️ View Unchanged
|
|
Flaky tests detected in 5889c59. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/24716531148
|
18f1122 to
ec51c53
Compare
f514bc6 to
c95cf84
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
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. |
7e9ca3f to
6ba2036
Compare
- Add Dialog.Description sub-component wrapping Base UI's Dialog.Description with the Text component (variant="body-md") - Add DialogModalContext so Popup can conditionally render the backdrop only when modal === true (previously always rendered) - Extract --viewport-inset CSS variable for consistent viewport padding - Add onOpenChangeComplete to RootProps - Move component description from Storybook params to Root JSDoc Split from #76690. Made-with: Cursor
- Dialog.Title: accept and merge `className` prop, matching Drawer.Title - Popup: reorder prop destructuring (`children` near the top) to match Drawer.Popup - Popup: document why the backdrop is only rendered for fully modal dialogs - Default story: showcase the new Dialog.Description sub-component - Types: elaborate the `container` prop JSDoc (cross-document use case) - Description: align the JSDoc phrasing with the sibling components - Context: move `contextValue` memo before the initial-validation `useEffect` to match Drawer.context - Tests: cover the conditional backdrop (modal vs non-modal vs trap-focus), Dialog.Action `disabled`/`loading` precedence, Dialog.Footer `render`/`className` support, and Dialog.Popup size values (default + small / medium / large / stretch / full) - CHANGELOG: add entry for the Dialog improvements Made-with: Cursor
Storybook infers the type summary and default from the TypeScript prop type, so the manual `table` override was redundant. Made-with: Cursor
Spell out the accepted value shapes and the default, matching the more explicit documentation style used for the `size` prop. Made-with: Cursor
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Spell out in `Description`'s JSDoc that the rendered element is linked to the popup via `aria-describedby` (matching the phrasing used on `Popover.Description`), and add a test that verifies the association at runtime via `toHaveAccessibleDescription`. Made-with: Cursor
`100dvh` tracks the dynamic viewport (excluding mobile browser chrome when it is hidden), so the popup's max-height and the `is-full` full-height variant are both bounded by the actually available height instead of the larger initial viewport. Made-with: Cursor
Query the backdrop in the test suite via a stable `data-wp-ui-dialog-backdrop` attribute on the backdrop element, mirroring the existing `data-wp-ui-dialog-close-icon` pattern, rather than a Base UI `role="presentation"` + `data-open` query that is shared with other Base UI primitives. Made-with: Cursor
The `body-md` text variant is an implementation detail that doesn't affect how consumers use `Dialog.Description`. Made-with: Cursor
Use the underscore-prefixed internal naming convention already established for `--_gcd-heading-margin` in the same file, so the variable doesn't accidentally look like a public theming knob alongside `--wp-ui-dialog-z-index`. Made-with: Cursor
`Root` now applies the `modal = true` default and passes the resolved value to both `_Dialog.Root` and `DialogModalProvider`, making `DialogModalProvider`'s `modal` a required prop. This removes a duplicated default that could silently drift from Base UI's own default if it ever changes. Made-with: Cursor
`Dialog.Description` now wraps `_Dialog.Description` inside `Text` (rather than the inverse), matching the `Popover.Description` structure and keeping sibling `Description` sub-components in sync. The JSDoc is also aligned with `Popover.Description`'s phrasing. Made-with: Cursor
Align with `Dialog.Description`: the `body-md` text variant is an implementation detail that doesn't affect how consumers use `Popover.Description`. Made-with: Cursor
Cover the remaining user-visible changes shipped in this PR: - `100vh` → `100dvh` for viewport-based popup heights (fits mobile dynamic viewport while browser UI shows/hides). - `Dialog.Title` now accepts and forwards a `className` prop (matches `Drawer.Title`). Made-with: Cursor
4579cd5 to
5889c59
Compare
Re-uses the Portal subcomponent and `portal` prop pattern, the modal default consolidation on `Root`, the `--_viewport-inset` underscore convention, the backdrop data attribute, and the `Description` wrapping shape adopted in #77194. Also fixes `Drawer.Title` to forward a custom `className` alongside the internal one. Made-with: Cursor
Re-uses the Portal subcomponent and `portal` prop pattern, the modal default consolidation on `Root`, the `--_viewport-inset` underscore convention, the backdrop data attribute, and the `Description` wrapping shape adopted in #77194. Also fixes `Drawer.Title` to forward a custom `className` alongside the internal one. Made-with: Cursor
* UI: Add Drawer primitive and align popup behavior Add the Drawer primitive with footer composition, deterministic size handling, safe-area spacing updates, and refreshed stories/tests to align behavior and docs with the current Dialog patterns. Made-with: Cursor * UI: Add changelog entry for Drawer primitive Record the new Drawer primitive in the UI package changelog. Made-with: Cursor * UI Drawer: Align with Dialog and Popover after #77194 Re-uses the Portal subcomponent and `portal` prop pattern, the modal default consolidation on `Root`, the `--_viewport-inset` underscore convention, the backdrop data attribute, and the `Description` wrapping shape adopted in #77194. Also fixes `Drawer.Title` to forward a custom `className` alongside the internal one. Made-with: Cursor * UI: Let `useRender` merge `Title`/`Description` class names Removes the explicit `clsx( styles.x, className )` in `Title` and `Description` for `Drawer`, `Dialog`, and `Popover`. The user-provided `className` now flows through `...props` onto the Base UI render element (`_Dialog.Title`, `_Popover.Description`, etc.), and Base UI's `useRender`/`mergeProps` merges it with the internal module class. Made-with: Cursor * Drawer: Document swipeDirection on RootProps Override Base UI wording so WPDS docs cover slide-in edge and swipe-to-dismiss. Made-with: Cursor * Drawer: Tidy Storybook stories Remove MobileNavigation and ActionSheet examples that read as production patterns. Use args.children for NonModal, inline named render functions where hooks are needed, and type ALL_SIZES from Drawer.Popup size to avoid drift. Made-with: Cursor * Drawer: Scope motion layers and horizontal safe-area Scope will-change and transform transitions to data-open and data-swiping so compositing is not retained while idle. Add safe-area padding on left and right drawer content to match top and bottom handling. Made-with: Cursor * UI: Share overlay modal and title validation contexts Add createOverlayModalContext and createOverlayTitleValidation utilities and wire Drawer, Dialog, and Popover context modules through them to remove duplication while preserving dev-only title checks and error messages. Made-with: Cursor * Drawer: Make Storybook renders args-friendly Use args-forwarding render functions where needed, remove redundant default args, and disable conflicting controls on stories with internal state. Made-with: Cursor * Drawer, Dialog: Default Header/Footer to landmark elements Render `Drawer.Header` / `Drawer.Footer` and `Dialog.Header` / `Dialog.Footer` as native `<header>` / `<footer>` landmarks by default so assistive technology can jump straight to them, useful when a surface contains many interactive elements before the action buttons. Consumers can still opt out via `render`. Made-with: Cursor * Drawer, Dialog: Drop the underscore prefix from --viewport-inset Match the non-underscored convention used by sibling custom properties (`--backdrop-opacity`, `--popup-size`). No behavior change. Made-with: Cursor * Drawer: Relax content safe-area selectors to descendant Switch the `.popup[data-swipe-direction="…"] > .content` rules to a descendant combinator. There is no expectation of nested `Drawer.Content` and the looser selector keeps the safe-area padding working through any intermediate wrappers. Made-with: Cursor * Drawer: Document the backdrop transition divergence from Dialog The Drawer backdrop runs a longer duration and different easing than Dialog's because it needs to match the popup's slide-in/out animation so both surfaces transition together. Add a comment explaining the intent so it is not mistaken for drift. Made-with: Cursor * Drawer, Dialog: Use data-testid to target the backdrop Replace the custom `data-wp-ui-drawer-backdrop` / `data-wp-ui-dialog-backdrop` attributes with `data-testid` so they are clearly test-only hooks rather than looking like generic styling or behavior attributes. Update the corresponding tests to query the backdrops via `queryAllByTestId`. Made-with: Cursor * Drawer, Dialog, Popover: Expand test coverage per review - Drawer: add a Header render/className parity test alongside the existing Footer one so regressions in the shared `useRender` pattern are caught. - Drawer: assert `data-swipe-direction` on the popup while rerendering across swipe directions so a wiring break cannot pass silently. - Drawer: add `initialFocus={ false }` and custom-callback focus tests, matching Dialog/Popover, to document the Drawer's own viewport wrapper on top of the shared hook. - Drawer: add an Escape-to-close smoke test that also asserts focus restoration to the trigger, documenting the keyboard contract. - Dialog, Popover: add a class-name-merge regression test for the shared `useRender` behavior applied to Title/Description. Made-with: Cursor --- Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: mirka <0mirka00@git.wordpress.org> Co-authored-by: vk17-starlord <vineet2003@git.wordpress.org> Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org> Co-authored-by: jameskoster <jameskoster@git.wordpress.org>

What?
Split from #76690 (Drawer primitive PR).
Adds several improvements to the
Dialogcomponent that were originally bundled with the Drawer PR, plus broader alignment with the Drawer's conventions and additional test coverage.Why?
These changes are independent of the Drawer implementation and are better reviewed and merged separately to keep the Drawer PR focused.
How?
New sub-component:
Dialog.DescriptionDialog.Descriptionwith theTextcomponent (variant="body-md"), matching thePopover.Descriptionwrapping shapeDescriptionPropstype, barrel export, CSS (.descriptionrule), story subcomponent entry, and ref-forwarding testDialog.Descriptionso the new sub-component is visible from the main storyaria-describedbyassociation, and a test verifies the association at runtime viatoHaveAccessibleDescriptionModal context + conditional backdrop
DialogModalContext/DialogModalProvider/useDialogModaltocontext.tsxDialog.Rootapplies themodal = truedefault once and forwards the resolved value to both_Dialog.RootandDialogModalProvider, soPopupcan read it via context<Dialog.Backdrop>conditional onmodal === true(previously always rendered). Non-modal dialogs shouldn't dim the page, andtrap-focuskeeps outside pointer interactions enabled, so a backdrop would misrepresent either modedata-wp-ui-dialog-backdropattribute (mirroring the existingdata-wp-ui-dialog-close-icon), so tests can query it without relying on Base UI internalsmodal={ true },modal={ false }, andmodal="trap-focus"CSS refactor
--wpds-dimension-padding-2xlused inwidthandmax-heightcalculations into a--_viewport-insetinternal CSS custom property (using the existing underscore-prefixed internal naming convention), matching the Drawer's approach100vhfor100dvhin the popup'smax-heightand theis-fullfull-height variant so the popup is bounded by the dynamic viewport (avoids overflow under mobile browser chrome)Type + docs
onOpenChangeCompletetoDialog.RootPropsPopupProps.container(mentions the cross-document / iframe use case, matchingDrawer)parameters.docs.description.componentto a JSDoc block on theRootfunctionDialog.Description's JSDoc phrasing with the sibling components (including the explicitaria-describedbynote), and drops the matchingbody-mdnote fromPopover.Description's JSDoc for consistencyAlignment with Drawer + additional coverage
Dialog.Title: accepts and merges aclassNameprop (matchesDrawer.Title)Dialog.Popup: prop destructuring order matchesDrawer.Popup(childrennear the top)Dialog.context:contextValuememo now lives next to the rest of the memoized data, matchingDrawer.contextDialog.Description'saria-describedbywiring, conditional backdrop rendering acrossmodalvalues,Dialog.Action'sdisabled/loadingprecedence,Dialog.Footer'srender/classNamesupport, andDialog.Popupacross all size values (default +small,medium,large,stretch,full) — none of these were previously exercisedTesting Instructions
npm run storybook:devDialog.Descriptionin the default storymodal={ false }(andmodal="trap-focus") no longer render a backdrop overlaynpm run test:unit packages/ui/src/dialog— all 22 tests should passFollow-ups
data-wp-ui-*-backdroptest hook toPopoverandAlertDialogso their backdrops can be queried without relying on Base UI internals (separate PR).Dialog.Popupis rendered without aDialog.Rootancestor, to prevent floating backdrops in misuse scenarios.Use of AI Tools
Cursor + Claude Opus 4.6 and Claude Opus 4.7
Made with Cursor