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
46 changes: 35 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
"turbo": "^2.6.3",
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.1",
"vitest": "^4.1.5"
"vitest": "^4.1.5",
"vitest-fail-on-console": "^0.10.1"
},
"overrides": {
"zod-validation-error": "^4.0.0"
Expand Down
1 change: 1 addition & 0 deletions packages/doc-gen/config/vitest/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@primer/vitest-config/setup'
1 change: 1 addition & 0 deletions packages/doc-gen/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export default defineConfig({
},
test: {
environment: 'node',
setupFiles: ['config/vitest/setup.js'],
},
})
1 change: 1 addition & 0 deletions packages/postcss-preset-primer/config/vitest/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@primer/vitest-config/setup'
1 change: 1 addition & 0 deletions packages/postcss-preset-primer/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import {defineConfig} from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
setupFiles: ['config/vitest/setup.js'],
},
})
1 change: 1 addition & 0 deletions packages/react/config/vitest/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@primer/vitest-config/setup'
57 changes: 47 additions & 10 deletions packages/react/src/ActionBar/ActionBar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {describe, expect, it, afterEach, vi} from 'vitest'
import {render, screen, act} from '@testing-library/react'
import {render, screen, act, waitFor} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import React, {createRef, useState} from 'react'
import ActionBar from './'
Expand Down Expand Up @@ -82,6 +82,10 @@ describe('ActionBar', () => {
})
})

const waitForActionBarEffects = async () => {
await act(async () => {})
}

