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( Deploy , ) 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( Deploy , ) 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(
body only
, ) expect(screen.getByRole('dialog')).not.toHaveAttribute('aria-labelledby') }) it('wires aria-describedby only when a description is rendered', () => { render( Deploy To production , ) 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 ( <> Deploy ) } 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( Deploy , ) expect(screen.getByRole('dialog')).toBeInTheDocument() fireEvent.keyDown(document, { key: 'Escape' }) expect(screen.queryByRole('dialog')).toBeNull() }) it('closes on backdrop click', () => { render( Deploy , ) 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( Deploy , ) expect(document.body.style.overflow).toBe('hidden') rerender( Deploy , ) 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('') }) })