// @vitest-environment jsdom /** * What the page claims: the title toggles by click and by key and says its * state; the rows are hidden, not removed, while closed; a `storageKey` is * read on mount and written on change. The slide is Chrome's to show * (measured 2026-09-09). */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { cleanup, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { CollapsibleSection } from './CollapsibleSection' import { MenuItem } from './Menu' afterEach(cleanup) beforeEach(() => window.localStorage.clear()) const rows = ( <> Item one Item two ) const title = () => screen.getByRole('button', { name: 'Section' }) describe('CollapsibleSection', () => { it('is open by default: the title says so, and the rows are there', () => { render({rows}) expect(title().getAttribute('aria-expanded')).toBe('true') expect(screen.getByText('Item one').closest('[hidden]')).toBeNull() }) it('a click on the title closes it, and the rows are hidden rather than gone', async () => { render({rows}) await userEvent.click(title()) expect(title().getAttribute('aria-expanded')).toBe('false') expect(screen.getByText('Item one').closest('[hidden]')).not.toBeNull() }) it('Enter and Space on the title toggle it', async () => { render({rows}) title().focus() await userEvent.keyboard('{Enter}') expect(title().getAttribute('aria-expanded')).toBe('false') await userEvent.keyboard(' ') expect(title().getAttribute('aria-expanded')).toBe('true') }) it('starts closed with defaultOpen false', () => { render({rows}) expect(title().getAttribute('aria-expanded')).toBe('false') }) it('an action beside the title acts without toggling', async () => { const add = vi.fn() render( , tooltip: 'Add', onClick: add }]}> {rows} , ) await userEvent.click(screen.getByRole('button', { name: 'Add' })) expect(add).toHaveBeenCalledTimes(1) expect(title().getAttribute('aria-expanded')).toBe('true') }) it('remembers under storageKey: written on change, read on mount', async () => { const { unmount } = render({rows}) await userEvent.click(title()) expect(window.localStorage.getItem('test.section')).toBe('closed') unmount() render({rows}) expect(title().getAttribute('aria-expanded')).toBe('false') }) it('the caller can own the state', async () => { const onOpenChange = vi.fn() render({rows}) await userEvent.click(title()) expect(onOpenChange).toHaveBeenCalledWith(true) expect(title().getAttribute('aria-expanded')).toBe('false') }) }) /** Opened before its rows are in (0.65.1, OPT-11): the title says open, the panel waits. */ describe('CollapsibleSection, pending', () => { it('the title says open and busy while the rows stay shut, then they open', () => { const { rerender } = render({rows}) expect(title().getAttribute('aria-expanded')).toBe('true') expect(title().getAttribute('aria-busy')).toBe('true') expect(screen.getByText('Item one').closest('[hidden]')).not.toBeNull() rerender({rows}) expect(title().getAttribute('aria-expanded')).toBe('true') expect(title().getAttribute('aria-busy')).toBeNull() expect(screen.getByText('Item one').closest('[hidden]')).toBeNull() }) it('a click while pending closes it', async () => { const onOpenChange = vi.fn() render({rows}) await userEvent.click(title()) expect(onOpenChange).toHaveBeenCalledWith(false) }) it('a closed section is not busy, and a click opens it', async () => { const onOpenChange = vi.fn() render({rows}) expect(title().getAttribute('aria-expanded')).toBe('false') expect(title().getAttribute('aria-busy')).toBeNull() await userEvent.click(title()) expect(onOpenChange).toHaveBeenCalledWith(true) }) }) /** What it hands its header (0.56.0): the folder's ⋮, the ⋮'s name, and the icon before the title. */ describe('CollapsibleSection, its header', () => { it('passes menu and menuLabel on: the ⋮ is named by menuLabel and opens the menu', async () => { render(} menuLabel="Section options">{rows}) await userEvent.click(screen.getByRole('button', { name: 'Section options' })) expect(await screen.findByRole('menuitem', { name: 'Rename' })).not.toBeNull() // The ⋮ acts; it does not fold the section. expect(title().getAttribute('aria-expanded')).toBe('true') }) it('with menu and no menuLabel, the ⋮ is named for the title', () => { render(}>{rows}) expect(screen.getByRole('button', { name: 'More options for Section' })).not.toBeNull() }) it('passes icon on: it is drawn inside the title’s toggle, before the words', () => { render(}>{rows}) const icon = screen.getByTestId('icon') expect(title().contains(icon)).toBe(true) expect(icon.compareDocumentPosition(screen.getByText('Section')) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy() }) })