diff --git a/.changeset/nine-buttons-lose.md b/.changeset/nine-buttons-lose.md
deleted file mode 100644
index 69945bd669e..00000000000
--- a/.changeset/nine-buttons-lose.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@primer/react': minor
----
-
-AnchoredOverlay: Add CSS Anchor Positioning to `AnchoredOverlay` (under a feature flag)
diff --git a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Alignment-light-css-anchor-positioning-linux.png b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Alignment-light-css-anchor-positioning-linux.png
index 4d42f44e486..a1d1eb327c3 100644
Binary files a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Alignment-light-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Alignment-light-css-anchor-positioning-linux.png differ
diff --git a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Position-Grid-light-css-anchor-positioning-linux.png b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Position-Grid-light-css-anchor-positioning-linux.png
index cfc272e058d..7ca823934ce 100644
Binary files a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Position-Grid-light-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Anchor-Position-Grid-light-css-anchor-positioning-linux.png differ
diff --git a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Fullscreen-Variant-light-css-anchor-positioning-linux.png b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Fullscreen-Variant-light-css-anchor-positioning-linux.png
index edb90f94d08..d8905c4e284 100644
Binary files a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Fullscreen-Variant-light-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Fullscreen-Variant-light-css-anchor-positioning-linux.png differ
diff --git a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Alignment-From-Anchor-light-css-anchor-positioning-linux.png b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Alignment-From-Anchor-light-css-anchor-positioning-linux.png
index 0dfe120c911..9fd40da8ea8 100644
Binary files a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Alignment-From-Anchor-light-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Alignment-From-Anchor-light-css-anchor-positioning-linux.png differ
diff --git a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Position-From-Anchor-light-css-anchor-positioning-linux.png b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Position-From-Anchor-light-css-anchor-positioning-linux.png
index b3853ddc166..cd1446b4fb2 100644
Binary files a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Position-From-Anchor-light-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Offset-Position-From-Anchor-light-css-anchor-positioning-linux.png differ
diff --git a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Scroll-With-Anchor-light-css-anchor-positioning-linux.png b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Scroll-With-Anchor-light-css-anchor-positioning-linux.png
index 940be94e376..6b80e1f6afe 100644
Binary files a/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Scroll-With-Anchor-light-css-anchor-positioning-linux.png and b/.playwright/snapshots/components/AnchoredOverlay.test.ts-snapshots/AnchoredOverlay-Scroll-With-Anchor-light-css-anchor-positioning-linux.png differ
diff --git a/package-lock.json b/package-lock.json
index e4d9cbafc77..f31d6e38150 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4816,21 +4816,23 @@
}
},
"node_modules/@floating-ui/core": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
- "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
- "version": "1.7.5",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz",
- "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==",
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+ "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "@floating-ui/core": "^1.7.4",
+ "@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
@@ -4852,6 +4854,7 @@
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@github-ui/storybook-addon-performance-panel": {
@@ -6474,55 +6477,6 @@
"node": "^16.13.0 || >=18.0.0"
}
},
- "node_modules/@oddbird/css-anchor-positioning": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/@oddbird/css-anchor-positioning/-/css-anchor-positioning-0.9.0.tgz",
- "integrity": "sha512-G5nfb4sU0auxJH7VHafPwVJjr1GhH5uPSkmytGqhNftCpT3QEh8pFtMd4YHt1dRwb4o9qVZxlGSKUIc4TIrysQ==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@floating-ui/dom": "^1.7.5",
- "@types/css-tree": "^2.3.11",
- "css-tree": "^3.1.0",
- "nanoid": "^5.1.6"
- }
- },
- "node_modules/@oddbird/css-anchor-positioning/node_modules/css-tree": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
- "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
- "license": "MIT",
- "dependencies": {
- "mdn-data": "2.12.2",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
- }
- },
- "node_modules/@oddbird/css-anchor-positioning/node_modules/mdn-data": {
- "version": "2.12.2",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
- "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
- "license": "CC0-1.0"
- },
- "node_modules/@oddbird/css-anchor-positioning/node_modules/nanoid": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
- "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.js"
- },
- "engines": {
- "node": "^18 || >=20"
- }
- },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"dev": true,
@@ -9083,12 +9037,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/css-tree": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/@types/css-tree/-/css-tree-2.3.11.tgz",
- "integrity": "sha512-aEokibJOI77uIlqoBOkVbaQGC9zII0A+JH1kcTNKW2CwyYWD8KM6qdo+4c77wD3wZOQfJuNWAr9M4hdk+YhDIg==",
- "license": "MIT"
- },
"node_modules/@types/debug": {
"version": "4.1.12",
"dev": true,
@@ -27795,7 +27743,6 @@
"@github/relative-time-element": "^4.5.0",
"@github/tab-container-element": "^4.8.2",
"@lit-labs/react": "1.2.1",
- "@oddbird/css-anchor-positioning": "^0.9.0",
"@oddbird/popover-polyfill": "^0.5.2",
"@primer/behaviors": "^1.10.2",
"@primer/live-region-element": "^0.7.1",
diff --git a/packages/react/package.json b/packages/react/package.json
index ce4b65c4361..44dc3e7f329 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -78,7 +78,6 @@
"@github/relative-time-element": "^4.5.0",
"@github/tab-container-element": "^4.8.2",
"@lit-labs/react": "1.2.1",
- "@oddbird/css-anchor-positioning": "^0.9.0",
"@oddbird/popover-polyfill": "^0.5.2",
"@primer/behaviors": "^1.10.2",
"@primer/live-region-element": "^0.7.1",
diff --git a/packages/react/src/ActionMenu/ActionMenu.examples.stories.tsx b/packages/react/src/ActionMenu/ActionMenu.examples.stories.tsx
index d55efb4b151..1f2b3bf420e 100644
--- a/packages/react/src/ActionMenu/ActionMenu.examples.stories.tsx
+++ b/packages/react/src/ActionMenu/ActionMenu.examples.stories.tsx
@@ -759,95 +759,3 @@ export const InsideDialog = () => {
)
}
-
-export const CenteredOnPage = () => {
- const [open, setOpen] = React.useState(false)
-
- return (
-
-
- Open menu
-
-
- alert('Copy link clicked')}>
- Copy link
- ⌘C
-
- alert('Quote reply clicked')}>
- Quote reply
- ⌘Q
-
- alert('Edit comment clicked')}>
- Edit comment
- ⌘E
-
-
- alert('Delete file clicked')}>
- Delete file
- ⌘D
-
-
-
-
-
- )
-}
-
-export const TwoActionMenus = () => {
- return (
-
-
- First menu
-
-
- alert('Copy clicked')}>
-
-
-
- Copy
- ⌘C
-
- alert('Archive clicked')}>
-
-
-
- Archive
-
-
- alert('Delete clicked')}>
- Delete
- ⌘D
-
-
-
-
-
-
- Second menu
-
-
- alert('Settings clicked')}>
-
-
-
- Settings
-
- alert('Workflows clicked')}>
-
-
-
- Workflows
-
-
-
-
-
-
- Documentation
-
-
-
-
-
- )
-}
diff --git a/packages/react/src/ActionMenu/ActionMenu.test.tsx b/packages/react/src/ActionMenu/ActionMenu.test.tsx
index 8d00bb8ea28..f7e74c3db77 100644
--- a/packages/react/src/ActionMenu/ActionMenu.test.tsx
+++ b/packages/react/src/ActionMenu/ActionMenu.test.tsx
@@ -9,7 +9,6 @@ import {Tooltip as TooltipV2} from '../TooltipV2/Tooltip'
import {SingleSelect} from '../ActionMenu/ActionMenu.features.stories'
import {MixedSelection} from '../ActionMenu/ActionMenu.examples.stories'
import {SearchIcon, KebabHorizontalIcon} from '@primer/octicons-react'
-import anchoredOverlayClasses from '../AnchoredOverlay/AnchoredOverlay.module.css'
import {getAnchoredPosition} from '@primer/behaviors'
import type {AnchorPosition} from '@primer/behaviors'
@@ -623,124 +622,58 @@ describe('ActionMenu', () => {
expect(baseAnchor).not.toHaveAttribute('aria-expanded', 'true')
})
- it('supports className prop on ActionMenu.Anchor with css anchor positioning flag', async () => {
- const component = HTMLRender(
-
-
-
-
-
-
-
-
- New file
-
- Copy link
- Edit file
- event.preventDefault()}>
- Delete file
-
-
- Github
-
-
-
-
-
- ,
- )
- const anchor = component.getByRole('button', {name: 'Toggle Menu'})
- expect(anchor).toHaveClass('test-class')
- expect(anchor).toHaveClass(anchoredOverlayClasses.Anchor)
- })
-
- it('supports className prop on ActionMenu.Button with css anchor positioning flag', async () => {
- const component = HTMLRender(
-
-
-
- Toggle Menu
-
-
- New file
-
- Copy link
- Edit file
- event.preventDefault()}>
- Delete file
-
-
- Github
-
-
-
-
-
- ,
- )
- const button = component.getByRole('button', {name: 'Toggle Menu'})
- expect(button).toHaveClass('test-class')
- expect(button).toHaveClass(anchoredOverlayClasses.Anchor)
- })
-
it('supports className prop on ActionMenu.Anchor', async () => {
const component = HTMLRender(
-
-
-
-
-
-
-
-
- New file
-
- Copy link
- Edit file
- event.preventDefault()}>
- Delete file
-
-
- Github
-
-
-
-
-
- ,
+
+
+
+
+
+
+
+ New file
+
+ Copy link
+ Edit file
+ event.preventDefault()}>
+ Delete file
+
+
+ Github
+
+
+
+
+ ,
)
const anchor = component.getByRole('button', {name: 'Toggle Menu'})
expect(anchor).toHaveClass('test-class')
- expect(anchor).not.toHaveClass(anchoredOverlayClasses.Anchor)
})
it('supports className prop on ActionMenu.Button', async () => {
const component = HTMLRender(
-
-
-
- Toggle Menu
-
-
- New file
-
- Copy link
- Edit file
- event.preventDefault()}>
- Delete file
-
-
- Github
-
-
-
-
-
- ,
+
+
+ Toggle Menu
+
+
+ New file
+
+ Copy link
+ Edit file
+ event.preventDefault()}>
+ Delete file
+
+
+ Github
+
+
+
+
+ ,
)
const button = component.getByRole('button', {name: 'Toggle Menu'})
expect(button).toHaveClass('test-class')
- expect(button).not.toHaveClass(anchoredOverlayClasses.Anchor)
})
})
diff --git a/packages/react/src/ActionMenu/ActionMenu.tsx b/packages/react/src/ActionMenu/ActionMenu.tsx
index d36ae802ca7..e501b117377 100644
--- a/packages/react/src/ActionMenu/ActionMenu.tsx
+++ b/packages/react/src/ActionMenu/ActionMenu.tsx
@@ -1,5 +1,4 @@
import React, {useCallback, useContext, useMemo, useEffect, useState} from 'react'
-import {clsx} from 'clsx'
import {TriangleDownIcon, ChevronRightIcon} from '@primer/octicons-react'
import type {AnchoredOverlayProps} from '../AnchoredOverlay'
import {AnchoredOverlay} from '../AnchoredOverlay'
@@ -74,10 +73,6 @@ const mergeAnchorHandlers = (anchorProps: React.HTMLAttributes, but
mergedAnchorProps.onKeyDown = mergedOnAnchorKeyDown
}
- if (buttonProps.className) {
- mergedAnchorProps.className = clsx(anchorProps.className, buttonProps.className)
- }
-
return mergedAnchorProps
}
@@ -158,11 +153,7 @@ const Menu: FCWithSlotMarker> = ({
}
}
} else {
- renderAnchor = anchorProps =>
- React.cloneElement(child, {
- ...anchorProps,
- className: clsx(anchorProps.className, child.props.className),
- })
+ renderAnchor = anchorProps => React.cloneElement(child, anchorProps)
}
return null
} else if (child.type === MenuButton || isSlot(child, MenuButton)) {
@@ -243,7 +234,6 @@ const Anchor: WithSlotMarker<
{React.cloneElement(child, {
...anchorProps,
ref: anchorRef,
- className: clsx(anchorProps.className, child.props.className),
onClick: onButtonClick,
onKeyDown: onButtonKeyDown,
})}
diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.module.css b/packages/react/src/AnchoredOverlay/AnchoredOverlay.module.css
index 72485a8d665..512a890b5ff 100644
--- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.module.css
+++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.module.css
@@ -12,48 +12,3 @@
display: inline-grid;
}
}
-
-.Wrapper {
- anchor-scope: --anchored-overlay-anchor;
-}
-
-.Anchor {
- /* Anchor name, this is currently tied to `renderAnchor` */
- anchor-name: --anchored-overlay-anchor;
-}
-
-.AnchoredOverlay {
- /* Anchor position, this is currently tied to `` */
- position-anchor: --anchored-overlay-anchor;
- position-try-fallbacks:
- flip-block,
- flip-inline,
- flip-block flip-inline;
- position-visibility: anchors-visible;
- z-index: 100;
- position: fixed;
-
- &[data-side='outside-bottom'] {
- /* stylelint-disable primer/spacing */
- top: calc(anchor(bottom) + var(--base-size-4));
- left: anchor(left);
- }
-
- &[data-side='outside-top'] {
- margin-bottom: var(--base-size-4);
- bottom: anchor(top);
- left: anchor(left);
- }
-
- &[data-side='outside-left'] {
- right: anchor(left);
- top: anchor(top);
- margin-right: var(--base-size-4);
- }
-
- &[data-side='outside-right'] {
- left: anchor(right);
- top: anchor(top);
- margin-left: var(--base-size-4);
- }
-}
diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
index 8bcb8d46284..279bcc112d1 100644
--- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
+++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
@@ -7,10 +7,8 @@ import {Button} from '../Button'
import BaseStyles from '../BaseStyles'
import type {AnchorPosition} from '@primer/behaviors'
import {implementsClassName} from '../utils/testing'
-import {FeatureFlags} from '../FeatureFlags'
import overlayClasses from '../Overlay/Overlay.module.css'
-import anchoredOverlayClasses from './AnchoredOverlay.module.css'
type TestComponentSettings = {
initiallyOpen?: boolean
@@ -18,7 +16,6 @@ type TestComponentSettings = {
onCloseCallback?: (gesture: string) => void
onPositionChange?: ({position}: {position: AnchorPosition}) => void
className?: string
- withCSSAnchorPositioningFeatureFlag?: boolean
}
const AnchoredOverlayTestComponent = ({
@@ -27,7 +24,6 @@ const AnchoredOverlayTestComponent = ({
onCloseCallback,
onPositionChange,
className,
- withCSSAnchorPositioningFeatureFlag,
}: TestComponentSettings = {}) => {
const [open, setOpen] = useState(initiallyOpen)
const onOpen = useCallback(
@@ -44,8 +40,7 @@ const AnchoredOverlayTestComponent = ({
},
[onCloseCallback],
)
-
- const content = (
+ return (
)
-
- if (withCSSAnchorPositioningFeatureFlag !== undefined) {
- return (
-
- {content}
-
- )
- }
-
- return content
}
-describe.each([true, false])(
- 'AnchoredOverlay (primer_react_css_anchor_positioning=%s)',
- (withCSSAnchorPositioningFeatureFlag: boolean) => {
- implementsClassName(
- props => (
-
- ),
- overlayClasses.Overlay,
+describe('AnchoredOverlay', () => {
+ implementsClassName(props => , overlayClasses.Overlay)
+ it('should call onOpen when the anchor is clicked', async () => {
+ const mockOpenCallback = vi.fn()
+ const mockCloseCallback = vi.fn()
+ const anchoredOverlay = render(
+ ,
)
-
- it('should call onOpen when the anchor is clicked', async () => {
- const mockOpenCallback = vi.fn()
- const mockCloseCallback = vi.fn()
- const anchoredOverlay = render(
- ,
- )
- const anchor = anchoredOverlay.baseElement.querySelector('[aria-haspopup="true"]')!
- await act(async () => {
- await userEvent.click(anchor)
- })
-
- expect(mockOpenCallback).toHaveBeenCalledTimes(1)
- expect(mockOpenCallback).toHaveBeenCalledWith('anchor-click')
- expect(mockCloseCallback).toHaveBeenCalledTimes(0)
- })
-
- it('should call onOpen when the anchor activated by a key press', async () => {
- const mockOpenCallback = vi.fn()
- const mockCloseCallback = vi.fn()
- const anchoredOverlay = render(
- ,
- )
- const anchor = anchoredOverlay.baseElement.querySelector('[aria-haspopup="true"]')!
- await act(async () => {
- await userEvent.type(anchor, '{Space}')
- })
-
- expect(mockOpenCallback).toHaveBeenCalledTimes(1)
- expect(mockOpenCallback).toHaveBeenCalledWith('anchor-key-press')
- expect(mockCloseCallback).toHaveBeenCalledTimes(0)
- })
-
- it('should call onClose when the user clicks off of the overlay', async () => {
- const mockOpenCallback = vi.fn()
- const mockCloseCallback = vi.fn()
- const anchoredOverlay = render(
- ,
- )
- await act(async () => {
- await userEvent.click(anchoredOverlay.baseElement)
- })
-
- expect(mockOpenCallback).toHaveBeenCalledTimes(0)
- expect(mockCloseCallback).toHaveBeenCalledTimes(1)
- expect(mockCloseCallback).toHaveBeenCalledWith('click-outside')
- })
-
- it('should call onClose when the escape key is pressed', async () => {
- const mockOpenCallback = vi.fn()
- const mockCloseCallback = vi.fn()
-
- render(
- ,
- )
-
- await act(async () => {
- await userEvent.keyboard('{Escape}')
- })
-
- expect(mockOpenCallback).toHaveBeenCalledTimes(0)
- expect(mockCloseCallback).toHaveBeenCalledTimes(1)
- expect(mockCloseCallback).toHaveBeenCalledWith('escape')
- })
-
- it('should call onPositionChange when provided', async () => {
- const mockPositionChangeCallback = vi.fn(({position}: {position: AnchorPosition}) => position)
- render(
- ,
- )
-
- await act(async () => {
- await userEvent.keyboard('{Escape}')
- })
-
- expect(mockPositionChangeCallback).toHaveBeenCalled()
- expect(mockPositionChangeCallback).toHaveBeenCalledWith({
- position: {
- anchorAlign: 'start',
- anchorSide: 'outside-bottom',
- left: 0,
- top: 36,
- },
- })
- })
-
- it('should support a `ref` through `overlayProps` on the overlay element', () => {
- const ref = createRef()
-
- function Test() {
- const anchorRef = useRef(null)
- return (
-
- {
- return (
-
- )
- }}
- >
- content
-
-
- )
- }
-
- render()
-
- expect(document.getElementById('overlay')).toBe(ref.current)
- })
- },
-)
-
-describe('AnchoredOverlay feature flag specific behavior', () => {
- describe('with primer_react_css_anchor_positioning feature flag enabled', () => {
- it('should render wrapper div when flag is enabled', () => {
- const {container} = render(
-
-
- ,
- )
-
- const wrapper = container.querySelector(`.${anchoredOverlayClasses.Wrapper}`)
- expect(wrapper).toBeInTheDocument()
+ const anchor = anchoredOverlay.baseElement.querySelector('[aria-haspopup="true"]')!
+ await act(async () => {
+ await userEvent.click(anchor)
})
- it('should apply Anchor class to anchor element when flag is enabled', () => {
- const {container} = render(
-
-
- ,
- )
+ expect(mockOpenCallback).toHaveBeenCalledTimes(1)
+ expect(mockOpenCallback).toHaveBeenCalledWith('anchor-click')
+ expect(mockCloseCallback).toHaveBeenCalledTimes(0)
+ })
- const anchor = container.querySelector('[aria-haspopup="true"]')
- expect(anchor).toHaveClass(anchoredOverlayClasses.Anchor)
+ it('should call onOpen when the anchor activated by a key press', async () => {
+ const mockOpenCallback = vi.fn()
+ const mockCloseCallback = vi.fn()
+ const anchoredOverlay = render(
+ ,
+ )
+ const anchor = anchoredOverlay.baseElement.querySelector('[aria-haspopup="true"]')!
+ await act(async () => {
+ await userEvent.type(anchor, '{Space}')
})
- it('should render overlay as visible immediately when flag is enabled', () => {
- const {baseElement} = render(
-
-
- ,
- )
+ expect(mockOpenCallback).toHaveBeenCalledTimes(1)
+ expect(mockOpenCallback).toHaveBeenCalledWith('anchor-key-press')
+ expect(mockCloseCallback).toHaveBeenCalledTimes(0)
+ })
- const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlay).toHaveAttribute('data-visibility-visible', '')
+ it('should call onClose when the user clicks off of the overlay', async () => {
+ const mockOpenCallback = vi.fn()
+ const mockCloseCallback = vi.fn()
+ const anchoredOverlay = render(
+ ,
+ )
+ await act(async () => {
+ await userEvent.click(anchoredOverlay.baseElement)
})
- it('should not use portal when flag is enabled', () => {
- const {baseElement, container} = render(
-
-
- ,
- )
+ expect(mockOpenCallback).toHaveBeenCalledTimes(0)
+ expect(mockCloseCallback).toHaveBeenCalledTimes(1)
+ expect(mockCloseCallback).toHaveBeenCalledWith('click-outside')
+ })
- // The overlay should be inside the component tree, not in the portal root
- const portalRoot = baseElement.querySelector('#__primerPortalRoot__')
- const overlayInPortal = portalRoot?.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlayInPortal).toBeNull()
+ it('should call onClose when the escape key is pressed', async () => {
+ const mockOpenCallback = vi.fn()
+ const mockCloseCallback = vi.fn()
- // The overlay should be inside the wrapper
- const wrapper = container.querySelector(`.${anchoredOverlayClasses.Wrapper}`)
- const overlayInWrapper = wrapper?.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlayInWrapper).toBeInTheDocument()
- })
-
- it('should apply AnchoredOverlay class to overlay when flag is enabled', () => {
- const {baseElement} = render(
-
-
- ,
- )
+ render(
+ ,
+ )
- const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlay).toHaveClass(anchoredOverlayClasses.AnchoredOverlay)
+ await act(async () => {
+ await userEvent.keyboard('{Escape}')
})
- it('should set data-anchor-position attribute when flag is enabled', () => {
- const {baseElement} = render(
-
-
- ,
- )
-
- const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlay).toHaveAttribute('data-anchor-position', 'true')
- })
+ expect(mockOpenCallback).toHaveBeenCalledTimes(0)
+ expect(mockCloseCallback).toHaveBeenCalledTimes(1)
+ expect(mockCloseCallback).toHaveBeenCalledWith('escape')
})
- describe('with primer_react_css_anchor_positioning feature flag disabled', () => {
- it('should not render wrapper div when flag is disabled', () => {
- const {container} = render(
-
-
- ,
- )
+ it('should call onPositionChange when provided', async () => {
+ const mockPositionChangeCallback = vi.fn(({position}: {position: AnchorPosition}) => position)
+ render()
- const wrapper = container.querySelector(`.${anchoredOverlayClasses.Wrapper}`)
- expect(wrapper).not.toBeInTheDocument()
+ await act(async () => {
+ await userEvent.keyboard('{Escape}')
})
- it('should not apply Anchor class to anchor element when flag is disabled', () => {
- const {container} = render(
-
-
- ,
- )
-
- const anchor = container.querySelector('[aria-haspopup="true"]')
- expect(anchor).not.toHaveClass(anchoredOverlayClasses.Anchor)
+ expect(mockPositionChangeCallback).toHaveBeenCalled()
+ expect(mockPositionChangeCallback).toHaveBeenCalledWith({
+ position: {
+ anchorAlign: 'start',
+ anchorSide: 'outside-bottom',
+ left: 0,
+ top: 36,
+ },
})
+ })
- it('should use portal when flag is disabled', () => {
- const {baseElement} = render(
-
-
- ,
+ it('should support a `ref` through `overlayProps` on the overlay element', () => {
+ const ref = createRef()
+
+ function Test() {
+ const anchorRef = useRef(null)
+ return (
+ {
+ return (
+
+ )
+ }}
+ >
+ content
+
)
+ }
- // The overlay should be inside the portal root
- const portalRoot = baseElement.querySelector('#__primerPortalRoot__')
- const overlayInPortal = portalRoot?.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlayInPortal).toBeInTheDocument()
- })
+ render()
- it('should set data-anchor-position to false when flag is disabled', () => {
- const {baseElement} = render(
-
-
- ,
- )
-
- const overlay = baseElement.querySelector('[data-component="AnchoredOverlay"]')
- expect(overlay).toHaveAttribute('data-anchor-position', 'false')
- })
+ expect(document.getElementById('overlay')).toBe(ref.current)
})
})
diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx
index 3f9513f94f9..f60070447b3 100644
--- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx
+++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx
@@ -14,7 +14,6 @@ import {IconButton, type IconButtonProps} from '../Button'
import {XIcon} from '@primer/octicons-react'
import classes from './AnchoredOverlay.module.css'
import {clsx} from 'clsx'
-import {useFeatureFlag} from '../FeatureFlags'
interface AnchoredOverlayPropsWithAnchor {
/**
@@ -124,13 +123,6 @@ export type AnchoredOverlayProps = AnchoredOverlayBaseProps &
(AnchoredOverlayPropsWithAnchor | AnchoredOverlayPropsWithoutAnchor) &
Partial>
-const applyAnchorPositioningPolyfill = async () => {
- if (typeof window !== 'undefined' && !('anchorName' in document.documentElement.style)) {
- const {default: polyfill} = await import('@oddbird/css-anchor-positioning/fn')
- polyfill()
- }
-}
-
const defaultVariant = {
regular: 'anchored',
narrow: 'anchored',
@@ -168,7 +160,6 @@ export const AnchoredOverlay: React.FC {
- const cssAnchorPositioning = useFeatureFlag('primer_react_css_anchor_positioning')
const anchorRef = useProvidedRefOrCreate(externalAnchorRef)
const [overlayRef, updateOverlayRef] = useRenderForcingRef()
const anchorId = useId(externalAnchorId)
@@ -227,11 +218,7 @@ export const AnchoredOverlay: React.FC
{renderAnchor &&
renderAnchor({
@@ -257,7 +242,6 @@ export const AnchoredOverlay: React.FC {
if (overlayProps?.ref) {
assignRef(overlayProps.ref, node)
}
updateOverlayRef(node)
}}
- data-anchor-position={cssAnchorPositioning}
- data-side={cssAnchorPositioning ? side : position?.anchorSide}
>
{showXIcon ? (
@@ -309,12 +291,6 @@ export const AnchoredOverlay: React.FC
)
-
- if (cssAnchorPositioning) {
- return
{innerContent}
- }
-
- return innerContent
}
function assignRef
(
diff --git a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts
index e3d5fd0d975..796448887e8 100644
--- a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts
+++ b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts
@@ -2,7 +2,6 @@ import {FeatureFlagScope} from './FeatureFlagScope'
export const DefaultFeatureFlags = FeatureFlagScope.create({
primer_react_breadcrumbs_overflow_menu: false,
- primer_react_css_anchor_positioning: false,
primer_react_css_has_selector_perf: false,
primer_react_select_panel_fullscreen_on_narrow: false,
primer_react_select_panel_order_selected_at_top: false,
diff --git a/packages/react/src/Overlay/Overlay.module.css b/packages/react/src/Overlay/Overlay.module.css
index 232058eb107..497c5a412f1 100644
--- a/packages/react/src/Overlay/Overlay.module.css
+++ b/packages/react/src/Overlay/Overlay.module.css
@@ -19,29 +19,14 @@
background-color: var(--overlay-bgColor);
border-radius: var(--borderRadius-large);
box-shadow: var(--shadow-floating-small);
-
- &[data-anchor-position='false'],
- &:not([data-anchor-position]):not([data-variant='modal']) {
- /* stylelint-disable-next-line primer/spacing */
- right: var(--right, auto);
- /* stylelint-disable-next-line primer/spacing */
- bottom: var(--bottom, auto);
-
- /* stylelint-disable-next-line selector-max-specificity */
- &[data-responsive='fullscreen']:not([data-variant]) {
- @media screen and (--viewportRange-narrow) {
- top: 0;
- left: 0;
- }
- }
-
- &:not([data-variant]) {
- /* stylelint-disable-next-line primer/spacing */
- top: var(--top, auto);
- /* stylelint-disable-next-line primer/spacing */
- left: var(--left, auto);
- }
- }
+ /* stylelint-disable-next-line primer/spacing */
+ top: var(--top, auto);
+ /* stylelint-disable-next-line primer/spacing */
+ left: var(--left, auto);
+ /* stylelint-disable-next-line primer/spacing */
+ right: var(--right, auto);
+ /* stylelint-disable-next-line primer/spacing */
+ bottom: var(--bottom, auto);
&:focus {
outline: none;
diff --git a/packages/react/src/Overlay/Overlay.tsx b/packages/react/src/Overlay/Overlay.tsx
index 6db7e9debac..362083945e2 100644
--- a/packages/react/src/Overlay/Overlay.tsx
+++ b/packages/react/src/Overlay/Overlay.tsx
@@ -10,7 +10,6 @@ import type {AnchorSide} from '@primer/behaviors'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
import classes from './Overlay.module.css'
import {clsx} from 'clsx'
-import {useFeatureFlag} from '../FeatureFlags'
type StyledOverlayProps = {
width?: keyof typeof widthMap
@@ -190,7 +189,6 @@ const Overlay = React.forwardRef(
forwardedRef,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): ReactElement => {
- const cssAnchorPositioning = useFeatureFlag('primer_react_css_anchor_positioning')
const overlayRef = useRef(null)
useRefObjectAsForwardedRef(forwardedRef, overlayRef)
const slideAnimationDistance = 8 // var(--base-size-8), hardcoded to do some math
@@ -231,26 +229,22 @@ const Overlay = React.forwardRef(
// To be backwards compatible with the old Overlay, we need to set the left prop if x-position is not specified
const leftPosition = left === undefined && right === undefined ? 0 : left
- const overlayContent = (
-
+ return (
+
+
+
)
-
- if (cssAnchorPositioning) {
- return overlayContent
- }
-
- return {overlayContent}
},
) as PolymorphicForwardRefComponent<'div', internalOverlayProps>