describe('ActionBar Registry System', () => {
it('should preserve order with deep nesting', () => {
render(
Expand Down Expand Up @@ -221,7 +225,9 @@ describe('ActionBar Registry System', () => {
await user.click(screen.getByText('Increment'))
}

expect(screen.getByRole('button', {name: 'Button 10'})).toBeInTheDocument()
await waitFor(() => {
expect(screen.getByRole('button', {name: 'Button 10'})).toBeInTheDocument()
})
})

it('should handle zero-width scenarios gracefully', () => {
Expand Down Expand Up @@ -356,7 +362,9 @@ describe('ActionBar.Menu returnFocusRef', () => {

// Verify focus is returned to the returnFocusRef element
const returnFocusTarget = screen.getByTestId('return-focus-target')
expect(document.activeElement).toEqual(returnFocusTarget)
await waitFor(() => {
expect(document.activeElement).toEqual(returnFocusTarget)
})
})

it('returns focus to returnFocusRef when menu item is selected', async () => {
Expand Down Expand Up @@ -390,7 +398,9 @@ describe('ActionBar.Menu returnFocusRef', () => {

// Verify focus is returned to the returnFocusRef element
const returnFocusTarget = screen.getByTestId('return-focus-target')
expect(document.activeElement).toEqual(returnFocusTarget)
await waitFor(() => {
expect(document.activeElement).toEqual(returnFocusTarget)
})
})

it('returns focus to anchor button when returnFocusRef is not provided', async () => {
Expand All @@ -414,34 +424,46 @@ describe('ActionBar.Menu returnFocusRef', () => {
await user.keyboard('{Escape}')

// Verify focus returns to the menu button (default behavior)
expect(document.activeElement).toEqual(menuButton)
await waitFor(() => {
expect(document.activeElement).toEqual(menuButton)
})
})
})

describe('ActionBar data-component attributes', () => {
it('renders ActionBar with data-component attribute', () => {
it('renders ActionBar with data-component attribute', async () => {
const {container} = render(
<ActionBar aria-label="Toolbar">
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold" />
</ActionBar>,
)

await waitFor(() => {
expect(screen.getByRole('toolbar')).toBeInTheDocument()
})
await waitForActionBarEffects()

const actionBar = container.querySelector('[data-component="ActionBar"]')
expect(actionBar).toBeInTheDocument()
})

it('renders ActionBar.IconButton with data-component attribute', () => {
it('renders ActionBar.IconButton with data-component attribute', async () => {
const {container} = render(
<ActionBar aria-label="Toolbar">
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold" />
</ActionBar>,
)

await waitFor(() => {
expect(screen.getByRole('toolbar')).toBeInTheDocument()
})
await waitForActionBarEffects()

const iconButton = container.querySelector('[data-component="ActionBar"] [data-component="IconButton"]')
expect(iconButton).toBeInTheDocument()
})

it('renders ActionBar.VerticalDivider with data-component attribute', () => {
it('renders ActionBar.VerticalDivider with data-component attribute', async () => {
const {container} = render(
<ActionBar aria-label="Toolbar">
<ActionBar.IconButton icon={BoldIcon} aria-label="Bold" />
Expand All @@ -450,11 +472,16 @@ describe('ActionBar data-component attributes', () => {
</ActionBar>,
)

await waitFor(() => {
expect(screen.getByRole('toolbar')).toBeInTheDocument()
})
await waitForActionBarEffects()

const divider = container.querySelector('[data-component="ActionBar.VerticalDivider"]')
expect(divider).toBeInTheDocument()
})

it('renders ActionBar.Group with data-component attribute', () => {
it('renders ActionBar.Group with data-component attribute', async () => {
const {container} = render(
<ActionBar aria-label="Toolbar">
<ActionBar.Group>
Expand All @@ -464,17 +491,27 @@ describe('ActionBar data-component attributes', () => {
</ActionBar>,
)

await waitFor(() => {
expect(screen.getByRole('toolbar')).toBeInTheDocument()
})
await waitForActionBarEffects()

const group = container.querySelector('[data-component="ActionBar.Group"]')
expect(group).toBeInTheDocument()
})

it('renders ActionBar.Menu.IconButton with data-component attribute', () => {
it('renders ActionBar.Menu.IconButton with data-component attribute', async () => {
render(
<ActionBar aria-label="Toolbar">
<ActionBar.Menu aria-label="More options" icon={BoldIcon} items={[{label: 'Option 1', onClick: vi.fn()}]} />
</ActionBar>,
)

await waitFor(() => {
expect(screen.getByRole('toolbar')).toBeInTheDocument()
})
await waitForActionBarEffects()

const menuButton = screen.getByRole('button', {name: 'More options'})
expect(menuButton).toHaveAttribute('data-component', 'ActionBar.Menu.IconButton')
})
Expand Down
20 changes: 9 additions & 11 deletions packages/react/src/ActionMenu/ActionMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,7 @@ describe('ActionMenu', () => {
const button = component.getByRole('button')

const user = userEvent.setup()
await act(async () => {
await user.click(button)
})
await user.click(button)

expect(component.queryByRole('menu')).toBeInTheDocument()
const menuItems = component.getAllByRole('menuitem')
Expand All @@ -355,13 +353,11 @@ describe('ActionMenu', () => {
await user.keyboard('{ArrowDown}')
expect(menuItems[1]).toEqual(document.activeElement)

await act(async () => {
// TODO: Removed one ArrowDown to account for the focus trap starting at the second element
// await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
})
// TODO: Removed one ArrowDown to account for the focus trap starting at the second element
// await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
await user.keyboard('{ArrowDown}')
expect(menuItems[menuItems.length - 1]).toEqual(document.activeElement) // last elememt

await user.keyboard('{ArrowDown}')
Expand Down Expand Up @@ -796,7 +792,9 @@ describe('ActionMenu', () => {

// The new anchor should have the same anchor-name re-applied, and the
// overlay should still reference it via position-anchor.
expect(newAnchor.style.getPropertyValue('anchor-name')).toBe(initialAnchorName)
await waitFor(() => {
expect(newAnchor.style.getPropertyValue('anchor-name')).toBe(initialAnchorName)
})
expect(overlay.style.getPropertyValue('position-anchor')).toBe(initialPositionAnchor)
})
})
Expand Down
8 changes: 5 additions & 3 deletions packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {act, createRef, useCallback, useRef, useState} from 'react'
import {describe, expect, it, vi} from 'vitest'
import {render} from '@testing-library/react'
import {render, waitFor} from '@testing-library/react'
import {userEvent} from 'vitest/browser'
import {AnchoredOverlay} from '../AnchoredOverlay'
import {Button} from '../Button'
Expand Down Expand Up @@ -577,7 +577,7 @@ describe('AnchoredOverlay CSS anchor positioning viewport handling', () => {
})

describe('AnchoredOverlay anchor element replacement', () => {
it('should re-apply anchor-name to a new anchor DOM element when the overlay reopens', () => {
it('should re-apply anchor-name to a new anchor DOM element when the overlay reopens', async () => {
function TestComponent() {
const anchorRef = useRef<HTMLButtonElement>(null)
const [open, setOpen] = useState(true)
Expand Down Expand Up @@ -633,6 +633,8 @@ describe('AnchoredOverlay anchor element replacement', () => {

const newAnchor = baseElement.querySelector('[data-testid="anchor"]') as HTMLElement
expect(newAnchor).not.toBe(initialAnchor)
expect(newAnchor.style.getPropertyValue('anchor-name')).toBe(anchorName)
await waitFor(() => {
expect(newAnchor.style.getPropertyValue('anchor-name')).toBe(anchorName)
})
})
})
Loading