diff --git a/src/app/globals.css b/src/app/globals.css index 9de9d6703e..409fa258a1 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1238,16 +1238,39 @@ summary::-webkit-details-marker { min-width: 0; } +.smart-search-rotating-text, +.smart-search-prompt-row, .smart-search-suggestion-row { display: none; } @media (min-width: 1024px) { + .smart-search-rotating-text { + display: flex; + } + + .smart-search-prompt-row, .smart-search-suggestion-row { display: flex; } } +.smart-search-rotating-text { + min-height: 1.5rem; + align-items: center; + justify-content: center; + gap: 0.35rem; + padding-inline: 0.25rem; + color: var(--text-soft); + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.01em; +} + +.smart-search-rotating-query { + color: var(--clinical-accent); +} + .answer-suggestion-label { color: var(--text-soft); font-size: 0.6875rem; diff --git a/src/components/clinical-dashboard/universal-search-command-surface.tsx b/src/components/clinical-dashboard/universal-search-command-surface.tsx index 3052409011..2252c2c7e3 100644 --- a/src/components/clinical-dashboard/universal-search-command-surface.tsx +++ b/src/components/clinical-dashboard/universal-search-command-surface.tsx @@ -22,6 +22,8 @@ import { const focusRing = "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]"; +const SMART_HINT_ROTATION_MS = 3200; + type DropdownItem = { id: string; label: string; @@ -54,15 +56,40 @@ function OptionShell({ active, children, hint }: { active: boolean; children: Re export type CommandSurfacePlacement = "bottom-dock" | "inline"; -function ContextHintRow({ examples, onPickExample }: { examples: string[]; onPickExample: (example: string) => void }) { +function SmartRotatingHint({ examples, modeLabel }: { examples: string[]; modeLabel: string }) { + const [activeExampleIndex, setActiveExampleIndex] = useState(0); + const activeExample = examples[activeExampleIndex % examples.length]; + + useEffect(() => { + if (examples.length <= 1) return; + const intervalId = window.setInterval(() => { + setActiveExampleIndex((current) => (current + 1) % examples.length); + }, SMART_HINT_ROTATION_MS); + return () => window.clearInterval(intervalId); + }, [examples]); + + if (!activeExample) return null; + + return ( +
+ Smart search + + + Try “{activeExample}” in {modeLabel}. + +
+ ); +} + +function SmartPromptRow({ examples, onPickExample }: { examples: string[]; onPickExample: (example: string) => void }) { return ( ); } @@ -545,14 +572,7 @@ export function UniversalSearchCommandSurface({ placement === "bottom-dock" ? "gap-1" : "gap-2", )} > - { - onQueryChange(example); - onDropdownOpenChange(true); - onFocusSearchInput?.(); - }} - /> +
{ @@ -585,6 +605,14 @@ export function UniversalSearchCommandSurface({ /> ) : null}
+ { + onQueryChange(example); + onDropdownOpenChange(true); + onFocusSearchInput?.(); + }} + /> diff --git a/tests/ui-overlap.spec.ts b/tests/ui-overlap.spec.ts index 22a8d994e8..a9fc0fd403 100644 --- a/tests/ui-overlap.spec.ts +++ b/tests/ui-overlap.spec.ts @@ -154,41 +154,55 @@ test.describe("Header element overlap coverage", () => { }); } - test("desktop smart search keeps suggested words above the composer", async ({ page }) => { + test("desktop smart search keeps rotating text above and prompts below the composer", async ({ page }) => { await page.setViewportSize({ width: 1280, height: 900 }); await mockDemoDashboard(page); await gotoHome(page); - const suggestionRow = page.getByTestId("smart-search-suggestion-row"); - await expect(suggestionRow).toBeVisible(); - await expect(suggestionRow.getByRole("button", { name: "lithium level timing" })).toBeVisible(); - await expect(suggestionRow.getByRole("button", { name: "clozapine ANC monitoring" })).toBeVisible(); + const rotatingText = page.getByTestId("smart-search-rotating-text"); + const promptRow = page.getByTestId("smart-search-prompt-row"); + await expect(rotatingText).toBeVisible(); + await expect(rotatingText).toContainText("Smart search"); + await expect(promptRow).toBeVisible(); + await expect(promptRow.getByRole("button", { name: "lithium level timing" })).toBeVisible(); + await expect(promptRow.getByRole("button", { name: "clozapine ANC monitoring" })).toBeVisible(); const geometry = await page.evaluate(() => { - const row = document.querySelector('[data-testid="smart-search-suggestion-row"]'); - const input = document.querySelector('[data-testid="global-search-input"]'); - if (!row || !input) return null; - const rowRect = row.getBoundingClientRect(); - const inputRect = input.getBoundingClientRect(); - return { rowBottom: rowRect.bottom, inputTop: inputRect.top }; + const hint = document.querySelector('[data-testid="smart-search-rotating-text"]'); + const prompt = document.querySelector('[data-testid="smart-search-prompt-row"]'); + const pill = document.querySelector(".answer-footer-search-pill"); + if (!hint || !prompt || !pill) return null; + const hintRect = hint.getBoundingClientRect(); + const promptRect = prompt.getBoundingClientRect(); + const pillRect = pill.getBoundingClientRect(); + return { + hintBottom: hintRect.bottom, + pillTop: pillRect.top, + pillBottom: pillRect.bottom, + promptTop: promptRect.top, + }; }); - expect(geometry, "suggestion row and smart search input must render").not.toBeNull(); - expect(geometry!.rowBottom, "suggestions should sit above the smart search bar").toBeLessThanOrEqual( - geometry!.inputTop + 1, + expect(geometry, "smart search hint, composer, and prompt row must render").not.toBeNull(); + expect(geometry!.hintBottom, "rotating text should sit above the smart search bar").toBeLessThanOrEqual( + geometry!.pillTop + 1, + ); + expect(geometry!.promptTop, "smart prompts should sit below the smart search bar").toBeGreaterThanOrEqual( + geometry!.pillBottom - 1, ); - await suggestionRow.getByRole("button", { name: "lithium level timing" }).click(); + await promptRow.getByRole("button", { name: "lithium level timing" }).click(); await expect(page.locator('[data-testid="global-search-input"]:visible').first()).toHaveValue( "lithium level timing", ); }); - test("phone smart search does not show the desktop suggested words row", async ({ page }) => { + test("phone smart search does not show the desktop rotating text or prompt row", async ({ page }) => { await page.setViewportSize({ width: 390, height: 820 }); await mockDemoDashboard(page); await gotoHome(page); - await expect(page.getByTestId("smart-search-suggestion-row")).toBeHidden(); + await expect(page.getByTestId("smart-search-rotating-text")).toBeHidden(); + await expect(page.getByTestId("smart-search-prompt-row")).toBeHidden(); }); });