From bb20cf506bc9a1fc7d442171b2c519220f115574 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 26 May 2026 20:32:03 +0200 Subject: [PATCH] fix(frontend): blur active element on every tab switch path (B21 follow-up) The previous B21 patch only blurred on user-initiated tab clicks via onTabChange. Two other paths still set activeKey while a JSON-tab input retained focus: - importLink: after a successful share-link parse, setActiveKey('1') switched to the form tab while the user's focus was still on the Input.Search they just pressed Enter in. Chrome logged the same "Blocked aria-hidden" warning because the panel they were leaving became aria-hidden synchronously, with their input still focused. - onTabChange entering the JSON tab: also did a bare setActiveKey with no blur, so going from a focused form input INTO the JSON tab could trip the warning in reverse. Fix: centralized switchTab(key) that blurs document.activeElement sync before calling setActiveKey. Every internal tab transition (importLink, onTabChange both directions) now routes through it. The single setActiveKey('1') in the open-modal useEffect is left as a plain setter because there's no focused input at modal-open time. --- frontend/src/pages/xray/OutboundFormModal.tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/xray/OutboundFormModal.tsx b/frontend/src/pages/xray/OutboundFormModal.tsx index fee094f7..55e61088 100644 --- a/frontend/src/pages/xray/OutboundFormModal.tsx +++ b/frontend/src/pages/xray/OutboundFormModal.tsx @@ -204,7 +204,7 @@ export default function OutboundFormModal({ setJsonDirty(false); setLinkInput(''); messageApi.success('Link imported successfully'); - setActiveKey('1'); + switchTab('1'); } const isEdit = outboundProp != null; @@ -358,28 +358,32 @@ export default function OutboundFormModal({ return true; } - function onTabChange(key: string) { - if (document.activeElement instanceof HTMLElement) { - document.activeElement.blur(); + // Wrap every tab switch with a blur of the active element. AntD marks + // the outgoing panel `aria-hidden="true"` synchronously when the + // controlled activeKey flips; if a focused input is still inside that + // panel (e.g. Input.Search on the JSON tab after user hits Enter to + // import), Chrome logs a WAI-ARIA warning. Doing the blur right + // before setActiveKey ensures the panel is unfocused by the time + // AntD applies the attribute. + function switchTab(key: string) { + if (typeof document !== 'undefined') { + (document.activeElement as HTMLElement | null)?.blur?.(); } + setActiveKey(key); + } + + function onTabChange(key: string) { if (key === '2') { const values = form.getFieldsValue(true) as OutboundFormValues; setJsonText(JSON.stringify(formValuesToWirePayload(values), null, 2)); setJsonDirty(false); - setActiveKey(key); + switchTab(key); return; } if (key === '1' && activeKey === '2') { if (!applyJsonToForm()) return; } - // Blur the currently focused element before AntD marks the outgoing - // tab panel aria-hidden. Without this, a focused input inside the - // hidden panel triggers a Chrome a11y warning ("Blocked aria-hidden - // on an element because its descendant retained focus"). - if (typeof document !== 'undefined') { - (document.activeElement as HTMLElement | null)?.blur?.(); - } - setActiveKey(key); + switchTab(key); } async function onOk() {