import { fireEvent, render, screen } from '@testing-library/react'
import { useState } from 'react'
import { describe, expect, it } from 'vitest'
import { Dialog, DialogContent, DialogDescription, DialogTitle } from '../src/components/ui/Dialog'
import { Sheet, SheetClose, SheetContent, SheetTitle } from '../src/components/ui/Sheet'
import { SearchPalette } from '../src/components/SearchPalette'
describe('Dialog (Overlay primitive)', () => {
it('renders a modal dialog with the panel focused when open', () => {
render(
,
)
const panel = screen.getByRole('dialog')
expect(panel).toHaveClass('dialog-panel')
expect(panel).toHaveAttribute('aria-modal', 'true')
expect(panel).toHaveFocus()
})
it('wires aria-labelledby only when a title is rendered', () => {
const { rerender } = render(
,
)
const panel = screen.getByRole('dialog')
const labelledBy = panel.getAttribute('aria-labelledby')
expect(labelledBy).toBeTruthy()
expect(document.getElementById(labelledBy!)).toHaveTextContent('Deploy')
// No DialogTitle → a dangling IDREF would announce nothing.
rerender(
,
)
expect(screen.getByRole('dialog')).not.toHaveAttribute('aria-labelledby')
})
it('wires aria-describedby only when a description is rendered', () => {
render(
,
)
const describedBy = screen.getByRole('dialog').getAttribute('aria-describedby')
expect(describedBy).toBeTruthy()
expect(document.getElementById(describedBy!)).toHaveTextContent('To production')
})
it('restores focus to the trigger after closing', () => {
function Controlled() {
const [open, setOpen] = useState(false)
return (
<>
>
)
}
render()
const trigger = screen.getByRole('button', { name: 'Open' })
// jsdom does not move focus on synthetic clicks — focus the trigger like a real user would.
trigger.focus()
fireEvent.click(trigger)
expect(screen.getByRole('dialog')).toHaveFocus()
fireEvent.keyDown(document, { key: 'Escape' })
expect(screen.queryByRole('dialog')).toBeNull()
expect(trigger).toHaveFocus()
})
it('closes on Escape when open', () => {
render(
,
)
expect(screen.getByRole('dialog')).toBeInTheDocument()
fireEvent.keyDown(document, { key: 'Escape' })
expect(screen.queryByRole('dialog')).toBeNull()
})
it('closes on backdrop click', () => {
render(
,
)
fireEvent.click(screen.getByRole('dialog').parentElement!)
expect(screen.queryByRole('dialog')).toBeNull()
})
it('locks body scroll while open and restores it on close', () => {
const { rerender } = render(
,
)
expect(document.body.style.overflow).toBe('hidden')
rerender(
,
)
expect(document.body.style.overflow).not.toBe('hidden')
})
})
describe('Sheet (Overlay skin)', () => {
it('renders with sheet classes and its own close label', () => {
render(
Filters
Done
,
)
const panel = screen.getByRole('dialog')
expect(panel).toHaveClass('sheet-panel')
expect(screen.getByRole('button', { name: 'Close sheet' })).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'Done' }))
expect(screen.queryByRole('dialog')).toBeNull()
})
it('keeps Tab focus inside the panel (wrap from last to first)', () => {
render(
Filters
,
)
const panel = screen.getByRole('dialog')
const closeBtn = screen.getByRole('button', { name: 'Close sheet' })
const last = screen.getByRole('button', { name: 'Only control' })
// Tab from the last control wraps to the first.
last.focus()
fireEvent.keyDown(panel, { key: 'Tab' })
expect(closeBtn).toHaveFocus()
// Shift+Tab from the first control wraps to the last.
fireEvent.keyDown(panel, { key: 'Tab', shiftKey: true })
expect(last).toHaveFocus()
})
})
describe('SearchPalette (Overlay behavior)', () => {
it('renders nothing while closed and no backdrop is left over the page', () => {
const { rerender } = render(
{}} onSelect={() => {}} />,
)
expect(screen.queryByRole('dialog')).toBeNull()
expect(document.querySelector('.search-palette-backdrop')).toBeNull()
expect(document.body.style.overflow).toBe('')
rerender( {}} onSelect={() => {}} />)
expect(screen.getByRole('dialog')).toHaveClass('search-palette-modal')
})
it('closes on Escape / backdrop click and unlocks body scroll', () => {
function Controlled() {
const [open, setOpen] = useState(true)
return (
<>
setOpen(false)} onSelect={() => {}} />
>
)
}
const { container } = render()
expect(document.body.style.overflow).toBe('hidden')
fireEvent.keyDown(window, { key: 'Escape' })
expect(screen.queryByRole('dialog')).toBeNull()
expect(document.body.style.overflow).toBe('')
fireEvent.click(screen.getByRole('button', { name: 'Open' }))
expect(screen.getByRole('dialog')).toBeInTheDocument()
fireEvent.click(container.querySelector('.search-palette-backdrop')!)
expect(screen.queryByRole('dialog')).toBeNull()
expect(document.body.style.overflow).toBe('')
})
})