Skip to content

fix(react-headless-components-preview): let the consumer popover prop win on the PopoverSurface, MenuPopover and Listbox slots - #36665

Merged
Dmytro Kirpa (dmytrokirpa) merged 3 commits into
microsoft:masterfrom
ArrayKnight:fix/headless-popover-attribute-36647
Sep 1, 2026
Merged

fix(react-headless-components-preview): let the consumer popover prop win on the PopoverSurface, MenuPopover and Listbox slots#36665
Dmytro Kirpa (dmytrokirpa) merged 3 commits into
microsoft:masterfrom
ArrayKnight:fix/headless-popover-attribute-36647

Conversation

@ArrayKnight

@ArrayKnight Ray Knight (ArrayKnight) commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Three hooks in the headless package set the native popover attribute on their surface slot on the wrong side of the props spread, so a consumer's own popover value can never win: usePopoverSurface pins it after ...props, useMenuPopover spreads popover: 'auto' over the base state's root, and useListbox assigns state.root.popover = 'auto' after the hook returns. Since popover="auto" is the browser's light-dismiss mode — opening one auto popover closes every other — an application that needs two surfaces open at once (a popover inside a popover, a persistent side surface) has no way to reach popover="manual". Measured on an 8-cell probe: 1/8 surfaces open before, 8/8 after.

The fix moves the attribute to the default side of the merge at all three sites, the position the package's own slot-default convention already provides, so the consumer's value merges over it. A regression test is added at each site; each was verified to fail without the fix. (The surface id and role, which are pinned deliberately for the trigger's aria-details contract, are intentionally not touched.)

Fixes #36647.

Extracted from #36656 per maintainer request — each in-tree fix from that PR as an isolated change.

… win on the PopoverSurface slot

The surface slot spelled its popover attribute after the consumer props
spread, so the prop was silently discarded — popover exclusivity then capped
a page at one open Popover where eight should show, and a consumer switching
to manual dismiss was ignored. It moves into the slot's defaultProps, the
placement the Tooltip hook already uses: the hook supplies the default, the
consumer wins.

The id spelled alongside it deliberately stays put. Popover.types documents
`<Popover id>` as the surface's id channel, and usePopoverTrigger points
aria-details at that same value, so letting a surface-level id win would
leave the trigger referencing an element that no longer exists.
PopoverSurface.test.tsx already guards that linkage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wmpBCYJpDJCLXcScCWz1i
… win on the MenuPopover and Listbox slots

Both surfaces pinned their popover attribute where a consumer's prop could
not reach it. useMenuPopover spread `popover: 'auto'` over the base state's
root after the base hook had already merged props, and useListbox assigned
`state.root.popover = 'auto'` outright after its hook returned. Because every
auto popover on a page closes the others, that capped a page at one open Menu
and one open listbox: measured, three listboxes give one open surface, and
three Menus give one. The default now merges before the base state rather than
over it, which is the order slot.always already uses for defaultProps — the
hook supplies 'auto', a consumer spelling popover="manual" wins, and the same
three-cell pages then show three open surfaces.

Nothing else rides either assignment. Unlike PopoverSurface, where an id was
spelled alongside the attribute and had to stay put, these two sites set the
popover attribute alone: the listbox's id and its aria-labelledby linkage are
owned by useListboxSlot, which this does not touch.

Light dismiss under the default is unchanged, and a closed listbox still stays
mounted at display:none.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wmpBCYJpDJCLXcScCWz1i
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Aj9uA3rCVgosnh2zNn8qkc

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes headless popover slots so consumer-provided popover values override the default. Confidence: 100/100.

Changes:

  • Makes popover="auto" an overridable default for PopoverSurface, MenuPopover, and Listbox.
  • Adds regression tests for default and overridden values.
  • Adds a patch change file.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
usePopoverSurface.ts Moves popover into slot defaults.
PopoverSurface.test.tsx Tests consumer override.
useMenuPopover.ts Gives base root props precedence.
Menu.test.tsx Tests default and override behavior.
useListbox.ts Gives listbox root props precedence.
Dropdown.test.tsx Tests listbox override.
change/...json Records the patch release.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-headless-components-preview
react-headless-components-preview: entire library
238.991 kB
67.288 kB
239.02 kB
67.291 kB
29 B
3 B
react-headless-components-preview
@fluentui/react-headless-components-preview/tag-picker
53.988 kB
17.748 kB
54.002 kB
17.751 kB
14 B
3 B
react-headless-components-preview
@fluentui/react-headless-components-preview/teaching-popover
36.048 kB
11.996 kB
36.063 kB
12.003 kB
15 B
7 B

🤖 This report was generated against 49c0291821bfa7e2ef29514685a666198001ac73

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Pull request demo site: URL

@dmytrokirpa

Copy link
Copy Markdown
Contributor

Thanks for the contribution Ray Knight (@ArrayKnight)!

@dmytrokirpa
Dmytro Kirpa (dmytrokirpa) merged commit 52aa7a8 into microsoft:master Sep 1, 2026
13 checks passed
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.

[Bug]: headless Popover/Menu/Listbox pin the popover attribute after the props spread, so only one surface can be open per page

3 participants