import { Meta, StoryObj } from '@storybook/react-webpack5'; import Link from '../../link'; import List from '../../list'; import { ListItem, type ListItemProps } from '../ListItem'; import { expect, userEvent, within, waitFor } from 'storybook/test'; const waitForFocus = async (assertion: () => Promise, timeout = 3000) => { await waitFor(assertion, { timeout }); }; const waitForListItem = async (canvas: ReturnType, timeout = 3000) => { await waitFor( async () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access await expect(canvas.getByRole('listitem')).toBeInTheDocument(); }, { timeout }, ); }; export default { component: ListItem, title: 'Content/ListItem/Tests/focus', tags: ['!autodocs', '!manifest'], parameters: { controls: { disable: true }, actions: { disable: true }, a11y: { disable: true }, knobs: { disable: true }, }, } satisfies Meta; type Story = StoryObj; const title = { full: 'Fully interactive', partial: 'Partially interactive', }; const subtitle = { full: 'Whole item should be focusable, control should not.', partial: 'Only control should be focusable, not whole item.', }; const additionalInfo = { static: ( Fully interactive ListItems don't allow any nested interactive elements like links or buttons within AdditionalInfo. ), interactive: ( This additional info has a focusable link ), }; const prompt = { static: Non-interactive prompt., interactive: ( This prompt has a{' '} single interactive element {' '} that spreads across the whole prompt area. ), }; const button = { full: ( {}}> Click me ), partial: ( {}}> Click me ), }; export const FullyInteractive: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).toHaveFocus(); }); }, render: () => ( ), }; export const FullyInteractiveFocusedOnPrompt: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).toHaveFocus(); }); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('link', { name: /^single interactive element/ })).toHaveFocus(); }); }, render: () => ( ), }; export const PartiallyInteractiveFocusedOnControl: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).toHaveFocus(); }); }, render: () => ( ), }; export const PartiallyInteractiveFocusedOnAdditionInfo: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).toHaveFocus(); }); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('link', { name: /^appended to the end/ })).toHaveFocus(); }); }, render: () => ( ), }; export const PartiallyInteractiveFocusedOnPrompt: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).toHaveFocus(); }); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('link', { name: /^appended to the end/ })).toHaveFocus(); }); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('link', { name: /^single interactive element/ })).toHaveFocus(); }); }, render: () => ( ), }; export const FullyInteractiveDisabled: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).not.toHaveFocus(); }); }, render: () => ( ), }; export const PartiallyInteractiveDisabled: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await waitForListItem(canvas); await userEvent.tab(); await waitForFocus(async () => { await expect(canvas.getByRole('button')).not.toHaveFocus(); }); }, render: () => ( ), };