diff --git a/.changeset/evil-coins-tease.md b/.changeset/evil-coins-tease.md new file mode 100644 index 00000000000..f64fb54c3ab --- /dev/null +++ b/.changeset/evil-coins-tease.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +AnchoredOverlay: Disables CSS anchor positioning if `portalContainerName` is true. (behind `primer_react_css_anchor_positioning` feature flag) diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx index 4caecfa0682..405970b1938 100644 --- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx +++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx @@ -8,6 +8,7 @@ import BaseStyles from '../BaseStyles' 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' @@ -174,9 +175,7 @@ describe.each([true, false])( 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( { 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( + + + {}} + onClose={() => {}} + renderAnchor={props => } + overlayProps={{portalContainerName: 'anchoredOverlayTestPortal'}} + > + + + + , + ) + + 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( + + + {}} + onClose={() => {}} + renderAnchor={props => } + renderAs="popover" + overlayProps={{portalContainerName: 'anchoredOverlayTestPortalPopover'}} + > + + + + , + ) + + 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', () => { diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx index aa5c0818d0a..a89234d59da 100644 --- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx +++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx @@ -175,7 +175,8 @@ export const AnchoredOverlay: React.FC typeof document !== 'undefined' && 'anchorName' in document.documentElement.style, ) - const cssAnchorPositioning = cssAnchorPositioningFlag && supportsNativeCSSAnchorPositioning + const cssAnchorPositioning = + cssAnchorPositioningFlag && supportsNativeCSSAnchorPositioning && !overlayProps?.portalContainerName // Only use Popover API when both CSS anchor positioning is enabled AND renderAs is true const shouldRenderAsPopover = cssAnchorPositioning && renderAs === 'popover' const anchorRef = useProvidedRefOrCreate(externalAnchorRef)