import { fireEvent, render, screen } from '@testing-library/react' import { useState } from 'react' import { describe, expect, it } from 'vitest' import { expectMinHitArea } from '../test/hitArea.ts' import { Segmented } from './Segmented.tsx' import styles from './Segmented.module.css' const options = [ { value: 'week', label: 'Week' }, { value: 'month', label: 'Month' }, { value: 'year', label: 'Year' }, ] as const type Period = (typeof options)[number]['value'] function Harness({ initial = 'week' }: { initial?: Period }) { const [value, setValue] = useState(initial) return } function radio(name: string) { return screen.getByRole('radio', { name }) } describe('Segmented', () => { it('is a named radiogroup of radios with one checked', () => { render() expect(screen.getByRole('radiogroup', { name: 'Archive period' })).toBeInTheDocument() expect(screen.getAllByRole('radio')).toHaveLength(3) expect(radio('Week')).toHaveAttribute('aria-checked', 'true') expect(radio('Month')).toHaveAttribute('aria-checked', 'false') expect(radio('Week')).toHaveClass(styles.selected!) }) it('has a single tab stop on the selected option', () => { render() expect(radio('Month')).toHaveAttribute('tabindex', '0') expect(radio('Week')).toHaveAttribute('tabindex', '-1') expect(radio('Year')).toHaveAttribute('tabindex', '-1') }) it('moves selection and focus with the arrow keys, wrapping at the ends', () => { render() radio('Week').focus() fireEvent.keyDown(radio('Week'), { key: 'ArrowRight' }) expect(radio('Month')).toHaveAttribute('aria-checked', 'true') expect(radio('Month')).toHaveFocus() fireEvent.keyDown(radio('Month'), { key: 'ArrowDown' }) expect(radio('Year')).toHaveAttribute('aria-checked', 'true') fireEvent.keyDown(radio('Year'), { key: 'ArrowRight' }) expect(radio('Week')).toHaveAttribute('aria-checked', 'true') expect(radio('Week')).toHaveFocus() fireEvent.keyDown(radio('Week'), { key: 'ArrowLeft' }) expect(radio('Year')).toHaveAttribute('aria-checked', 'true') fireEvent.keyDown(radio('Year'), { key: 'ArrowUp' }) expect(radio('Month')).toHaveAttribute('aria-checked', 'true') expect(radio('Month')).toHaveFocus() expect(radio('Year')).toHaveAttribute('aria-checked', 'false') }) it('jumps to the first and last option with Home and End', () => { render() fireEvent.keyDown(radio('Month'), { key: 'End' }) expect(radio('Year')).toHaveAttribute('aria-checked', 'true') fireEvent.keyDown(radio('Year'), { key: 'Home' }) expect(radio('Week')).toHaveAttribute('aria-checked', 'true') }) it('selects an option on click', () => { render() fireEvent.click(radio('Year')) expect(radio('Year')).toHaveAttribute('aria-checked', 'true') expect(radio('Week')).toHaveAttribute('aria-checked', 'false') }) it('gives every option a ≥ 44 px hit area', () => { render() for (const option of screen.getAllByRole('radio')) expectMinHitArea(option) }) it('fails fast when the value is not one of the options', () => { expect(() => render( {}} />), ).toThrow('value "decade" is not one of its options') }) })