import React, { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react-webpack5'; import { expect, userEvent, within } from 'storybook/test'; import { withVariantConfig } from '../../.storybook/helpers'; import CheckboxButton from './CheckboxButton'; export default { component: CheckboxButton, title: 'Actions/CheckboxButton/Tests', tags: ['!autodocs', '!manifest'], } satisfies Meta; type Story = StoryObj; /** All checkbox states across all themes. */ export const Variants: Story = { render: function Render() { const [unchecked, setUnchecked] = useState(false); const [checked, setChecked] = useState(true); const [indeterminate, setIndeterminate] = useState(false); return (
setUnchecked((v) => !v)} /> setChecked((v) => !v)} /> setIndeterminate((v) => !v)} /> {}} /> {}} />
); }, ...withVariantConfig(['default', 'dark', 'bright-green', 'forest-green']), }; /** * `Space` toggles when focused (requires `onChange`). Tab skips disabled checkboxes. * Indeterminate resolves to checked on first `Space`, then toggles normally. */ export const KeyboardInteraction: Story = { render: function Render() { const [unchecked, setUnchecked] = useState(false); const [checked, setChecked] = useState(true); const [indeterminate, setIndeterminate] = useState(false); const [disabledUnchecked] = useState(false); const [disabledChecked] = useState(true); const row: React.CSSProperties = { display: 'flex', alignItems: 'center', gap: '8px', }; const label: React.CSSProperties = { fontSize: '14px', color: '#4a5568', width: '140px', }; return (
setUnchecked((v: boolean) => !v)} /> Unchecked
setChecked((v: boolean) => !v)} /> Checked
setIndeterminate((v: boolean) => !v)} /> Indeterminate
{}} /> Disabled unchecked
{}} /> Disabled checked
); }, play: async ({ canvasElement, step }) => { const wait = async (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); }); const canvas = within(canvasElement); await step('tab to Unchecked and toggle on then off', async () => { await userEvent.tab(); const cb = canvas.getByRole('checkbox', { name: /^unchecked$/i }); await expect(cb).toHaveFocus(); await wait(400); await userEvent.keyboard(' '); await expect(cb).toBeChecked(); await wait(400); await userEvent.keyboard(' '); await expect(cb).not.toBeChecked(); }); await wait(400); await step('tab to Checked and toggle off then on', async () => { await userEvent.tab(); const cb = canvas.getByRole('checkbox', { name: /^checked$/i }); await expect(cb).toHaveFocus(); await wait(400); await userEvent.keyboard(' '); await expect(cb).not.toBeChecked(); await wait(400); await userEvent.keyboard(' '); await expect(cb).toBeChecked(); }); await wait(400); await step('tab to Indeterminate and toggle on then off', async () => { await userEvent.tab(); const cb = canvas.getByRole('checkbox', { name: /^indeterminate$/i }); await expect(cb).toHaveFocus(); await wait(400); await userEvent.keyboard(' '); await expect(cb).toBeChecked(); await wait(400); await userEvent.keyboard(' '); await expect(cb).not.toBeChecked(); }); await wait(400); await step('tab skips both disabled checkboxes — focus leaves the component', async () => { await userEvent.tab(); const disabledUnchecked = canvas.getByRole('checkbox', { name: /disabled unchecked/i }); const disabledChecked = canvas.getByRole('checkbox', { name: /disabled checked/i }); await expect(disabledUnchecked).not.toHaveFocus(); await expect(disabledChecked).not.toHaveFocus(); }); }, }; /** Base checkbox states in right-to-left layout. */ export const RTL: Story = { render: function Render() { const [unchecked, setUnchecked] = useState(false); const [checked, setChecked] = useState(true); const [indeterminate, setIndeterminate] = useState(false); return (
setUnchecked((v) => !v)} /> setChecked((v) => !v)} /> setIndeterminate((v) => !v)} /> {}} /> {}} />
); }, ...withVariantConfig(['rtl']), }; /** Checkbox states at 400% zoom for accessibility testing. */ export const Zoom400: Story = { render: () => (
{}} /> {}} /> {}} />
), ...withVariantConfig(['400%']), };