mirror of
https://github.com/mastodon/mastodon.git
synced 2025-11-27 18:10:58 +00:00
172 lines
4.2 KiB
TypeScript
172 lines
4.2 KiB
TypeScript
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<typeof Hotkeys>;
|
|
|
|
export default meta;
|
|
|
|
type Story = StoryObj<typeof meta>;
|
|
|
|
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<keyof HandlerMap | null>(
|
|
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 (
|
|
<Hotkeys handlers={handlers}>
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: 8,
|
|
padding: '1em',
|
|
border: '1px dashed #ccc',
|
|
fontSize: 14,
|
|
color: '#222',
|
|
}}
|
|
>
|
|
<h1
|
|
style={{
|
|
fontSize: 22,
|
|
marginBottom: '0.3em',
|
|
}}
|
|
>
|
|
Hotkey playground
|
|
</h1>
|
|
<p>
|
|
Last pressed hotkey: <output>{matchedHotkey ?? 'None'}</output>
|
|
</p>
|
|
<p>
|
|
Click within the dashed border and press the <kbd>n</kbd>
|
|
or <kbd>/</kbd> key. Press
|
|
<kbd>Backspace</kbd> to clear the displayed hotkey.
|
|
</p>
|
|
<p>
|
|
Try typing a sequence, like <kbd>g</kbd> shortly followed by{' '}
|
|
<kbd>h</kbd>, <kbd>n</kbd>, or
|
|
<kbd>f</kbd>
|
|
</p>
|
|
<p>
|
|
Note that this playground doesn't support all hotkeys we use in
|
|
the app.
|
|
</p>
|
|
<p>
|
|
When a <button type='button'>Button</button> is focused,
|
|
<kbd>Enter</kbd>
|
|
should not trigger open, but <kbd>o</kbd>
|
|
should.
|
|
</p>
|
|
<p>
|
|
When an input element is focused, hotkeys should not interfere with
|
|
regular typing:
|
|
</p>
|
|
<input type='text' />
|
|
</div>
|
|
</Hotkeys>
|
|
);
|
|
},
|
|
play: hotkeyTest,
|
|
};
|