diff --git a/src/components/tools-page-mockups/tool-fixtures.ts b/src/components/tools-page-mockups/tool-fixtures.ts index 8c0265ebc..a5a28c6aa 100644 --- a/src/components/tools-page-mockups/tool-fixtures.ts +++ b/src/components/tools-page-mockups/tool-fixtures.ts @@ -114,9 +114,9 @@ export const tools: ToolFixture[] = [ export const pinnedToolIds = ["clinical-kb-search", "documents", "medication-prescribing", "services"] as const; export const areaLabels: Record = { - assessment: "Assess", - care: "Treat", - coordination: "Coordinate", + assessment: "Assessment", + care: "Treatment", + coordination: "Coordination", personal: "Saved", reference: "Reference", }; diff --git a/src/components/tools-page-mockups/tools-page-mockup-page.tsx b/src/components/tools-page-mockups/tools-page-mockup-page.tsx index 1d0ea068d..d18dd2b94 100644 --- a/src/components/tools-page-mockups/tools-page-mockup-page.tsx +++ b/src/components/tools-page-mockups/tools-page-mockup-page.tsx @@ -18,6 +18,7 @@ import { ShieldCheck, Smartphone, Sparkles, + Star, Stethoscope, X, type LucideIcon, @@ -215,7 +216,7 @@ function SearchControl({ aria-label="Clear search" onClick={onClear} className={cn( - "grid h-9 w-9 shrink-0 place-items-center rounded-full text-[color:var(--text-soft)] hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)]", + "grid h-10 w-10 shrink-0 place-items-center rounded-full text-[color:var(--text-soft)] hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)]", focusRing, )} > @@ -226,7 +227,7 @@ function SearchControl({ type="submit" aria-label={label} className={cn( - "grid h-10 w-10 shrink-0 place-items-center rounded-full bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)]", + "grid h-11 w-11 shrink-0 place-items-center rounded-full bg-[color:var(--clinical-accent)] text-[color:var(--clinical-accent-contrast)]", focusRing, )} > @@ -256,7 +257,7 @@ function FilterChip({ aria-pressed={pressed} onClick={onClick} className={cn( - "inline-flex min-h-10 items-center justify-center gap-2 rounded-md border px-3 text-sm font-bold shadow-[var(--shadow-inset)]", + "inline-flex min-h-11 items-center justify-center gap-2 rounded-md border px-3 text-sm font-bold shadow-[var(--shadow-inset)]", pressed ? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]" : "border-[color:var(--border)] bg-[color:var(--surface)] text-[color:var(--text)] hover:border-[color:var(--clinical-accent-border)] hover:bg-[color:var(--surface-subtle)]", @@ -650,7 +651,7 @@ function PhoneBrowserPreview({ {item.title} - + {item.area} @@ -701,28 +702,30 @@ function CommandCenterMockup() { -
-
-
-
-

Start here

-

- Most-used tools stay one click away. -

+ {!filter.isFiltering && ( +
+
+
+
+

Start here

+

+ Most-used tools stay one click away. +

+
-
-
- {pinned.map((tool) => ( - - ))} -
-
+
+ {pinned.map((tool) => ( + + ))} +
+
- - + + + )} - + {!filter.isFiltering && } ); @@ -783,99 +786,109 @@ function WorkflowBoardMockup() {
-
-
- - - - -
+ {!filter.isFiltering && ( +
+
+ + + + + +
-
+ +
+ + + )} - + {!filter.isFiltering && }
); @@ -951,19 +964,47 @@ function SplitPaneMockup() {
-
-
-

- Filters sit beside the overview, while the full-width All tools view below carries the main browsing - weight. -

-
- {["clinical-kb-search", suggestedId, "medication-prescribing", "favourites"].map((id) => ( - - ))} -
+ {filter.isFiltering ? ( + <> +
+
+
+

+ Showing {filter.filtered.length} of {tools.length} +

+
+ {filter.filtered.length > 0 ? ( +
+ {filter.filtered.map((tool) => ( + + ))} +
+ ) : ( + + )} + + ) : ( + <> +
+
+

+ Filters sit beside the overview, while the full-width All tools view below carries the main browsing + weight. +

+
+ {["clinical-kb-search", suggestedId, "medication-prescribing", "favourites"].map((id) => ( + + ))} +
+ + )}
@@ -974,18 +1015,20 @@ function SplitPaneMockup() { /> - + {!filter.isFiltering && ( + + )} - + {!filter.isFiltering && } ); diff --git a/src/components/tools-page-mockups/use-tool-filter.ts b/src/components/tools-page-mockups/use-tool-filter.ts index 6ec1c415e..94618abfe 100644 --- a/src/components/tools-page-mockups/use-tool-filter.ts +++ b/src/components/tools-page-mockups/use-tool-filter.ts @@ -64,6 +64,7 @@ export function useToolFilter(tools: ToolFixture[]) { counts, filterId, filtered, + isFiltering: query.trim().length > 0 || filterId !== "all", query, reset: () => { setFilterId("all"); diff --git a/tests/ui-tools-collapse.spec.ts b/tests/ui-tools-collapse.spec.ts new file mode 100644 index 000000000..71f20f451 --- /dev/null +++ b/tests/ui-tools-collapse.spec.ts @@ -0,0 +1,55 @@ +import { expect, test, type Page } from "playwright/test"; + +// Additive coverage for collapse-on-filter: when a search/filter is active the +// static primary region (Start here / workflow lanes / launcher overview) is +// hidden so results are never shown beneath a still-populated grid. + +async function goto(page: Page, path: string) { + await page.goto(path, { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle", { timeout: 15_000 }).catch(() => undefined); +} + +test.describe("Tools mockups collapse the primary region when filtering", () => { + test.describe.configure({ timeout: 60_000 }); + + test("command center hides Start here and avoids a populated grid over an empty state", async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 900 }); + await goto(page, "/mockups/tools-command-center"); + + await expect(page.getByRole("heading", { name: "Start here" })).toBeVisible(); + + const search = page.getByRole("searchbox").first(); + await search.fill("medication"); + await expect(page.getByRole("heading", { name: "Start here" })).toHaveCount(0); + await expect(page.getByLabel("Open Medication Prescribing")).toBeVisible(); + + // No-match: empty state shows and the pinned grid is gone (not sitting above it). + await search.fill("zzzzznotarealtool"); + await expect(page.getByText(/No tools match/)).toBeVisible(); + await expect(page.getByRole("heading", { name: "Start here" })).toHaveCount(0); + }); + + test("workflow board has a Resume lane and hides lanes when searching", async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 900 }); + await goto(page, "/mockups/tools-workflow-board"); + + await expect(page.getByRole("heading", { name: "Resume" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Assess" })).toBeVisible(); + + await page.getByRole("searchbox").first().fill("medication"); + await expect(page.getByRole("heading", { name: "Assess" })).toHaveCount(0); + await expect(page.getByLabel("Open Medication Prescribing")).toBeVisible(); + }); + + test("split pane swaps the launcher overview to live results when filtering", async ({ page }) => { + await page.setViewportSize({ width: 1280, height: 900 }); + await goto(page, "/mockups/tools-split-pane"); + + await expect(page.getByRole("heading", { name: "Launcher overview" })).toBeVisible(); + + await page.getByRole("searchbox").first().fill("medication"); + await expect(page.getByRole("heading", { name: "Launcher overview" })).toHaveCount(0); + await expect(page.getByRole("heading", { name: "Results" })).toBeVisible(); + await expect(page.getByLabel("Open Medication Prescribing")).toBeVisible(); + }); +});