fix(react-headless-components-preview): share anchor-name across positioned popovers#36347
Conversation
…tioned popovers usePositioning overwrote the trigger's anchor-name with a unique name per instance, so two popovers sharing one trigger (e.g. a hover Tooltip and a click Menu on the same button) clobbered each other — only the last writer stayed anchored and the other fell back to default placement. Append this instance's anchor name to the trigger's comma-separated anchor-name list and remove only our own on cleanup, preserving any author-set or sibling anchors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📊 Bundle size report
🤖 This report was generated against 19d82d85bda458b0af3fa4e0814bf3232fc78594 |
|
Pull request demo site: URL |
Confidence Score: 85/100The code changes look correct and well-covered by tests, but merge readiness is currently blocked by failing CI checks. FindingsBlockers (must fix before merge)
Warnings (should address)
Info (consider)
Category Breakdown
RecommendationREQUEST_CHANGES Code quality is solid and the fix is well-tested, but CI must be green before merge. Posted via the /review-pr skill. |
| expect(node.style.getPropertyValue('anchor-name')).toMatch(/^--popover-anchor-/); | ||
| }); | ||
|
|
||
| it('appends to anchor-name so multiple instances can share one trigger', () => { |
There was a problem hiding this comment.
we should also add a e2e test that covers the Menu + Tooltip scenario mentioned in the issue
| // single trigger (e.g. a Tooltip on hover and a Menu on click attached to | ||
| // the same button) without clobbering each other's anchor. On cleanup we | ||
| // remove only our own name, preserving any others still in use. | ||
| const readAnchorNames = () => |
There was a problem hiding this comment.
nit: define this function in module scope rather than in the closure.
| .filter(Boolean); | ||
|
|
||
| const names = readAnchorNames(); | ||
| if (!names.includes(anchorName)) { |
There was a problem hiding this comment.
This check could be included in readAnchorNames. Rather than call .filter you can call .find and save an iteration of the array. You can short circuit if anchorName is falsey.

Fixes
Part of #36346.
What
usePositioningwrote a uniqueanchor-nameonto the trigger element and overwrote anything already there:When two positioned popovers share one trigger — e.g. a hover Tooltip + a click Menu on the same button — the two
usePositioninginstances clobber each other'sanchor-name.anchor-nameis single-valued here, so the last writer wins; the other popover'sposition-anchorpoints at a name that no longer exists and falls back to default (static / top‑left) placement.Rendered proof (menu closed, tooltip visible)
How
anchor-nameaccepts a comma-separated list. EachusePositioninginstance now appends its own name and removes only its own on cleanup, preserving any author-set or sibling anchors:Single-popover triggers are unchanged; N popovers per trigger now each resolve their own
position-anchor.Tests
Added two unit tests to
usePositioning.test.tsx:anchor-namecontains both distinct namesanchor-nameis preservedAll 19
usePositioningtests pass; Tooltip suite (16) unaffected.