Skip to content

Commit 8f8844e

Browse files
authored
[CSS Anchor Positioning] Disable feature flag if portalContainerName is true (#7805)
1 parent 9dbecd7 commit 8f8844e

3 files changed

Lines changed: 69 additions & 3 deletions

File tree

.changeset/evil-coins-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
5+
AnchoredOverlay: Disables CSS anchor positioning if `portalContainerName` is true. (behind `primer_react_css_anchor_positioning` feature flag)

packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import BaseStyles from '../BaseStyles'
88
import type {AnchorPosition} from '@primer/behaviors'
99
import {implementsClassName} from '../utils/testing'
1010
import {FeatureFlags} from '../FeatureFlags'
11+
import {registerPortalRoot} from '../Portal'
1112

1213
import overlayClasses from '../Overlay/Overlay.module.css'
1314
import anchoredOverlayClasses from './AnchoredOverlay.module.css'
@@ -174,8 +175,6 @@ describe.each([true, false])(
174175
expect(mockCloseCallback).toHaveBeenCalledWith('escape')
175176
})
176177

177-
// onPositionChange is not supported when the CSS anchor positioning flag is enabled,
178-
// because positioning is handled by the browser rather than `useAnchoredPosition`.
179178
it.skipIf(withCSSAnchorPositioningFeatureFlag)('should call onPositionChange when provided', async () => {
180179
const mockPositionChangeCallback = vi.fn(({position}: {position: AnchorPosition}) => position)
181180
render(
@@ -354,6 +353,67 @@ describe('AnchoredOverlay feature flag specific behavior', () => {
354353
const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
355354
expect(overlay).not.toHaveAttribute('popover')
356355
})
356+
357+
describe('when overlayProps.portalContainerName is provided', () => {
358+
it('should fall back to JS positioning (data-anchor-position="false") even with the flag enabled', () => {
359+
const portalRoot = document.createElement('div')
360+
document.body.appendChild(portalRoot)
361+
registerPortalRoot(portalRoot, 'anchoredOverlayTestPortal')
362+
363+
const {baseElement} = render(
364+
<FeatureFlags flags={{primer_react_css_anchor_positioning: true}}>
365+
<BaseStyles>
366+
<AnchoredOverlay
367+
open={true}
368+
onOpen={() => {}}
369+
onClose={() => {}}
370+
renderAnchor={props => <Button {...props}>Anchor Button</Button>}
371+
overlayProps={{portalContainerName: 'anchoredOverlayTestPortal'}}
372+
>
373+
<button type="button">Focusable Child</button>
374+
</AnchoredOverlay>
375+
</BaseStyles>
376+
</FeatureFlags>,
377+
)
378+
379+
const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
380+
expect(overlay).toHaveAttribute('data-anchor-position', 'false')
381+
expect(overlay).not.toHaveClass(anchoredOverlayClasses.AnchoredOverlay)
382+
383+
portalRoot.remove()
384+
})
385+
386+
it('should not opt into the Popover API even when renderAs="popover"', () => {
387+
const portalRoot = document.createElement('div')
388+
document.body.appendChild(portalRoot)
389+
registerPortalRoot(portalRoot, 'anchoredOverlayTestPortalPopover')
390+
391+
const {baseElement} = render(
392+
<FeatureFlags flags={{primer_react_css_anchor_positioning: true}}>
393+
<BaseStyles>
394+
<AnchoredOverlay
395+
open={true}
396+
onOpen={() => {}}
397+
onClose={() => {}}
398+
renderAnchor={props => <Button {...props}>Anchor Button</Button>}
399+
renderAs="popover"
400+
overlayProps={{portalContainerName: 'anchoredOverlayTestPortalPopover'}}
401+
>
402+
<button type="button">Focusable Child</button>
403+
</AnchoredOverlay>
404+
</BaseStyles>
405+
</FeatureFlags>,
406+
)
407+
408+
const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
409+
expect(overlay).not.toHaveAttribute('popover')
410+
411+
const anchor = baseElement.querySelector('[aria-haspopup="true"]')
412+
expect(anchor).not.toHaveAttribute('popovertarget')
413+
414+
portalRoot.remove()
415+
})
416+
})
357417
})
358418

359419
describe('with primer_react_css_anchor_positioning feature flag disabled', () => {

packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ export const AnchoredOverlay: React.FC<React.PropsWithChildren<AnchoredOverlayPr
175175
() => typeof document !== 'undefined' && 'anchorName' in document.documentElement.style,
176176
)
177177

178-
const cssAnchorPositioning = cssAnchorPositioningFlag && supportsNativeCSSAnchorPositioning
178+
const cssAnchorPositioning =
179+
cssAnchorPositioningFlag && supportsNativeCSSAnchorPositioning && !overlayProps?.portalContainerName
179180
// Only use Popover API when both CSS anchor positioning is enabled AND renderAs is true
180181
const shouldRenderAsPopover = cssAnchorPositioning && renderAs === 'popover'
181182
const anchorRef = useProvidedRefOrCreate(externalAnchorRef)

0 commit comments

Comments
 (0)