import React, { useState } from 'react' import { type Meta, type StoryObj } from '@storybook/react' import { expect, fn, userEvent, waitFor, within } from '@storybook/test' import isChromatic from 'chromatic' import { Button } from '~components/Button' import { Icon } from '~components/Icon' import { Menu, MenuHeader, MenuItem, MenuPopover, MenuSection, MenuTrigger } from '../index' const meta = { title: 'Components/Menu/Menu tests', component: MenuTrigger, args: { defaultOpen: isChromatic(), children: <>, }, } satisfies Meta export default meta type Story = StoryObj export const KitchenSink: Story = { parameters: { chromatic: { disable: false }, }, args: { defaultOpen: true, }, decorators: [ (Story) => (
), ], render: ({ defaultOpen, ...args }) => ( Section One } href="https://cultureamp.com" > Save }>Edit }>Move Up }> Menu item with a longer label }>Delete } isDisabled> Delete but disabled Other action Other action Other action ), } export const Basic: Story = { render: ({ defaultOpen, ...args }) => ( } onAction={() => alert('Menu item pressed')} > Trigger an alert } href="https://cultureamp.com" target="_blank" > Go to cultureamp.com Item 3 Item 4 Item 5 ), play: async ({ canvasElement, step }) => { const canvas = within(canvasElement.parentElement!) const menuButton = canvas.getByRole('button') await step('Menu opens on click', async () => { await userEvent.click(menuButton) await waitFor(() => expect(canvas.getByRole('menu')).toBeVisible()) }) await step('Arrow keys adjust focus', async () => { await userEvent.keyboard('[ArrowDown]') await waitFor(() => expect(canvas.getByRole('menuitem', { name: 'Go to cultureamp.com' })).toHaveFocus(), ) }) await step('Esc closes menu', async () => { await userEvent.keyboard('[Escape]') await waitFor(() => expect(canvas.queryByRole('menu')).not.toBeInTheDocument()) }) await step('Menu opens on enter press', async () => { await userEvent.keyboard('[Enter]') await waitFor(() => expect(canvas.getByRole('menuitem', { name: 'Trigger an alert' })).toHaveFocus(), ) await userEvent.keyboard('[Escape]') }) await step('Menu opens on arrow up/down', async () => { await userEvent.keyboard('[ArrowDown]') await waitFor(() => expect(canvas.getByRole('menuitem', { name: 'Trigger an alert' })).toHaveFocus(), ) await userEvent.keyboard('[Escape]') await userEvent.keyboard('[ArrowUp]') await waitFor(() => expect(canvas.getByRole('menuitem', { name: 'Item 5' })).toHaveFocus()) }) }, } const mockOnClick = fn() export const DisabledItems: Story = { render: () => ( Item 1 Item 2 Item 3 Item 4 Item 5 ), play: async ({ canvasElement, step }) => { const canvas = within(canvasElement.parentElement!) const menuButton = canvas.getByRole('button') await step('Disabled items are unable to be focused', async () => { await userEvent.click(menuButton) expect(canvas.getByRole('menuitem', { name: 'Item 2' })).toHaveFocus() await userEvent.keyboard('[ArrowDown]') await userEvent.keyboard('[ArrowDown]') expect(canvas.getByRole('menuitem', { name: 'Item 5' })).toHaveFocus() await userEvent.keyboard('[Escape]') }) await step("Clicking a disabled item doesn't trigger onClick", async () => { await userEvent.click(menuButton) await userEvent.click(canvas.getByRole('menuitem', { name: 'Item 1' })) await waitFor(() => expect(mockOnClick).not.toBeCalled()) }) }, } export const WithSections: Story = { render: () => ( Section One Item 1 Item 2 Section Two Item 3 Item 4 Item 5 ), } export const Controlled: Story = { render: () => { const [isOpen, setOpen] = useState(false) return ( <> Item 1 setOpen(true)}>Item 2 Item 3 Item 4 Item 5 ) }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement.parentElement!) const externalButton = await canvas.findByRole('button', { name: 'Toggle open', }) await step('Menu opens on external button click', async () => { await userEvent.click(externalButton) await waitFor(() => expect(canvas.getByRole('menu')).toBeVisible()) }) }, }