import { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react-vite'; import { expect } from 'storybook/test'; import type { HandlerMap } from '.'; import { Hotkeys } from '.'; const meta = { title: 'Components/Hotkeys', component: Hotkeys, args: { global: undefined, focusable: undefined, handlers: {}, }, tags: ['test'], } satisfies Meta; export default meta; type Story = StoryObj; const hotkeyTest: Story['play'] = async ({ canvas, userEvent }) => { async function confirmHotkey(name: string, shouldFind = true) { // 'status' is the role of the 'output' element const output = await canvas.findByRole('status'); if (shouldFind) { await expect(output).toHaveTextContent(name); } else { await expect(output).not.toHaveTextContent(name); } } const button = await canvas.findByRole('button'); await userEvent.click(button); await userEvent.keyboard('n'); await confirmHotkey('new'); await userEvent.keyboard('/'); await confirmHotkey('search'); await userEvent.keyboard('o'); await confirmHotkey('open'); await userEvent.keyboard('{Alt>}N{/Alt}'); await confirmHotkey('forceNew'); await userEvent.keyboard('gh'); await confirmHotkey('goToHome'); await userEvent.keyboard('gn'); await confirmHotkey('goToNotifications'); await userEvent.keyboard('gf'); await confirmHotkey('goToFavourites'); /** * Ensure that hotkeys are not triggered when certain * interactive elements are focused: */ await userEvent.keyboard('{enter}'); await confirmHotkey('open', false); const input = await canvas.findByRole('textbox'); await userEvent.click(input); await userEvent.keyboard('n'); await confirmHotkey('new', false); await userEvent.keyboard('{backspace}'); await confirmHotkey('None', false); /** * Reset playground: */ await userEvent.click(button); await userEvent.keyboard('{backspace}'); }; export const Default = { render: function Render() { const [matchedHotkey, setMatchedHotkey] = useState( null, ); const handlers = { back: () => { setMatchedHotkey(null); }, new: () => { setMatchedHotkey('new'); }, forceNew: () => { setMatchedHotkey('forceNew'); }, search: () => { setMatchedHotkey('search'); }, open: () => { setMatchedHotkey('open'); }, goToHome: () => { setMatchedHotkey('goToHome'); }, goToNotifications: () => { setMatchedHotkey('goToNotifications'); }, goToFavourites: () => { setMatchedHotkey('goToFavourites'); }, }; return (

Hotkey playground

Last pressed hotkey: {matchedHotkey ?? 'None'}

Click within the dashed border and press the n or / key. Press Backspace to clear the displayed hotkey.

Try typing a sequence, like g shortly followed by{' '} h, n, or f

Note that this playground doesn't support all hotkeys we use in the app.

When a is focused, Enter should not trigger open, but o should.

When an input element is focused, hotkeys should not interfere with regular typing:

); }, play: hotkeyTest, };