feat(transcribe): add ShortcutCheatsheet dialog overlay (#327)
Native <dialog aria-modal> cheatsheet: showModal()/close() bridge, close button focused on open, eight grouped <kbd> rows (nav/edit/utility), an autosave footer line, and a reduced-motion-guarded fade. Closes on Esc, backdrop click, and the close button; "?" while open is a no-op. Adds the shortcut_close_panel i18n key. 8 component tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import ShortcutCheatsheet from './ShortcutCheatsheet.svelte';
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
describe('ShortcutCheatsheet', () => {
|
||||
it('is not in the accessibility tree when closed', async () => {
|
||||
render(ShortcutCheatsheet, { open: false, onClose: vi.fn() });
|
||||
await expect.element(page.getByRole('dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('opens as a modal dialog with a labelled heading when open', async () => {
|
||||
render(ShortcutCheatsheet, { open: true, onClose: vi.fn() });
|
||||
await expect.element(page.getByRole('dialog')).toBeInTheDocument();
|
||||
await expect.element(page.getByRole('heading')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('lists all eight shortcut rows', async () => {
|
||||
render(ShortcutCheatsheet, { open: true, onClose: vi.fn() });
|
||||
const dialog = page.getByRole('dialog').element() as HTMLElement;
|
||||
const keyCaps = dialog.querySelectorAll('kbd');
|
||||
expect(keyCaps.length).toBe(8);
|
||||
});
|
||||
|
||||
it('shows the autosave footer line', async () => {
|
||||
render(ShortcutCheatsheet, { open: true, onClose: vi.fn() });
|
||||
const dialog = page.getByRole('dialog').element() as HTMLElement;
|
||||
expect(dialog.textContent).toContain('automatisch');
|
||||
});
|
||||
|
||||
it('calls onClose when Escape is pressed', async () => {
|
||||
const onClose = vi.fn();
|
||||
render(ShortcutCheatsheet, { open: true, onClose });
|
||||
const dialog = page.getByRole('dialog').element() as HTMLDialogElement;
|
||||
dialog.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
||||
// native <dialog> turns Esc into a 'cancel' + 'close'; assert close fired onClose
|
||||
dialog.dispatchEvent(new Event('close'));
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onClose when the backdrop is clicked', async () => {
|
||||
const onClose = vi.fn();
|
||||
render(ShortcutCheatsheet, { open: true, onClose });
|
||||
const dialog = page.getByRole('dialog').element() as HTMLDialogElement;
|
||||
// a click whose target is the dialog element itself is a backdrop click
|
||||
dialog.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not close on "?" while open (open-only, not a toggle)', async () => {
|
||||
const onClose = vi.fn();
|
||||
render(ShortcutCheatsheet, { open: true, onClose });
|
||||
const dialog = page.getByRole('dialog').element() as HTMLDialogElement;
|
||||
dialog.dispatchEvent(new KeyboardEvent('keydown', { key: '?', bubbles: true }));
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('focuses the close button on open', async () => {
|
||||
render(ShortcutCheatsheet, { open: true, onClose: vi.fn() });
|
||||
const closeButton = page.getByRole('button', { name: /schließen/i }).element();
|
||||
expect(document.activeElement).toBe(closeButton);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user