Skip to content
19 changes: 16 additions & 3 deletions src/components/clinical-dashboard/global-mockup-search-shell.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand DownExpand Up@@ -86,6 +88,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();
Expand DownExpand Up@@ -137,12 +140,22 @@ function GlobalMockupSearchShellClient({
// because the state above is already seeded from the URL.
if (lastSyncedSearchParamsRef.current === searchParamString) return;
lastSyncedSearchParamsRef.current = searchParamString;

setSearchMode(resolvedSearchMode);
setQuery(currentUrlHasQuery ? requestedQuery : "");
}, [currentUrlHasQuery, requestedQuery, resolvedSearchMode, searchParamString]);

if (searchParams.get("focus") === "1") inputRef.current?.focus({ preventScroll: true });
}, [currentUrlHasQuery, requestedQuery, resolvedSearchMode, searchParamString, searchParams]);
useEffect(() => {
if (!requestedFocus) return undefined;
const focusInput = () => {
inputRef.current?.focus({ preventScroll: true });
};
const frame = window.requestAnimationFrame(focusInput);
const timeout = window.setTimeout(focusInput, focusHydrationRetryDelayMs);
return () => {
window.cancelAnimationFrame(frame);
window.clearTimeout(timeout);
};
}, [pathname, requestedFocus, searchParamString]);

useEffect(() => {
let cancelled = false;
Expand Down
11 changes: 11 additions & 0 deletions tests/ui-tools.spec.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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"]) {
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");
Expand Down
Loading