From 8ff4cb5197608867520fe59a99c440d7b839d364 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:10:43 +0000 Subject: [PATCH 1/6] Initial plan From bfeacdd732f27c2f50e21f43ec60b67e647f42ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:22:07 +0000 Subject: [PATCH 2/6] Preserve focus=1 on initial shell route loads --- .../global-mockup-search-shell.tsx | 18 ++++++++++++++++-- tests/ui-tools.spec.ts | 11 +++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/components/clinical-dashboard/global-mockup-search-shell.tsx b/src/components/clinical-dashboard/global-mockup-search-shell.tsx index 0ccb26c9cc..75ff659701 100644 --- a/src/components/clinical-dashboard/global-mockup-search-shell.tsx +++ b/src/components/clinical-dashboard/global-mockup-search-shell.tsx @@ -149,12 +149,26 @@ function GlobalMockupSearchShellClient({ // not wiped by the WebKit requestAnimationFrame race. setQuery(""); } - - if (params.get("focus") === "1") inputRef.current?.focus({ preventScroll: true }); }); return () => window.cancelAnimationFrame(frame); }, [availableModeIds, initialSearchMode, isDetailPage, pathname, searchParamString]); + useEffect(() => { + const params = new URLSearchParams(window.location.search); + if (params.get("focus") !== "1") return undefined; + const focusInput = () => { + const activeElement = document.activeElement; + if (activeElement && activeElement !== document.body && activeElement !== document.documentElement) return; + inputRef.current?.focus({ preventScroll: true }); + }; + const frame = window.requestAnimationFrame(focusInput); + const timeout = window.setTimeout(focusInput, 300); + return () => { + window.cancelAnimationFrame(frame); + window.clearTimeout(timeout); + }; + }, [pathname, searchParamString]); + useEffect(() => { let cancelled = false; const frame = window.requestAnimationFrame(() => { diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts index c6a30b5242..6ea8b1b5df 100644 --- a/tests/ui-tools.spec.ts +++ b/tests/ui-tools.spec.ts @@ -221,6 +221,17 @@ test.describe("Clinical KB applications launcher", () => { } }); + test("mode home deep links preserve focus=1 on initial load", async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 900 }); + + for (const path of ["/services?focus=1", "/forms?focus=1"] as const) { + await gotoLauncher(page, path); + const sharedSearch = page.getByTestId("global-search-input"); + await expect(sharedSearch).toBeVisible(); + await expect(sharedSearch).toBeFocused(); + } + }); + test("services mode shows source-backed records in search results", async ({ page }) => { await page.setViewportSize({ width: 1280, height: 900 }); await gotoLauncher(page, "/services?q=13YARN&focus=1&run=1"); From f5185d21428465eb88a3a98384c742681908867d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:25:16 +0000 Subject: [PATCH 3/6] Stabilize focus reapply on deep-link shell loads --- .../clinical-dashboard/global-mockup-search-shell.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/clinical-dashboard/global-mockup-search-shell.tsx b/src/components/clinical-dashboard/global-mockup-search-shell.tsx index 75ff659701..f313146842 100644 --- a/src/components/clinical-dashboard/global-mockup-search-shell.tsx +++ b/src/components/clinical-dashboard/global-mockup-search-shell.tsx @@ -86,6 +86,7 @@ function GlobalMockupSearchShellClient({ const fallbackMode = visibleShellModes[0]?.id ?? initialMode; const initialSearchMode = availableModeIds?.length && !availableModeIds.includes(initialMode) ? fallbackMode : initialMode; + const requestedFocus = searchParams.get("focus") === "1"; const requestedRun = searchParams.get("run") === "1"; const currentUrlHasQuery = searchParams.has("q") || searchParams.has("query"); const requestedQuery = (searchParams.get("q") ?? searchParams.get("query") ?? "").trim(); @@ -154,11 +155,11 @@ function GlobalMockupSearchShellClient({ }, [availableModeIds, initialSearchMode, isDetailPage, pathname, searchParamString]); useEffect(() => { - const params = new URLSearchParams(window.location.search); - if (params.get("focus") !== "1") return undefined; + if (!requestedFocus) return undefined; + const shouldApplyFocus = (activeElement: Element | null) => + !activeElement || activeElement === document.body || activeElement === document.documentElement; const focusInput = () => { - const activeElement = document.activeElement; - if (activeElement && activeElement !== document.body && activeElement !== document.documentElement) return; + if (!shouldApplyFocus(document.activeElement)) return; inputRef.current?.focus({ preventScroll: true }); }; const frame = window.requestAnimationFrame(focusInput); @@ -167,7 +168,7 @@ function GlobalMockupSearchShellClient({ window.cancelAnimationFrame(frame); window.clearTimeout(timeout); }; - }, [pathname, searchParamString]); + }, [pathname, requestedFocus, searchParamString]); useEffect(() => { let cancelled = false; From 0cc155691c8af2acb160ffef6515760bf1594d24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:27:34 +0000 Subject: [PATCH 4/6] Document focus hydration retry delay constant --- .../clinical-dashboard/global-mockup-search-shell.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/clinical-dashboard/global-mockup-search-shell.tsx b/src/components/clinical-dashboard/global-mockup-search-shell.tsx index f313146842..4cfe68b541 100644 --- a/src/components/clinical-dashboard/global-mockup-search-shell.tsx +++ b/src/components/clinical-dashboard/global-mockup-search-shell.tsx @@ -37,6 +37,8 @@ const mockupQueryModeOptions: Array<{ value: ClinicalQueryMode; label: string }> { value: "required_documentation", label: "Documentation" }, { value: "compare_guidance", label: "Compare" }, ]; +// Re-apply focus shortly after the first frame to survive initial hydration remounts. +const focusHydrationRetryDelayMs = 300; type GlobalMockupSearchShellProps = { children: ReactNode; @@ -163,7 +165,7 @@ function GlobalMockupSearchShellClient({ inputRef.current?.focus({ preventScroll: true }); }; const frame = window.requestAnimationFrame(focusInput); - const timeout = window.setTimeout(focusInput, 300); + const timeout = window.setTimeout(focusInput, focusHydrationRetryDelayMs); return () => { window.cancelAnimationFrame(frame); window.clearTimeout(timeout); From 78fb4743096eb17dd2232ad232ab8eed2d005f79 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:28:50 +0000 Subject: [PATCH 5/6] Fix ui-smoke focus restoration --- .../clinical-dashboard/global-mockup-search-shell.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/clinical-dashboard/global-mockup-search-shell.tsx b/src/components/clinical-dashboard/global-mockup-search-shell.tsx index 4cfe68b541..5b66ce2cdb 100644 --- a/src/components/clinical-dashboard/global-mockup-search-shell.tsx +++ b/src/components/clinical-dashboard/global-mockup-search-shell.tsx @@ -158,10 +158,7 @@ function GlobalMockupSearchShellClient({ useEffect(() => { if (!requestedFocus) return undefined; - const shouldApplyFocus = (activeElement: Element | null) => - !activeElement || activeElement === document.body || activeElement === document.documentElement; const focusInput = () => { - if (!shouldApplyFocus(document.activeElement)) return; inputRef.current?.focus({ preventScroll: true }); }; const frame = window.requestAnimationFrame(focusInput); From 774bbd4e96d6992aa0bdd677100d06525583f5aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:40:56 +0000 Subject: [PATCH 6/6] Remove unnecessary as const from focus test array --- tests/ui-tools.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts index b936fa367c..273b33c497 100644 --- a/tests/ui-tools.spec.ts +++ b/tests/ui-tools.spec.ts @@ -224,7 +224,7 @@ test.describe("Clinical KB applications launcher", () => { test("mode home deep links preserve focus=1 on initial load", async ({ page }) => { await page.setViewportSize({ width: 1280, height: 900 }); - for (const path of ["/services?focus=1", "/forms?focus=1"] as const) { + for (const path of ["/services?focus=1", "/forms?focus=1"]) { await gotoLauncher(page, path); const sharedSearch = page.getByTestId("global-search-input"); await expect(sharedSearch).toBeVisible();