Public: anyone can read this; the crew can change it.
`?` opens a keyboard sheet listing the keys of the page you are on, and j/k/enter/esc work the same on every list #10977
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/10922-opens-a-keyboard-sheet-listing-the-keys → main 1 commit fork
1 file
changed +45
−0
test/keyhelp.test.ts added +45
| @@ -0,0 +1,45 @@ | ||
| 1 | import { expect, test } from 'bun:test'; | |
| 2 | import { readFileSync } from 'node:fs'; | |
| 3 | ||
| 4 | const src = readFileSync(new URL('../src/web/client.ts', import.meta.url), 'utf8'); | |
| 5 | const layout = readFileSync(new URL('../src/web/layout.ts', import.meta.url), 'utf8'); | |
| 6 | ||
| 7 | const helpStart = src.indexOf('// ---- help overlay'); | |
| 8 | const handlerStart = src.lastIndexOf("doc.addEventListener('keydown'", helpStart); | |
| 9 | const handler = src.slice(handlerStart, helpStart); | |
| 10 | const help = src.slice(helpStart, src.indexOf('// ---- command palette')); | |
| 11 | ||
| 12 | test('the field guard returns before any plain key is read', () => { | |
| 13 | const guard = handler.indexOf("t.matches('input, textarea, select, [contenteditable]')"); | |
| 14 | expect(guard).toBeGreaterThan(0); | |
| 15 | for (const key of ['j', 'k', 'o', '?', '/', ':', 'c', 'C']) { | |
| 16 | const at = handler.lastIndexOf(`e.key === '${key}'`); | |
| 17 | expect(at).toBeGreaterThan(guard); | |
| 18 | } | |
| 19 | // The guard block ends in a bare return: nothing typed in a field falls through. | |
| 20 | const block = handler.slice(guard, handler.indexOf('if (e.metaKey || e.ctrlKey || e.altKey) return;')); | |
| 21 | expect(block).toMatch(/\n\s+return;\n\s+\}\n/); | |
| 22 | }); | |
| 23 | ||
| 24 | test('an open dialog owns its keys', () => { | |
| 25 | expect(handler).toContain("t.closest('dialog[open]')) return;"); | |
| 26 | }); | |
| 27 | ||
| 28 | test('j/k, enter, esc and ? are rows of the one table the sheet renders', () => { | |
| 29 | const table = src.match(/var STATIC_KEYS = \[([\s\S]*?)\n \];/)?.[1] ?? ''; | |
| 30 | for (const label of ["'j / k'", "'o or Enter'", "'?'", "'Esc'"]) expect(table).toContain(label); | |
| 31 | expect(help).toContain('pageKeys().concat(STATIC_KEYS)'); | |
| 32 | expect(help).toContain("querySelectorAll('[data-shortcut]')"); | |
| 33 | }); | |
| 34 | ||
| 35 | test('the sheet opens as the shared modal, which closes on Esc and returns focus', () => { | |
| 36 | expect(help).toContain('openModal(dlg);'); | |
| 37 | expect(src).toContain('var was = document.activeElement;'); | |
| 38 | expect(src).toContain("if (e.key !== 'Escape') return;"); | |
| 39 | expect(src).toContain('was.focus()'); | |
| 40 | }); | |
| 41 | ||
| 42 | test('the dialog is out of the flow and in the page chrome, built without innerHTML', () => { | |
| 43 | expect(layout).toContain('id="help-dialog"'); | |
| 44 | expect(help).not.toMatch(/\.innerHTML\s*[=+]/); | |
| 45 | }); |