From 20f51c658436c8230902f9ce90135cfadd1f217e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 18 Oct 2026 10:42:13 +0000 Subject: [PATCH] =?UTF-8?q?Workers=20P3-Endpr=C3=BCfung=20W3:=20Fokus=20ke?= =?UTF-8?q?hrt=20nach=20dem=20Anhalten=20auf=20den=20Knopf=20der=20Leiste?= =?UTF-8?q?=20zur=C3=BCck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Dialog schloss im selben Takt, in dem pausing den Knopf der Leiste sperrte; useDialogFocus konnte den Fokus deshalb nicht zurückgeben, und er blieb für Tastatur- und Screenreader-Nutzer dauerhaft auf . Das Board setzt ihn jetzt selbst, sobald pausing endet (auch nach einem Fehler), und nur dann, wenn der Fokus verloren ist. Der Prüfstand-Check (Szenario I) ist grün. Co-Authored-By: Claude Sonnet 5.5 --- src/components/workers/WorkersBoard.jsx | 20 ++++++++++++++++++-- tests/unit/workersComponents.test.mjs | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/components/workers/WorkersBoard.jsx b/src/components/workers/WorkersBoard.jsx index cf86356..f3f3f30 100644 --- a/src/components/workers/WorkersBoard.jsx +++ b/src/components/workers/WorkersBoard.jsx @@ -30,6 +30,8 @@ export default function WorkersBoard({ api, isAdmin, stageRef }) { const ref = stageRef ?? ownRef const aliveRef = useRef(true) const sideRef = useRef(null) + const toolbarRef = useRef(null) + const refocusPauseRef = useRef(false) // nach dem Umschalten bekommt der Knopf „Alles anhalten“ / „Fortsetzen“ den Fokus zurück, falls er ihn verlor const [workers, setWorkers] = useState({}) const [selectedId, setSelectedId] = useState(null) const [settingsOpen, setSettingsOpen] = useState(false) @@ -55,6 +57,19 @@ export default function WorkersBoard({ api, isAdmin, stageRef }) { const { trace, loading: traceLoading, error: traceError } = useWorkerTrace(api, selectedId) + // Fokus nach dem Umschalten: Während pausing gilt, ist der Knopf der Leiste gesperrt. Schließt der Dialog im selben Takt, in dem pausing + // beginnt, kann useDialogFocus den Fokus nicht auf den gesperrten Knopf zurückgeben, und er bliebe für immer auf . Deshalb + // wird er erst hier gesetzt, wenn pausing wieder endet (auch nach einem Fehler) und der Knopf wieder bedienbar ist. Hat der Mensch + // inzwischen woanders hin gewechselt (Fokus nicht auf ), bleibt es dabei. + useEffect(() => { + if (pausing || !refocusPauseRef.current) return + refocusPauseRef.current = false + const active = document.activeElement + if (active && active !== document.body) return + const button = toolbarRef.current?.querySelector('[data-wk-pause]') + if (button) button.focus() + }, [pausing]) + // Hat inzwischen jemand anderes angehalten, entfällt die Frage; sie darf nach einem späteren Fortsetzen nicht wieder auftauchen const haltedByState = Boolean(state?.paused) useEffect(() => { @@ -95,6 +110,7 @@ export default function WorkersBoard({ api, isAdmin, stageRef }) { // jemand anderes angehalten hat. const setPause = async (target) => { if (pausing) return + refocusPauseRef.current = true setPausing(true) setPauseError('') try { @@ -118,10 +134,10 @@ export default function WorkersBoard({ api, isAdmin, stageRef }) { return ( <> -
+
{paused ? PAUSED_TEXT : RUNNING_TEXT} - + {isAdmin && }
diff --git a/tests/unit/workersComponents.test.mjs b/tests/unit/workersComponents.test.mjs index 3fa4c75..438169b 100644 --- a/tests/unit/workersComponents.test.mjs +++ b/tests/unit/workersComponents.test.mjs @@ -126,3 +126,25 @@ test('WorkersBoard: „Alles anhalten“ öffnet erst einen Bestätigungsdialog, assert.equal((s.match(/]*type="button"/g) || []).length) assert.doesNotMatch(s, /window\.confirm|confirm\(/) }) + +// Endprüfung P3 (W3): Nach „Anhalten“ schließt der Dialog im selben Takt, in dem pausing beginnt und den Knopf der Leiste sperrt; +// useDialogFocus kann den Fokus dann nicht zurückgeben (focus() auf einem gesperrten Knopf tut nichts) und er bliebe auf . +// Das Board gibt ihn deshalb selbst zurück, sobald pausing endet. Das echte Verhalten prüft der Prüfstand (shot.cjs, Szenario I). +test('WorkersBoard: der Fokus kehrt nach dem Umschalten auf den Knopf der Leiste zurück, sobald er wieder bedienbar ist (W3)', () => { + const s = read('WorkersBoard.jsx') + assert.match(s, /const toolbarRef = useRef\(null\)/); assert.match(s, /const refocusPauseRef = useRef\(false\)/) + assert.match(s, /
/) + assert.match(s, /