Jede Aktion sperrt die Knöpfe der Zeile; Chromium nimmt einem gesperrten Knopf den Fokus und gibt ihn nicht zurück, Tastatur- und Screenreader-Nutzer standen danach am Seitenanfang. begin() merkt sich das fokussierte Bedienelement und das offene Feld; ist die Anfrage vorbei, geht der Fokus zurück an den Knopf, sonst ans Textfeld, an den Knopf, der das Feld öffnete, sonst an die Zeile (tabIndex -1). Verlässt die Zeile die Liste, nimmt die Meldung oben (tabIndex -1) den Fokus, auch bei gleichem Text zweimal nacheinander. Es wird nur eingegriffen, wenn der Fokus wirklich verloren ging und vor der Aktion ein Bedienelement ihn hatte (Safari fokussiert Knöpfe nicht). Neues Modul workersFocus.js (rein, document wird übergeben). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
60 lines
3.5 KiB
JavaScript
60 lines
3.5 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { loadModule } from './load.mjs'
|
|
|
|
// Fokus nach Pult-Aktionen (Endprüfung P4, W3): ein gesperrter Knopf verliert den Fokus an <body>; die Zeile gibt ihn danach zurück,
|
|
// aber nur, wenn er wirklich verloren ging. Reine Logik mit einer document-Attrappe, kein DOM nötig.
|
|
const { activeControl, focusLost, restoreFocus } = await loadModule('src/lib/workersFocus.js')
|
|
|
|
function fakeDoc() {
|
|
const body = { name: 'body' }
|
|
const doc = { body, documentElement: { name: 'html' }, activeElement: body }
|
|
const el = (name, extra = {}) => ({ name, isConnected: true, disabled: false, focusCalls: 0, focus() { this.focusCalls += 1; doc.activeElement = this }, ...extra })
|
|
return { doc, body, el }
|
|
}
|
|
|
|
test('focusLost: body, Wurzel, nichts, gesperrtes oder ausgehängtes Element gelten als verloren, ein bedienbares Element nicht', () => {
|
|
const { doc, body, el } = fakeDoc()
|
|
assert.equal(focusLost(doc), true) // activeElement = body
|
|
doc.activeElement = doc.documentElement; assert.equal(focusLost(doc), true)
|
|
for (const nothing of [null, undefined]) { doc.activeElement = nothing; assert.equal(focusLost(doc), true, String(nothing)) }
|
|
doc.activeElement = el('a', { disabled: true }); assert.equal(focusLost(doc), true) // der Browser lässt es erst beim nächsten Takt los
|
|
doc.activeElement = el('b', { isConnected: false }); assert.equal(focusLost(doc), true)
|
|
doc.activeElement = el('c'); assert.equal(focusLost(doc), false)
|
|
// ohne document (Node, Server) gibt es nichts zurückzugeben
|
|
for (const none of [null, undefined]) assert.equal(focusLost(none), false)
|
|
assert.equal(body.name, 'body')
|
|
})
|
|
|
|
test('activeControl: das fokussierte Bedienelement, sonst null (body und Wurzel zählen nicht)', () => {
|
|
const { doc, el } = fakeDoc()
|
|
assert.equal(activeControl(doc), null)
|
|
doc.activeElement = doc.documentElement; assert.equal(activeControl(doc), null)
|
|
doc.activeElement = null; assert.equal(activeControl(doc), null)
|
|
const button = el('button'); doc.activeElement = button
|
|
assert.equal(activeControl(doc), button)
|
|
assert.equal(activeControl(null), null); assert.equal(activeControl(undefined), null)
|
|
})
|
|
|
|
test('restoreFocus: nimmt den ersten Kandidaten, der noch da und frei ist; greift nur ein, wenn der Fokus verloren ging', () => {
|
|
const { doc, body, el } = fakeDoc()
|
|
const gone = el('gone', { isConnected: false })
|
|
const locked = el('locked', { disabled: true })
|
|
const first = el('first')
|
|
const second = el('second')
|
|
assert.equal(restoreFocus(doc, [null, undefined, gone, locked, first, second]), true)
|
|
assert.equal(doc.activeElement, first); assert.deepEqual([first.focusCalls, second.focusCalls, gone.focusCalls, locked.focusCalls], [1, 0, 0, 0])
|
|
|
|
// der Fokus liegt woanders (die Person hat inzwischen selbst gewählt): nichts wird angefasst
|
|
const other = el('other'); doc.activeElement = other
|
|
assert.equal(restoreFocus(doc, [first]), false)
|
|
assert.equal(doc.activeElement, other); assert.equal(first.focusCalls, 1)
|
|
|
|
// kein brauchbarer Kandidat, kein document, keine Liste: false, nichts passiert
|
|
doc.activeElement = body
|
|
assert.equal(restoreFocus(doc, [null, gone, locked]), false); assert.equal(doc.activeElement, body)
|
|
assert.equal(restoreFocus(null, [first]), false); assert.equal(restoreFocus(doc, []), false)
|
|
// Dinge ohne focus() (Attrappen, Textknoten) werden übersprungen
|
|
assert.equal(restoreFocus(doc, [{ isConnected: true }, 'text', 7, second]), true); assert.equal(doc.activeElement, second)
|
|
})
|