Skip to content
Draft
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/remove-css-anchor-positioning-flag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

AnchoredOverlay: Remove the `primer_react_css_anchor_positioning` feature flag. CSS anchor positioning is now enabled by default when the browser supports it natively (and no `portalContainerName` is provided), so consumers no longer need to opt in via the feature flag.
12 changes: 0 additions & 12 deletions .github/skills/feature-flags/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,6 @@ function MyOverlay() {

## Real Codebase Examples

### CSS anchor positioning (`packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx`)

```tsx
const cssAnchorPositioning = useFeatureFlag('primer_react_css_anchor_positioning')
useEffect(() => {
if (cssAnchorPositioning && !hasLoadedAnchorPositioningPolyfill.current) {
applyAnchorPositioningPolyfill()
hasLoadedAnchorPositioningPolyfill.current = true
}
}, [open, overlayRef, updateOverlayRef, cssAnchorPositioning])
```

### Breadcrumbs overflow (`packages/react/src/Breadcrumbs/Breadcrumbs.tsx`)

```tsx
Expand Down
48 changes: 19 additions & 29 deletions e2e/components/ActionMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,44 +65,34 @@ const stories: Array<{
},
] as const

const featureFlagVariants = [
{flagEnabled: false, suffix: ''},
{flagEnabled: true, suffix: '.css-anchor-positioning'},
] as const

test.describe('ActionMenu', () => {
for (const story of stories) {
test.describe(story.title, () => {
for (const theme of themes) {
test.describe(theme, () => {
for (const {flagEnabled, suffix} of featureFlagVariants) {
test(`default @vrt${suffix}`, async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
featureFlags: {
primer_react_css_anchor_positioning: flagEnabled,
},
},
})
test(`default @vrt`, async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})

const buttonNames = story.buttonNames ?? [story.buttonName ?? 'Open menu']
const buttonNames = story.buttonNames ?? [story.buttonName ?? 'Open menu']

// Default state
// Open state
// Default state
// Open state

if (!story.skipOpen) {
for (const buttonName of buttonNames) {
await page.locator('button', {hasText: buttonName}).waitFor()
await page.getByRole('button', {name: buttonName}).click()
}
if (!story.skipOpen) {
for (const buttonName of buttonNames) {
await page.locator('button', {hasText: buttonName}).waitFor()
await page.getByRole('button', {name: buttonName}).click()
}
expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(
`ActionMenu.${story.title}.${theme}${suffix}.png`,
)
})
}
}
expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(
`ActionMenu.${story.title}.${theme}.png`,
)
})
})
}
})
Expand Down
101 changes: 46 additions & 55 deletions e2e/components/AnchoredOverlay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,71 +126,62 @@ const theme = 'light'
test.describe('AnchoredOverlay', () => {
for (const story of stories) {
test.describe(story.title, () => {
for (const withCSSAnchorPositioning of [false, true]) {
const namePostfix = withCSSAnchorPositioning ? '.css-anchor-positioning' : ''
test(`default @vrt`, async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})

test(`default @vrt${namePostfix ? ` ${namePostfix}` : ''}`, async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
...(withCSSAnchorPositioning && {
featureFlags: {
primer_react_css_anchor_positioning: true,
},
}),
},
if (story.viewport) {
await page.setViewportSize({
width: viewports[story.viewport],
height: 667,
})
}

if (story.viewport) {
await page.setViewportSize({
width: viewports[story.viewport],
height: 667,
})
}

// Open dialog if needed
if (story.openDialog) {
await page.getByRole('button', {name: 'Open Dialog'}).click()
}

// Open nested dialog if needed
if (story.openNestedDialog) {
await page.getByRole('button', {name: 'Open Inner Dialog'}).click()
}

// If the story has multiple overlays, screenshot each one individually
if (story.buttonNames) {
for (const name of story.buttonNames) {
await page.locator('button', {hasText: name}).first().waitFor()
const btn = page.getByRole('button', {name}).first()
await btn.click()
await waitForImages(page)

expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(
`AnchoredOverlay.${story.title}.${name}.${theme}${namePostfix}.png`,
)
// Open dialog if needed
if (story.openDialog) {
await page.getByRole('button', {name: 'Open Dialog'}).click()
}

// Close the overlay before opening the next one
await btn.click()
}
} else {
// Open the overlay
const buttonName = story.buttonName ?? 'Button'
await page.locator('button', {hasText: buttonName}).first().waitFor()
const overlayButton = page.getByRole('button', {name: buttonName}).first()
await overlayButton.click()
// Open nested dialog if needed
if (story.openNestedDialog) {
await page.getByRole('button', {name: 'Open Inner Dialog'}).click()
}

// for the dev stories, we intentionally change the content after the overlay is open to test that it repositions correctly
if (story.waitForText) await page.getByText(story.waitForText).waitFor()
// If the story has multiple overlays, screenshot each one individually
if (story.buttonNames) {
for (const name of story.buttonNames) {
await page.locator('button', {hasText: name}).first().waitFor()
const btn = page.getByRole('button', {name}).first()
await btn.click()
await waitForImages(page)

expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(
`AnchoredOverlay.${story.title}.${theme}${namePostfix}.png`,
`AnchoredOverlay.${story.title}.${name}.${theme}.png`,
)

// Close the overlay before opening the next one
await btn.click()
}
})
}
} else {
// Open the overlay
const buttonName = story.buttonName ?? 'Button'
await page.locator('button', {hasText: buttonName}).first().waitFor()
const overlayButton = page.getByRole('button', {name: buttonName}).first()
await overlayButton.click()

// for the dev stories, we intentionally change the content after the overlay is open to test that it repositions correctly
if (story.waitForText) await page.getByText(story.waitForText).waitFor()
await waitForImages(page)

expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(
`AnchoredOverlay.${story.title}.${theme}.png`,
)
}
})
})
}
})
Loading
Loading