View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012031 | Taler | auditor-webui | public | 2026-11-10 23:42 | 2026-11-10 23:42 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012031: Auditor: 'Suppress the row' confirmation does not move focus into the dialog | ||||
| Description | What happens: Clicking 'Suppress' on a report row (DefaultList -> ConfirmModal, the package's only dialog) opens an aria-modal overlay, but focus stays on the row's 'Suppress' button behind the overlay. Pressing Enter re-triggers that hidden button. Tab walks through the rest of the background table before it reaches the dialog. Measured: activeElement after opening = BUTTON#suppress-2 'Suppress' (behind the overlay); Enter leaves the dialog open. Expected: This is a destructive confirmation without an input, so focus goes to the safe choice, Cancel. When the dialog closes, focus returns to the element that opened it. Where: - packages/taler-auditor-webui/src/components/modal/index.tsx:32 - packages/taler-auditor-webui/src/paths/default/index.tsx:89 Measured in headless Chromium (document.activeElement): before: BUTTON#suppress-2 'Suppress' (the trigger, behind the overlay) after: BUTTON 'Cancel'; Enter closes the dialog without confirming, and focus returns to BUTTON#suppress-2 Proposed fix: ConfirmModal focuses its Cancel/Close button in an effect when it becomes active. It remembers document.activeElement and refocuses that element on close if it is still in the document. Without the restore, closing would drop focus to <body>, where previously it had at least stayed on the opener. Patch against taler-typescript-core master attached; the same change is committed on branch dev/bots/autofocus (durian). Screenshots attached (focused element outlined in red): 'before' is master, 'after' is with the patch. Verified: tsc --noEmit ok; eslint + prettier clean; package unit tests 23/23 pass (run on the built files, since the `pnpm test` glob does not expand on node 20); ConfirmModal rendered in Chromium with the same props DefaultList passes, driven by keyboard, before and after | ||||
| Tags | No tags attached. | ||||
| Attached Files | auditor--suppress-confirm-modal.diff (1,637 bytes)
diff --git a/packages/taler-auditor-webui/src/components/modal/index.tsx b/packages/taler-auditor-webui/src/components/modal/index.tsx
index 152e35d76..01d552062 100644
--- a/packages/taler-auditor-webui/src/components/modal/index.tsx
+++ b/packages/taler-auditor-webui/src/components/modal/index.tsx
@@ -5,6 +5,7 @@
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import type { ComponentChildren, VNode } from "preact";
+import { useEffect, useRef } from "preact/hooks";
interface Props {
active?: boolean;
@@ -28,6 +29,17 @@ export function ConfirmModal({
label,
}: Props): VNode | null {
const { i18n } = useTranslationContext();
+ const cancelButton = useRef<HTMLButtonElement>(null);
+ useEffect(() => {
+ if (!active) return;
+ // Start on the safe choice so that Enter cannot confirm by accident, and
+ // hand focus back to whatever opened the dialog once it closes.
+ const opener = document.activeElement;
+ cancelButton.current?.focus();
+ return () => {
+ if (opener instanceof HTMLElement && opener.isConnected) opener.focus();
+ };
+ }, [active]);
if (!active) return null;
return (
<div
@@ -49,7 +61,7 @@ export function ConfirmModal({
</header>
<div class="px-6 py-5">{children}</div>
<footer class="flex justify-end gap-3 border-t border-gray-200 bg-gray-50 px-6 py-4">
- <button class="auditor-button" onClick={onCancel}>
+ <button ref={cancelButton} class="auditor-button" onClick={onCancel}>
{onConfirm ? (
<i18n.Translate>Cancel</i18n.Translate>
) : (
| ||||
| child of | 0012024 | new | Web UIs (bank, wallet, exchange AML/KYC, challenger, auditor, web-util forms): dialogs and single-purpose forms open without keyboard focus on the obvious field |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-10 23:42 | bot-cg | New Issue | |
| 2026-11-10 23:42 | bot-cg | Relationship added | child of 0012024 |
| 2026-11-10 23:42 | bot-cg | File Added: auditor--suppress-confirm-modal.diff | |
| 2026-11-10 23:42 | bot-cg | File Added: auditor--suppress-modal--before.png | |
| 2026-11-10 23:42 | bot-cg | File Added: auditor--suppress-modal--after.png |