View Issue Details

IDProjectCategoryView StatusLast Update
0012055Talerwallet (WebExtension)public2026-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
PrioritynormalSeveritytweakReproducibilityhave not tried
Status newResolutionopen 
Summary0012055: Wallet: '•••' overflow menu opens without moving focus into the menu
DescriptionWhat happens:
Bank accounts and wire-transfer instructions have a '•••' (role=menu) with 'Copy payto URI'. Opening it leaves focus on the trigger, so keyboard and screen-reader users are not taken into the menu. activeElement: BUTTON '•••'.

Expected:
First menu item focused on open; focus returns to the trigger when the menu is closed with Escape (from inside the menu) or by choosing an item, so it is not lost when the items unmount.

Where:
  - packages/wallet-webui/src/ui/OverflowMenu.tsx:15

Measured in headless Chromium (document.activeElement):
  before: BUTTON '•••'
  after: BUTTON role=menuitem 'Copy payto URI'

Proposed fix:
Refs on trigger and menu; open effect focuses the first menuitem; Escape and item selection return focus to the trigger. Outside clicks unchanged.

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 (0 errors in wallet-webui with workspace sources; plain `tsc --noEmit -p .` output identical to baseline, whose errors come from stale taler-util/wallet-core lib/); eslint + prettier clean on changed files; package tests screens/exchange-removal/payment-entry/routing bundled from src: 89/95 pass, the same 6 App-level demo tests fail before and after; scratch test: open -> first item, Escape -> trigger, select -> trigger; existing screens.test copy-payto flow passes. Call sites: BankAccountsScreen, WireTransferScreen.
TagsNo tags attached.
Attached Files
wallet--overflow-menu.diff (2,195 bytes)   
diff --git a/packages/wallet-webui/src/ui/OverflowMenu.tsx b/packages/wallet-webui/src/ui/OverflowMenu.tsx
index 54fb5d93c..84b872dd0 100644
--- a/packages/wallet-webui/src/ui/OverflowMenu.tsx
+++ b/packages/wallet-webui/src/ui/OverflowMenu.tsx
@@ -12,11 +12,20 @@ export function OverflowMenu(props: {
 }) {
   const [open, setOpen] = useState(false);
   const container = useRef<HTMLDivElement>(null);
+  const trigger = useRef<HTMLButtonElement>(null);
+  const menu = useRef<HTMLDivElement>(null);
 
   useEffect(() => {
     if (!open) return;
+    // A menu takes focus when it opens and hands it back to its button when
+    // it closes from the keyboard or a choice, so focus is never lost with
+    // the removed items.
+    menu.current?.querySelector<HTMLElement>('[role="menuitem"]')?.focus();
     const closeOnEscape = (event: KeyboardEvent) => {
-      if (event.key === "Escape") setOpen(false);
+      if (event.key !== "Escape") return;
+      if (menu.current?.contains(document.activeElement))
+        trigger.current?.focus();
+      setOpen(false);
     };
     const closeOutside = (event: PointerEvent) => {
       if (!container.current?.contains(event.target as Node)) setOpen(false);
@@ -32,6 +41,7 @@ export function OverflowMenu(props: {
   return (
     <div class="relative shrink-0" ref={container}>
       <button
+        ref={trigger}
         type="button"
         aria-label={props.label}
         aria-haspopup="menu"
@@ -43,6 +53,7 @@ export function OverflowMenu(props: {
       </button>
       {open && (
         <div
+          ref={menu}
           role="menu"
           aria-label={props.label}
           class="absolute right-0 z-10 mt-1 min-w-48 rounded-xl border border-outlineVariant bg-surface p-1 shadow-lg"
@@ -54,6 +65,7 @@ export function OverflowMenu(props: {
               role="menuitem"
               onClick={() => {
                 setOpen(false);
+                trigger.current?.focus();
                 item.onSelect();
               }}
               class="min-h-11 w-full rounded-lg px-3 py-2 text-left text-sm font-semibold text-primary hover:bg-primaryContainer focus:outline-none focus:ring-2 focus:ring-primary"
wallet--overflow-menu.diff (2,195 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: wallet--overflow-menu.diff
2026-11-10 23:43bot-cgFile Added: wallet--overflow-menu--before.png
2026-11-10 23:43bot-cgFile Added: wallet--overflow-menu--after.png