Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/evil-coins-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': patch
---

AnchoredOverlay: Disables CSS anchor positioning if `portalContainerName` is true. (behind `primer_react_css_anchor_positioning` feature flag)
66 changes: 63 additions & 3 deletions packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type {AnchorPosition} from '@primer/behaviors'
import {implementsClassName} from '../utils/testing'
import {FeatureFlags} from '../FeatureFlags'
import {registerPortalRoot} from '../Portal'

import overlayClasses from '../Overlay/Overlay.module.css'
import anchoredOverlayClasses from './AnchoredOverlay.module.css'
Expand Down Expand Up @@ -174,9 +175,7 @@
expect(mockCloseCallback).toHaveBeenCalledWith('escape')
})

// onPositionChange is not supported when the CSS anchor positioning flag is enabled,
// because positioning is handled by the browser rather than `useAnchoredPosition`.
it.skipIf(withCSSAnchorPositioningFeatureFlag)('should call onPositionChange when provided', async () => {
it('should call onPositionChange when provided', async () => {
const mockPositionChangeCallback = vi.fn(({position}: {position: AnchorPosition}) => position)
render(
<AnchoredOverlayTestComponent
Expand All @@ -190,7 +189,7 @@
await userEvent.keyboard('{Escape}')
})

expect(mockPositionChangeCallback).toHaveBeenCalled()

Check failure on line 192 in packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx

View workflow job for this annotation

GitHub Actions / test (react-19)

[@primer/react (chromium)] src/AnchoredOverlay/AnchoredOverlay.test.tsx > AnchoredOverlay (primer_react_css_anchor_positioning=true) > should call onPositionChange when provided

AssertionError: expected "vi.fn()" to be called at least once ❯ toHaveBeenCalled src/AnchoredOverlay/AnchoredOverlay.test.tsx:192:41

Check failure on line 192 in packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx

View workflow job for this annotation

GitHub Actions / test (react-18)

[@primer/react (chromium)] src/AnchoredOverlay/AnchoredOverlay.test.tsx > AnchoredOverlay (primer_react_css_anchor_positioning=true) > should call onPositionChange when provided

AssertionError: expected "vi.fn()" to be called at least once ❯ toHaveBeenCalled src/AnchoredOverlay/AnchoredOverlay.test.tsx:192:41
expect(mockPositionChangeCallback).toHaveBeenCalledWith({
position: {
anchorAlign: 'start',
Expand Down Expand Up @@ -354,6 +353,67 @@
const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
expect(overlay).not.toHaveAttribute('popover')
})

describe('when overlayProps.portalContainerName is provided', () => {
it('should fall back to JS positioning (data-anchor-position="false") even with the flag enabled', () => {
const portalRoot = document.createElement('div')
document.body.appendChild(portalRoot)
registerPortalRoot(portalRoot, 'anchoredOverlayTestPortal')

const {baseElement} = render(
<FeatureFlags flags={{primer_react_css_anchor_positioning: true}}>
<BaseStyles>
<AnchoredOverlay
open={true}
onOpen={() => {}}
onClose={() => {}}
renderAnchor={props => <Button {...props}>Anchor Button</Button>}
overlayProps={{portalContainerName: 'anchoredOverlayTestPortal'}}
>
<button type="button">Focusable Child</button>
</AnchoredOverlay>
</BaseStyles>
</FeatureFlags>,
)

const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
expect(overlay).toHaveAttribute('data-anchor-position', 'false')
expect(overlay).not.toHaveClass(anchoredOverlayClasses.AnchoredOverlay)

portalRoot.remove()
})

it('should not opt into the Popover API even when renderAs="popover"', () => {
const portalRoot = document.createElement('div')
document.body.appendChild(portalRoot)
registerPortalRoot(portalRoot, 'anchoredOverlayTestPortalPopover')

const {baseElement} = render(
<FeatureFlags flags={{primer_react_css_anchor_positioning: true}}>
<BaseStyles>
<AnchoredOverlay
open={true}
onOpen={() => {}}
onClose={() => {}}
renderAnchor={props => <Button {...props}>Anchor Button</Button>}
renderAs="popover"
overlayProps={{portalContainerName: 'anchoredOverlayTestPortalPopover'}}
>
<button type="button">Focusable Child</button>
</AnchoredOverlay>
</BaseStyles>
</FeatureFlags>,
)

const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
expect(overlay).not.toHaveAttribute('popover')

const anchor = baseElement.querySelector('[aria-haspopup="true"]')
expect(anchor).not.toHaveAttribute('popovertarget')

portalRoot.remove()
})
})
})

describe('with primer_react_css_anchor_positioning feature flag disabled', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ export const AnchoredOverlay: React.FC<React.PropsWithChildren<AnchoredOverlayPr
() => typeof document !== 'undefined' && 'anchorName' in document.documentElement.style,
)

const cssAnchorPositioning = cssAnchorPositioningFlag && supportsNativeCSSAnchorPositioning
const cssAnchorPositioning =
cssAnchorPositioningFlag && supportsNativeCSSAnchorPositioning && !overlayProps?.portalContainerName
Comment thread
TylerJDev marked this conversation as resolved.
Comment on lines +178 to +179
// Only use Popover API when both CSS anchor positioning is enabled AND renderAs is true
const shouldRenderAsPopover = cssAnchorPositioning && renderAs === 'popover'
const anchorRef = useProvidedRefOrCreate(externalAnchorRef)
Expand Down
Loading