import * as React from 'react' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { describe, expect, it, vi } from 'vitest' import { axe } from 'vitest-axe' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './collapsible' function Basic({ defaultOpen, disabled, keepMounted, }: { defaultOpen?: boolean; disabled?: boolean; keepMounted?: boolean } = {}) { return ( Toggle

Panel body

) } describe('Collapsible', () => { it('renders trigger with data-slot', () => { render() const trigger = screen.getByRole('button', { name: 'Toggle' }) expect(trigger).toHaveAttribute('data-slot', 'collapsible-trigger') }) it('defaults closed: panel not in the DOM', () => { render() expect(screen.queryByText('Panel body')).toBeNull() }) it('opens on trigger click and closes on a second click', async () => { const user = userEvent.setup() render() const trigger = screen.getByRole('button', { name: 'Toggle' }) await user.click(trigger) const panel = await screen.findByText('Panel body') expect(panel.closest('[data-slot="collapsible-content"]')).not.toBeNull() expect(trigger).toHaveAttribute('data-panel-open') await user.click(trigger) await waitFor(() => { expect(screen.queryByText('Panel body')).toBeNull() }) }) it('respects defaultOpen', () => { render() expect(screen.getByText('Panel body')).toBeInTheDocument() }) it('disabled prevents toggling', async () => { const user = userEvent.setup() render() const trigger = screen.getByRole('button', { name: 'Toggle' }) expect(trigger.getAttribute('data-disabled')).not.toBeNull() await user.click(trigger) expect(screen.queryByText('Panel body')).toBeNull() }) it('keepMounted leaves the panel in the DOM when closed with data-closed', () => { render() const panel = screen.getByText('Panel body').closest('[data-slot="collapsible-content"]') as HTMLElement expect(panel).not.toBeNull() expect(panel.getAttribute('data-closed')).not.toBeNull() }) it('controlled mode: onOpenChange fires with the next open state', async () => { const user = userEvent.setup() const onOpenChange = vi.fn() function Controlled() { const [open, setOpen] = React.useState(false) return ( { onOpenChange(next, details) setOpen(next) }} > Toggle

Panel body

) } render() await user.click(screen.getByRole('button', { name: 'Toggle' })) expect(onOpenChange).toHaveBeenCalledTimes(1) expect(onOpenChange).toHaveBeenLastCalledWith(true, expect.anything()) await screen.findByText('Panel body') }) it('forwards className on each part', async () => { const user = userEvent.setup() render( Toggle

Panel body

, ) const trigger = screen.getByRole('button', { name: 'Toggle' }) expect(trigger.className).toContain('trigger-cls') expect(trigger.className).toContain('cursor-pointer') await user.click(trigger) const panel = (await screen.findByText('Panel body')).closest('[data-slot="collapsible-content"]') as HTMLElement expect(panel.className).toContain('content-cls') expect(panel.className).toContain('overflow-hidden') }) it('has no a11y violations when open', async () => { const { container } = render() expect(await axe(container)).toHaveNoViolations() }) })