[ui] Add internal wp compat overlay slot helper#77851
Conversation
f04adc4 to
c8f1ac9
Compare
|
Size Change: +4 B (0%) Total Size: 7.93 MB 📦 View Changed
ℹ️ View Unchanged
|
6dcdcb3 to
b9db3e2
Compare
a9e8ace to
e5b587f
Compare
|
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. |
mirka
left a comment
There was a problem hiding this comment.
Excited to start testing this out!
One thing about the name. "Prime slot" was just a code name that I used randomly. In the context of our package, maybe we just call it the "wp compat overlay slot"? Or maybe even just "wp overlay slot". Any other ideas? I feel like "wp" needs to be the operative word, since the slot is specifically a WP concern.
e5b587f to
849664a
Compare
|
@mirka should have addressed all feedback |
|
Also opened a small follow-up to test the slot: #78095 (Tooltip portals through the slot when the gate is open) and the Storybook setup |
|
Flaky tests detected in 137688e. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/25735856136
|
849664a to
a53ea9c
Compare
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. Cross-package wiring goes through `@wordpress/private-apis` (both packages are on the allow-list): `@wordpress/ui` locks `getWpCompatOverlaySlot()` onto its `privateApis` export; `@wordpress/components` unlocks it from `Draggable`. The `@wordpress/ui` dep on `@wordpress/components` is transitional, scoped to the legacy-overlay migration. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
Public-facing comments shouldn't reference the underlying overlay
library by name. Rewrites the four references in the helper, hook, and
CSS module to describe the relevant behavior in library-agnostic terms
("the default portal container", "overlay positioners in this
package", etc.). The technical claims are unchanged — only the wording
that named the upstream library.
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. Cross-package wiring goes through `@wordpress/private-apis` (both packages are on the allow-list): `@wordpress/ui` locks `getWpCompatOverlaySlot()` onto its `privateApis` export; `@wordpress/components` unlocks it from `Draggable`. The `@wordpress/ui` dep on `@wordpress/components` is transitional, scoped to the legacy-overlay migration. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
Per review feedback that flagged overexplanation and drift-prone specifics in code comments: collapse the internal type, helper, function, and module-level docblocks to the load-bearing technical content only. Inline comments in `getWpCompatOverlaySlot()` shrink to 2–3 lines. CSS header keeps the unlayered/`position: fixed`/physical- anchoring rationale (load-bearing, subtle) and trims the z-index prose around the literal value. No functional change; tests unchanged and passing.
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. Cross-package wiring goes through `@wordpress/private-apis` (both packages are on the allow-list): `@wordpress/ui` locks `getWpCompatOverlaySlot()` onto its `privateApis` export; `@wordpress/components` unlocks it from `Draggable`. The `@wordpress/ui` dep on `@wordpress/components` is transitional, scoped to the legacy-overlay migration. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. Cross-package wiring goes through `@wordpress/private-apis` (both packages are on the allow-list): `@wordpress/ui` locks `getWpCompatOverlaySlot()` onto its `privateApis` export; `@wordpress/components` unlocks it from `Draggable`. The `@wordpress/ui` dep on `@wordpress/components` is transitional, scoped to the legacy-overlay migration. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. Cross-package wiring goes through `@wordpress/private-apis` (both packages are on the allow-list): `@wordpress/ui` locks `getWpCompatOverlaySlot()` onto its `privateApis` export; `@wordpress/components` unlocks it from `Draggable`. The `@wordpress/ui` dep on `@wordpress/components` is transitional, scoped to the legacy-overlay migration. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. Cross-package wiring goes through `@wordpress/private-apis` (both packages are on the allow-list): `@wordpress/ui` locks `getWpCompatOverlaySlot()` onto its `privateApis` export; `@wordpress/components` unlocks it from `Draggable`. The `@wordpress/ui` dep on `@wordpress/components` is transitional, scoped to the legacy-overlay migration. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it).
…78095) * Tooltip: Default the portal container to the wp compat overlay slot Wire `Tooltip.Portal` to default its `container` prop to the `@wordpress/ui` compat overlay slot (when the runtime opts in via WordPress detection or the explicit window flag). A caller-supplied `container` prop continues to take precedence (props are spread after the default). Adds three integration tests in `packages/ui/src/tooltip/test` covering: popup is portaled into the slot when the gate is open; slot is not created when the gate is closed (dormant default); and caller-supplied containers override the slot default. Adds a Storybook decorator (`WithWpCompatOverlaySlot`) that opts a single story into the slot and cleans up on unmount, plus demonstration stories: per-component (`Inside WP Compat Overlay Slot`) and cross-library (`@wordpress/ui` Tooltip inside `@wordpress/components` Modal/Popover) under `Playground/WP Compat Overlay Slot`. This is a sample wiring of the prime-slot infrastructure for one leaf overlay; it doubles as the end-to-end stacking validator for the helper introduced upstream. * Move WithWpCompatOverlaySlot decorator into @wordpress/ui The decorator previously lived in `storybook/decorators/`, but per-component stories under `packages/ui/src/tooltip/stories/` imported it from there — a `packages/ui` → `storybook/` cross-package boundary violation. Move it to `packages/ui/src/utils/with-wp-compat-overlay-slot.tsx`, co-located with the helper it opts into. The Tooltip story now imports it as a same-package file. The cross-library demo under `storybook/stories/playground/` imports it via `packages/ui/src/...` which is the normal upper-level → package import direction. Drive-by: now imports `WP_COMPAT_OVERLAY_SLOT_ATTRIBUTE` from the helper module instead of hardcoding the data attribute string. * [ui] README: document useEnableWpCompatOverlaySlot() The hook landed in #77851 alongside the dormant compat overlay slot infrastructure, and goes live for end users once Tooltip starts portaling into the slot (this PR). README documentation has been intentionally deferred to this PR so the public-API copy lands alongside a real consumer. Adds a "Mixing with @wordpress/components" subsection under "Setup -> Elsewhere" — the only place a host actually needs to call the hook (standard WordPress editor screens auto-enable the slot via the window.wp.components global and don't need it). * [ui] Tooltip / decorator / playground: remove Base UI mentions Drops references to the underlying overlay library by name from the three docblocks/header comments added in this PR. Rewords each to describe the relevant behavior in library-agnostic terms ("the default portal container", "Tooltip.Portal", "the default portal"). No code changes. * Co-locate WithWpCompatOverlaySlot decorator with playground stories Moves the decorator out of `@wordpress/ui` (where it had ended up to avoid a `packages/ui` → `storybook/` cross-boundary dependency for the per-component Tooltip story) and into the same directory as the only remaining consumer, the cross-library Playground story. The dedicated `Inside WP Compat Overlay Slot` Tooltip story is removed along with the move. The decorator mutates a window-level flag that's process-wide across the Storybook preview iframe, so applying it to per-component stories made the opt-in leak into every sibling story rendered on the same autodocs page. Keeping the decorator next to the single playground story that needs it confines the leak blast radius to the playground; cross-library stacking remains the end-to-end validator for the slot. * [ui] Portal/Positioner: Harmonize JSDoc across overlay primitives Tighten and align the JSDoc on `Portal` and `Positioner` subcomponents across `Tooltip`, `Popover`, `Select`, `Autocomplete`, `Drawer`, `Dialog`, and `AlertDialog` to a single-sentence "Used to apply custom <feature> to <Component>'s <content>" cadence. The prop listings and "Pass to X.Popup's `portal`/`positioner` prop" boilerplate are documented by the TypeScript types — the docblock no longer duplicates them. --- Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: mirka <0mirka00@git.wordpress.org>
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. `@wordpress/components` imports `getWpCompatOverlaySlot()` directly from `@wordpress/ui`'s public exports (also promoted from internal in this change). The `@wordpress/components` dep on `@wordpress/ui` is transitional, scoped to the legacy-overlay migration. Cross-document drags (e.g. dragging an element inside an iframe while the slot is in the parent document) fall back to the previous placement so the clone's viewport-relative geometry stays in a single coordinate space. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. `@wordpress/components` imports `getWpCompatOverlaySlot()` directly from `@wordpress/ui`'s public exports (also promoted from internal in this change). The `@wordpress/components` dep on `@wordpress/ui` is transitional, scoped to the legacy-overlay migration. Cross-document drags (e.g. dragging an element inside an iframe while the slot is in the parent document) fall back to the previous placement so the clone's viewport-relative geometry stays in a single coordinate space. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. `@wordpress/components` imports `getWpCompatOverlaySlot()` directly from `@wordpress/ui`'s public exports (also promoted from internal in this change). The `@wordpress/components` dep on `@wordpress/ui` is transitional, scoped to the legacy-overlay migration. Cross-document drags (e.g. dragging an element inside an iframe while the slot is in the parent document) fall back to the previous placement so the clone's viewport-relative geometry stays in a single coordinate space. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. `@wordpress/components` imports `getWpCompatOverlaySlot()` directly from `@wordpress/ui`'s public exports (also promoted from internal in this change). The `@wordpress/components` dep on `@wordpress/ui` is transitional, scoped to the legacy-overlay migration. Cross-document drags (e.g. dragging an element inside an iframe while the slot is in the parent document) fall back to the previous placement so the clone's viewport-relative geometry stays in a single coordinate space. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping.
…ot (#78183) * Draggable: Migrate clone wrapper to wp compat overlay slot Replace the legacy body-level / element-wrapper placement and its `z-index: 1000000000` with a portal-style migration onto the `@wordpress/ui` compat overlay slot (#77851). When the slot is available, the drag clone joins the slot's body-level stacking context across all three placement modes, so an active drag automatically shares stacking with any `@wordpress/ui` overlay opened mid-drag without needing per-version z-index races. Auto-enabled in WordPress environments via the slot helper's `window.wp.components` auto-detect; standalone hosts that bundle `@wordpress/components` directly fall back to the previous placement until they call `useEnableWpCompatOverlaySlot()`. `@wordpress/components` imports `getWpCompatOverlaySlot()` directly from `@wordpress/ui`'s public exports (also promoted from internal in this change). The `@wordpress/components` dep on `@wordpress/ui` is transitional, scoped to the legacy-overlay migration. Cross-document drags (e.g. dragging an element inside an iframe while the slot is in the parent document) fall back to the previous placement so the clone's viewport-relative geometry stays in a single coordinate space. The default placement mode (`appendToOwnerDocument: false`, no `dragComponent`) previously appended the clone to the dragged element's parent. In WP environments where the slot is now in effect, the clone instead lives in the slot — a body-level location. In-repo ripgrep finds no CSS or event-delegation scoping anchored to the clone's previous in-flow parent; external consumers that relied on that ancestry must either not opt into the slot or migrate their scoping. * Draggable: Storybook: render docs-page stories in iframes The drag clone uses `position: fixed`, which Storybook's docs-page wrappers break because they apply `transform`s that establish new containing blocks. As a result the clone resolves against those wrappers instead of the viewport and lands in the wrong place on the autodocs page. Render each Draggable story in its own iframe on the autodocs page via `parameters.docs.story.inline: false`, which restores viewport-relative positioning for the clone. * Draggable: Storybook: polish cross-document fallback playground story Three small follow-ups on the iframe regression story: - Inject the Draggable SCSS into the iframe via Vite's `?inline` import (same pattern `WithGlobalCSS` uses with `global-basic.scss?inline`) instead of duplicating rule bodies in `srcDoc`. Single source of truth; future SCSS edits flow through automatically. - Guard the style injection on `iframeDoc?.head` so the brief about:blank → srcDoc transition doesn't throw on the initial `useEffect` pass. - Align the slot-presence display with the public `getWpCompatOverlaySlot()` API: it now returns `undefined` rather than `null` when no slot is registered. * CHANGELOG: Restore entries dropped during rebase Three Unreleased entries were inadvertently removed when the Draggable migration commit was rebased onto trunk: - @wordpress/components Internal: `Modal`, `Menu`, `DropdownMenu` motion-token adoption (#76097). - @wordpress/components Internal: `Popover` close-button z-index cleanup (#78180). - @wordpress/ui Bug Fixes: `Text` CSS-defense values for paragraph and heading variants (#78172). Restore them under their original headings. * Storybook: Fix popover-with-slotfill cross-iframe collision boundary `Popover.Popup` stopped accepting `collisionBoundary` directly when #78168 introduced the `Popover.Positioner` slot subcomponent. The prop is now silently ignored on `Popup`, so the cross-iframe story's collision avoidance regressed after the rebase onto trunk. Route the boundary through `Popover.Positioner` (matching the modern `Popover.Popup`'s `positioner` slot pattern) so the popup honors the iframe's clipping edge again. This file is `.jsx` so the type system didn't catch the silent prop-drop. * Draggable: Storybook: refresh AppendElementToOwnerDocument JSDoc The story's JSDoc still described the legacy "escape ancestor stacking context" rationale, which now contradicts the updated `appendToOwnerDocument` JSDoc in `types.ts` for hosts that opt into the `@wordpress/ui` compat overlay slot — where the clone always lives in the body-level slot and the prop is a no-op. Update the story's docblock to mirror the type-level guidance and call out the cross-document fallback exception. * Draggable: CHANGELOG: Call out default-mode in-flow ancestor change The original Draggable entry covered the stacking + cross-document fallback story, but left the load-bearing behavior change for third-party consumers in the PR description only: in the default placement mode (no `appendToOwnerDocument`, no `__experimentalDragComponent`), the clone used to be a DOM descendant of the dragged element's parent. With the slot active, it now lives at the body-level slot regardless. Surface that change directly in the CHANGELOG entry, including a migration hint for consumers that scoped CSS or event delegation on the clone's previous ancestry. * Draggable: Add e2e regression for chip-inside-compat-slot Lock in the structural guarantee that underpins the stacking claim in #78183: when `@wordpress/components`'s `Draggable` runs in a WordPress environment, the drag chip is rendered inside the body-level `[data-wp-compat-overlay-slot]`. That single structural assertion subsumes the visual stacking contract — the slot creates an isolated stacking context with `z-index: 1000000003`, so anything appended into it stacks above any `@wordpress/components` overlay opened mid-drag (which live outside the slot at lower `z-index`s). Asserting structure rather than visual layering keeps the test robust against unrelated overlay z-index churn, and avoids a brittle `elementFromPoint`-style probe across the parent-doc/canvas-iframe boundary. * Storybook: Trim file-level docblocks on playground stories Drop the file-level brain-dump JSDoc from the `draggable-cross-document-fallback` and `popover-with-slotfill` playground stories. The story body and any per-story copy carry the user-facing explanation; the file-level prose was internal reasoning that doesn't belong in the story source. Per mirka's review on PR #78183 (empty-suggestion blocks). * Storybook: popover-with-slotfill story: use public @wordpress/ui API Switch the playground story to consume `Popover` from the public `@wordpress/ui` entry point instead of reaching into `packages/ui/src/popover`. Inline a small `IframePortal` helper locally so the story no longer depends on `packages/ui/src/popover/stories/utils` either (those story utilities are not part of any public surface). Also swap the `Slot` ref from `useRef` to a state setter so the popup re-renders once the slot's container element mounts, which removes a first-render race the previous `useRef` pattern had. Per mirka's review on PR #78183. * Storybook: draggable cross-doc story: load components styles via Storybook bundle Swap the iframe's style injection from a `?inline` import of `packages/components/src/draggable/style.scss` (reaching into another package's source) to Storybook's own `storybook/package-styles/components-ltr.lazy.scss`, which is the canonical bundle of `@wordpress/components` styles for stories. The injected CSS is now broader than strictly necessary (the whole package stylesheet rather than only Draggable's rules), but this is a debug fixture and the cost is negligible. In exchange we drop the cross-package src reach. Per mirka's review on PR #78183. * Storybook: Move cross-document fallback story under "Debug fixtures" The cross-document fallback story is strictly defensive regression coverage and doesn't illustrate a pattern non- maintainers would seek out. Move it under a `Debug fixtures` sub-section in the sidebar so the main `Playground/` namespace stays focused on intended-usage demos. Per mirka's review on PR #78183. * Storybook: Drop redundant `parameters.sourceLink` from playground stories The `source-link` Storybook addon already derives the GitHub source path from `storyData.importPath` when no explicit `parameters.sourceLink` is provided (see `storybook/addons/source-link/manager.ts`). For stories living under `storybook/stories/playground/`, that fallback resolves to the same value the explicit `sourceLink` was hard-coding, so the declaration is pure duplication. Per mirka's review on PR #78183 (empty-suggestion blocks covering the `parameters: { sourceLink: ... }` literal). * Draggable: Migrate styles from SCSS to a CSS module Move the (already small) Draggable stylesheet to a CSS module so its rules travel via `@wordpress/style-runtime` (and therefore into any iframe wrapped in `<StyleProvider>` — e.g. the block-editor canvas) without needing the package-level `build-style/style.css` bundle. Drops the `@use` line from `packages/components/src/style.scss`, following the same shape as the `AlignmentMatrixControl` (#73714/#73757) and `AnglePickerControl` (#73786) migrations. The CSS-module class names are standard (hashed). The legacy `components-draggable__*` / `is-dragging-components-draggable` class names are kept by adding them alongside the hashed ones in the JS `classList.add(...)` calls, since several other Gutenberg packages reference them in their own stylesheets (block-editor's `list-view`, `block-tools`, `block-library`'s `navigation` editor, `edit-widgets`' `widget-area` editor) and block-editor runtime JS reads `is-dragging-components-draggable` off `document.body`. Dropping those names would silently break those consumers. Per mirka's review on PR #78183 (CSS-module option for the iframe story); the corresponding Storybook simplification follows in a separate commit. * Storybook: Simplify cross-document fallback story with StyleProvider Now that Draggable's styles ship as a CSS module routed through `@wordpress/style-runtime`, the cross-document fallback story no longer needs to manually `?inline`-import and inject the whole `components-ltr` SCSS bundle into the iframe's `<head>`. Wrap the portaled iframe content in `<StyleProvider document={iframeDoc}>` from `@wordpress/components` instead — `StyleProvider` calls `registerDocument()` on the iframe document, and the style registry replays every registered CSS module (Draggable included) into that document. The visible behavior is unchanged: the orange clone still tracks the cursor inside the iframe, demonstrating the cross-document fallback. Per mirka's review on PR #78183. * Draggable: CHANGELOG: Move entry to Unreleased and slim it down The Enhancements entry for this PR ended up rolled into the already-cut `33.1.0` release section during an earlier rebase, and had grown to a 700-character paragraph spelling out every edge case (cross-document fallback, `appendToOwnerDocument` semantics, in-flow ancestor migration hints). Move it back to `## Unreleased` and trim to a two-sentence summary in line with the surrounding entries. The dropped detail still lives in the JSDoc, the code comments, and the PR description's <details> blocks. * Draggable: Trim verbose inline code comments Sweep across the comments added by this PR, dropping redundant duplication, narration of self-evident code, and prose that already lives in the PR description / JSDoc: - Drop the duplicate compat-slot note from the `AppendElementToOwnerDocument` story JSDoc (the interaction is already described on the prop's TS JSDoc in `types.ts`). - Tighten the prop JSDoc for `appendToOwnerDocument` to a single short paragraph. - Slim the same-document-only slot guard comment in `Draggable.start()` (the conditional itself reads as "slot if same document"). - Compact the rationale comment for `parameters.docs.story.inline: false` in the Draggable autodocs config to a single explanation. - Trim the structural-stacking assertion comment in the Playwright `draggable-blocks` spec. - Drop the forward-looking "can be removed on a future Stylelint upgrade" note from `CSS_BASELINE_2024_FUNCTIONS`. No behavior change. * @wordpress/ui CHANGELOG: Move #78183 entry to Unreleased The `getWpCompatOverlaySlot()` export bullet was left inside the already-released `## 0.13.0` section when the parallel `@wordpress/components` entry was moved to `## Unreleased`. * Draggable: Keep physical `left` for the invisible drag image Mirroring this offscreen stand-in in RTL has no benefit — either side hides it equally — so revert to the original physical property and silence the logical-properties lint with a targeted comment. * @wordpress/ui CHANGELOG: Trim #78183 entry * @wordpress/ui: Restore unrelated tsconfig change * Storybook: Drop redundant story-name overrides * Draggable: Keep body cursor class global The cursor flip is also triggered by external code (block-editor keyboard drag, etc.) that toggles `is-dragging-components-draggable` directly. Targeting the legacy class globally preserves that flow, which a module-hashed selector silently broke. * Draggable: Guard class arrays against the Jest CSS-module mock `jest-preset-default`'s style mock returns `undefined` for any class, which `classList.add()` would coerce to a literal "undefined" token. Filter falsy entries to keep test DOM clean. * Draggable: Address minor self-review nits - Note why the invisible drag image bypasses the compat slot. - Drop a redundant chip-count assertion in the e2e spec. - Flag the SCSS-only stylelint override pattern explicitly. * Storybook: Group compat-slot fixtures under Debug fixtures Consolidates the manual verification stories (`WP Compat Overlay Slot`, `Popover with SlotFill`) alongside the existing Draggable fixture. * Draggable: Use kebab-case for CSS module class names --- Co-authored-by: ciampo <mciampini@git.wordpress.org> Co-authored-by: mirka <0mirka00@git.wordpress.org>
Compresses JSDoc, inline comments, prose, and CHANGELOG/README copy added by #77851, #78095, and #78183 down to the essentials. Public API docs keep the "what / when / two opt-in paths" guidance; internal narration and implementation reasoning that the code already conveys are dropped or compressed. No behavior changes.
Compresses JSDoc, inline comments, prose, and README copy added by #77851, #78095, and #78183 down to the essentials. Public API docs keep the "what / when / two opt-in paths" guidance; internal narration and implementation reasoning that the code already conveys is dropped or compressed. No behavior changes.
What?
Adds a dormant body-level overlay slot to
@wordpress/ui. Once a leaf overlay portals into it, that overlay stacks above@wordpress/componentsoverlays in mixed-library compositions. No consumers in this PR.Why?
Reliable cross-library stacking, without per-instance plumbing on every
@wordpress/uioverlay. The first leaf-overlay wiring (Tooltip) lands in #78095 and validates the slot end-to-end; this PR ships the infrastructure on its own so it can land independently.How?
Two opt-in paths, both lazy:
window.wp.componentsis on the global — the typical script-loader setup for plugins and admin screens, so most consumers do nothing.useEnableWpCompatOverlaySlot()hook for hosts that bundle@wordpress/components(or only@wordpress/ui) directly rather than relying on the global — apps that aren't built with standard WordPress build tooling.When opted in, the helper lazy-creates a single
<div data-wp-compat-overlay-slot>ondocument.bodyat z-index1000000003. Otherwise it returnsnulland the default portal container the overlay primitives ship with stays in effect.Implementation notes
position: fixed; top: 0; left: 0; z-index: 1000000003; isolation: isolate. Full reasoning lives in the file's header comment.Portal, which calls the helper on every render) pick up the slot on first mount. The enable is one-way by design — a single component shouldn't be able to turn off shared infrastructure for everyone else.Windowinterface; the hook is the only documented opt-in surface.document), with no cross-frame traversal. This gives the right placement for both common iframe patterns: Gutenberg's editor canvas iframe is acreatePortalboundary, not a script boundary, so@wordpress/uicomponents rendered inside the canvas keep running in the parent's JS realm and the slot lands in the parent body — escaping the iframe naturally. True script-boundary iframes (Storybook's preview, embedded standalone apps) load their own bundle, run their own JS realm, and get a slot in their own document, alongside the bundle's CSS modules.@wordpress/uipackage instances on the same page coordinate through the DOM via the[data-wp-compat-overlay-slot]attribute — one slot regardless of bundle count. Cache self-heals when the element is removed externally.document.bodyis null-guarded.Testing Instructions
npx jest --config=test/unit/jest.config.js --testPathPattern='packages/ui/src/utils/test/(wp-compat-overlay-slot|use-enable-wp-compat-overlay-slot)'Expected: 28 passing tests. The helper is dormant, so there's no user-facing surface to verify in this PR; end-to-end validation lands in #78095.
Next steps
@wordpress/uioverlays through the slot, starting with Tooltip ([ui] Tooltip: Default portal container to the wp compat overlay slot #78095) and continuing incrementally..components-draggable__cloneso an active drag stays above the slot — likely by migrating it onto the slot rather than bumping its z-index.Use of AI Tools
Authored with Cursor (Claude). Author reviewed all changes.