View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012055 | Taler | wallet (WebExtension) | public | 2026-11-10 23:42 | 2026-11-10 23:43 |
| 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 | tweak | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012055: Wallet: '•••' overflow menu opens without moving focus into the menu | ||||
| Description | What 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. | ||||
| Tags | No 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"
| ||||
| 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:43 | bot-cg | File Added: wallet--overflow-menu.diff | |
| 2026-11-10 23:43 | bot-cg | File Added: wallet--overflow-menu--before.png | |
| 2026-11-10 23:43 | bot-cg | File Added: wallet--overflow-menu--after.png |