View Issue Details

IDProjectCategoryView StatusLast Update
0012059Talerotherpublic2026-11-10 23:43
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 
Summary0012059: web-util: Date fields: the calendar dialog opens with focus left behind the overlay, and closing it drops focus to the page body
DescriptionWhat happens:
absoluteTimeText/isoDateText fields (VQF 902.x KYC forms, AML decision Information 'Expiration', AML Rules) open a modal Calendar inside Dialog when the calendar button is clicked. Focus stays on the calendar button underneath the grey overlay; keyboard users have to Tab through the backdrop and the year/month arrows to reach a day. After a day is picked, the dialog unmounts and focus falls to BODY, so Tab restarts at the top of the page. Escape does nothing.

Expected:
Opening the picker focuses the selected day (Enter keeps it, Tab walks the month); picking a day or pressing Escape closes the picker and returns focus to the calendar button.

Where:
  - packages/web-util/src/forms/Calendar.tsx:53
  - packages/web-util/src/forms/Dialog.tsx:13

Measured in headless Chromium (document.activeElement):
  before: open: calendar trigger BUTTON behind the overlay; after picking a day: BODY; Escape: no effect
  after: open: day BUTTON '11' (selected day); after 'Next month' click: stays on 'Next month'; after picking a day: calendar trigger BUTTON; after Escape: calendar trigger BUTTON

Proposed fix:
Calendar focuses its selected day on mount (useAutoFocus, so navigating months does not pull focus back). Dialog remembers the focused element when it opens and restores it when it closes, and closes on Escape (onClose read through a ref, because callers pass an inline arrow). Needs the useAutoFocus helper from web-util--form-focus.diff.

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 ok (no new errors); eslint clean; no unit tests exist for Calendar/Dialog; Chromium calendar scenario before/after
TagsNo tags attached.
Attached Files
web-util--date-picker-dialog.diff (3,032 bytes)   
diff --git a/packages/web-util/src/forms/Calendar.tsx b/packages/web-util/src/forms/Calendar.tsx
index e25f17e13..3a24ff61d 100644
--- a/packages/web-util/src/forms/Calendar.tsx
+++ b/packages/web-util/src/forms/Calendar.tsx
@@ -19,7 +19,7 @@ import {
 import { VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useTranslationContext } from "../context/translation.js";
-import { composeRef, saveRef } from "../components/utils.js";
+import { composeRef, saveRef, useAutoFocus } from "../components/utils.js";
 
 const THIS_MONTH = getMonth(new Date());
 const THIS_YEAR = getYear(new Date());
@@ -47,6 +47,12 @@ export function Calendar({
     if (input.current === document.activeElement) return;
     input.current.value = !year ? "" : String(year);
   }, [year]);
+  // The calendar is a picker the user has just opened: start on the selected
+  // day so that Enter keeps it and Tab moves on through the month.
+  const days = useRef<HTMLDivElement>(null);
+  useAutoFocus(true, () =>
+    days.current?.querySelector<HTMLElement>('[data-selected="true"]'),
+  );
 
   const start = startOfWeek(startOfMonth(showingDate), { weekStartsOn: 1 });
   const end = endOfWeek(endOfMonth(showingDate), { weekStartsOn: 1 });
@@ -183,7 +189,10 @@ export function Calendar({
         ))}
       </div>
       <div class="isolate mt-2">
-        <div class="grid grid-cols-7 gap-px rounded-lg bg-gray-200 text-sm shadow ring-1 ring-gray-200">
+        <div
+          ref={days}
+          class="grid grid-cols-7 gap-px rounded-lg bg-gray-200 text-sm shadow ring-1 ring-gray-200"
+        >
           {daysInMonth.map((current, idx) => (
             <button
               type="button"
diff --git a/packages/web-util/src/forms/Dialog.tsx b/packages/web-util/src/forms/Dialog.tsx
index 4bf96a446..ddba23f14 100644
--- a/packages/web-util/src/forms/Dialog.tsx
+++ b/packages/web-util/src/forms/Dialog.tsx
@@ -1,4 +1,5 @@
 import { ComponentChildren, VNode, h } from "preact";
+import { useEffect, useRef } from "preact/hooks";
 import { useTranslationContext } from "../context/translation.js";
 
 export function Dialog({
@@ -9,6 +10,24 @@ export function Dialog({
   children: ComponentChildren;
 }): VNode {
   const { i18n } = useTranslationContext();
+  const onCloseRef = useRef(onClose);
+  onCloseRef.current = onClose;
+  useEffect(() => {
+    // Hand focus back to the control that opened the dialog, otherwise it is
+    // lost to the page body when the dialog content goes away.
+    const previousFocus =
+      document.activeElement instanceof HTMLElement
+        ? document.activeElement
+        : null;
+    function closeOnEscape(event: KeyboardEvent): void {
+      if (event.key === "Escape") onCloseRef.current?.();
+    }
+    document.addEventListener("keydown", closeOnEscape);
+    return () => {
+      document.removeEventListener("keydown", closeOnEscape);
+      previousFocus?.focus();
+    };
+  }, []);
   return (
     <div
       class="relative z-10"
web-util--calendar--before.png (26,044 bytes)   
web-util--calendar--before.png (26,044 bytes)   
web-util--calendar--after.png (25,828 bytes)   
web-util--calendar--after.png (25,828 bytes)   

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:43bot-cgFile Added: web-util--date-picker-dialog.diff
2026-11-10 23:43bot-cgFile Added: web-util--calendar--before.png
2026-11-10 23:43bot-cgFile Added: web-util--calendar--after.png