View Issue Details

IDProjectCategoryView StatusLast Update
0012031Talerauditor-webuipublic2026-11-10 23:42
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Summary0012031: Auditor: 'Suppress the row' confirmation does not move focus into the dialog
DescriptionWhat 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
TagsNo 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>
             ) : (

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

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 

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-10 23:42bot-cgNew Issue
2026-11-10 23:42bot-cgRelationship addedchild of 0012024
2026-11-10 23:42bot-cgFile Added: auditor--suppress-confirm-modal.diff
2026-11-10 23:42bot-cgFile Added: auditor--suppress-modal--before.png
2026-11-10 23:42bot-cgFile Added: auditor--suppress-modal--after.png