Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions src/components/applications-launcher-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ const launcherApps: LauncherApp[] = toolCatalogRecords.map((record) => ({

const toolsLauncherCopy = {
heading: "Tools",
description:
"Open the clinical tools and connected workflows you use for assessment, prescribing, documents, and saved work.",
description: "Assessment, prescribing, documents, and saved work.",
allSectionLabel: "All tools",
countNoun: "tools",
emptyTitle: "No tools match",
Expand Down Expand Up @@ -733,17 +732,15 @@ export function ApplicationsLauncherWorkspace({
aria-labelledby="tools-home-title"
className={cn(
"mx-auto w-full max-w-[90rem] overflow-x-hidden px-4 pb-8 text-[color:var(--text)] sm:px-6 lg:px-8",
// The composer sits in the hero at every width, so phones only need a
// small safe-area cushion rather than bottom-dock clearance.
"pb-[calc(2rem+env(safe-area-inset-bottom))] sm:pb-8",
"pt-7 sm:pt-10 lg:pt-14",
"pb-[calc(12rem+env(safe-area-inset-bottom))] sm:pb-8",
"pt-5 sm:pt-8 lg:pt-10",
className,
)}
>
<section
aria-label="Tools home"
data-testid="tools-home"
className="mx-auto grid max-w-5xl justify-items-center gap-3.5 text-center sm:gap-6"
className="mx-auto grid max-w-5xl justify-items-center gap-3 text-center sm:gap-4"
>
<ModeHomeHero
testId="tools-home"
Expand Down
8 changes: 4 additions & 4 deletions src/components/clinical-dashboard/differentials-home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,21 @@ type DifferentialEvidenceState = "source-backed" | "guided";
const primaryActions: DifferentialAction[] = [
{
label: "Search presentations",
description: "Explore by symptoms or scenario",
description: "By symptom or scenario.",
query: "acute confusion differential diagnosis",
icon: Search,
target: "presentations",
},
{
label: "Compare differentials",
description: "Compare likely causes side by side",
description: "Likely causes, side by side.",
query: "delirium vs dementia differential diagnosis",
icon: GitCompareArrows,
target: "diagnoses",
},
{
label: "Recent work",
description: "Continue where you left off",
description: "Pick up where you left off.",
query: "recent differential diagnosis work",
icon: Clock3,
target: "search",
Expand Down Expand Up @@ -1272,7 +1272,7 @@ export function DifferentialsHome({
<ModeHomeTemplate
testId="differentials-home-template"
title="Differentials"
subtitle="Search your imported differentials catalogue against matches from your indexed library."
subtitle="Match your catalogue against your indexed library."
icon={BrainCircuit}
headingLevel={1}
desktopComposerSlotId={desktopComposerSlotId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,19 +310,19 @@ function DocumentSearchHome({
const startItems = [
{
label: "Recent documents",
description: "Continue reading where you left off",
description: "Pick up where you left off.",
icon: Clock3,
action: onOpenRecentDocuments,
},
{
label: "Browse library",
description: "Search all indexed sources",
description: "All indexed sources.",
icon: FolderOpen,
action: onOpenLibrary,
},
{
label: "Open a source PDF",
description: "View original source files",
description: "Original source files.",
icon: ExternalLink,
action: onOpenSourcePdf,
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/clinical-dashboard/favourites-hub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,11 @@ export function FavouritesHub({

return (
<div data-testid="favourites-hub" className="mx-auto w-full max-w-6xl space-y-4 overflow-x-hidden sm:space-y-5">
<div className="mx-auto grid w-full max-w-5xl justify-items-center gap-3.5 pt-4 text-center sm:gap-6 sm:pt-7">
<div className="mx-auto grid w-full max-w-5xl justify-items-center gap-3 pt-3 text-center sm:gap-4 sm:pt-5">
<ModeHomeHero
testId="favourites-home"
title="Favourites"
subtitle="Keep trusted notes, sources, medication pages, and clinical sets ready for reuse."
subtitle="Notes, sources, medication pages, and clinical sets, ready to reuse."
icon={Heart}
headingLevel={headingLevel}
compact
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ function MedicationHome({
actionsLabel="Medication prompts"
actions={medicationPrompts.map((prompt) => ({
title: prompt.label,
description: prompt.description,
description: "Prescribing-focused search.",
icon: prompt.icon,
onClick: () => onSuggestedSearch(prompt.label),
disabled: loading,
Expand Down
10 changes: 5 additions & 5 deletions src/components/forms/forms-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ import { countVerifiedRegistryRecords, useRegistryRecords } from "@/lib/use-regi
const taskCards: ModeHomeAction[] = [
{
title: "Find a form",
description: "Search by number, pathway, clock, or keyword.",
description: "Number, pathway, clock, keyword.",
icon: Search,
href: appModeHomeHref("forms", { focus: true }),
},
{
title: "Readiness checks",
description: "Review maker, clock, copies, and source.",
description: "Maker, clock, copies, source.",
icon: ClipboardCheck,
href: `/forms/${defaultFormSlug() ?? ""}`,
},
{
title: "Browse pathways",
description: "Before, current, parallel, and after forms.",
description: "Before, current, parallel, after.",
icon: Route,
href: appModeHomeHref("forms", {
query: "forms pathway before current parallel after",
Expand Down Expand Up @@ -113,13 +113,13 @@ export function FormsHomePage() {
<ModeHomeMain testId="forms-home">
<ModeHomeTemplate
testId="forms-home-template"
title="What do you need from forms?"
title="Forms"
subtitle="Search, check, or follow a pathway."
icon={FileText}
desktopComposerSlotId={modeHomeDesktopComposerSlotId}
actionsLabel="Forms tasks"
actions={hasRegistryRecords ? taskCards : []}
pillsTitle="Common tasks"
pillsTitle="Browse by type"
pills={hasRegistryRecords ? commonTasks : []}
footer={
hasRegistryRecords ? (
Expand Down
26 changes: 13 additions & 13 deletions src/components/mode-home-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,30 +75,30 @@ export function ModeHomeHero({

return (
<section
className={cn("grid justify-items-center px-4 sm:gap-3 sm:px-0", compact ? "gap-2" : "gap-3")}
className={cn("grid justify-items-center px-4 sm:gap-3 sm:px-0", compact ? "gap-1.5" : "gap-3")}
aria-labelledby={`${testId ?? "mode-home"}-title`}
>
<span
className={cn(
"mode-home-icon grid place-items-center rounded-2xl border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)] sm:h-14 sm:w-14",
compact ? "h-11 w-11" : "h-12 w-12",
"mode-home-icon grid place-items-center rounded-2xl border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)] sm:h-12 sm:w-12 lg:h-14 lg:w-14 lg:rounded-[1.15rem]",
compact ? "h-11 w-11" : "h-14 w-14",
)}
>
<Icon className={cn("sm:h-7 sm:w-7", compact ? "h-5 w-5" : "h-6 w-6")} aria-hidden="true" />
<Icon className={cn("sm:h-6 sm:w-6 lg:h-7 lg:w-7", compact ? "h-5 w-5" : "h-7 w-7")} aria-hidden="true" />
</span>
<div className={cn("grid", compact ? "gap-1.5" : "gap-2")}>
<div className={cn("grid", compact ? "gap-1 sm:gap-1.5" : "gap-2")}>
<Heading
id={`${testId ?? "mode-home"}-title`}
className={cn(
"text-balance font-extrabold leading-[1.1] tracking-normal text-[color:var(--text-heading)] sm:text-3xl lg:text-4xl",
compact ? "text-2xl" : "text-3xl-minus",
"text-balance font-extrabold leading-[1.05] tracking-normal text-[color:var(--text-heading)] sm:text-[1.9rem] lg:text-[2.15rem]",
compact ? "text-[1.45rem]" : "text-[1.85rem]",
)}
>
{title}
</Heading>
<p
className={cn(
"mx-auto max-w-2xl text-pretty text-sm font-medium text-[color:var(--text-muted)] sm:text-base sm:leading-6",
"mx-auto max-w-2xl text-pretty text-sm font-medium text-[color:var(--text-muted)] sm:text-[0.95rem] sm:leading-5 lg:text-base",
compact ? "leading-5" : "leading-6",
)}
>
Expand Down Expand Up @@ -219,7 +219,7 @@ export function ModeHomeTemplate({
<div
data-testid={testId}
className={cn(
"mode-home-template mx-auto box-border flex w-full max-w-none flex-col items-center justify-center gap-3.5 px-0 text-center sm:max-w-[58rem] sm:gap-5",
"mode-home-template mx-auto box-border flex w-full max-w-none flex-col items-center justify-center gap-3.5 px-0 text-center sm:max-w-[60rem] sm:gap-4 lg:gap-5",
className,
)}
>
Expand All @@ -235,7 +235,7 @@ export function ModeHomeTemplate({
{actions?.length ? (
<section
aria-label={actionsLabel}
className="grid w-full max-w-none overflow-hidden rounded-none border-y border-[color:var(--border)] bg-[color:var(--surface)] shadow-[var(--shadow-card)] sm:max-w-none sm:grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] sm:gap-4 sm:overflow-visible sm:rounded-xl sm:border sm:shadow-[var(--shadow-card)] lg:rounded-none lg:border-0 lg:bg-transparent lg:shadow-none"
className="grid w-full max-w-none overflow-hidden rounded-none border-y border-[color:var(--border)] bg-[color:var(--surface)] shadow-[var(--shadow-card)] sm:max-w-none sm:grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] sm:gap-3 sm:overflow-visible sm:rounded-xl sm:border sm:shadow-[var(--shadow-card)] lg:rounded-none lg:border-0 lg:bg-transparent lg:shadow-none"
>
{actions.map((action, index) => {
const ActionIcon = action.icon;
Expand All @@ -245,10 +245,10 @@ export function ModeHomeTemplate({
<ActionIcon className="h-5 w-5 sm:h-6 sm:w-6" aria-hidden="true" />
</span>
<span className="min-w-0">
<span className="block text-balance text-base font-bold leading-5 text-[color:var(--text-heading)] [overflow-wrap:anywhere]">
<span className="block text-balance text-[0.98rem] font-bold leading-5 text-[color:var(--text-heading)] [overflow-wrap:anywhere] sm:text-[0.95rem]">
{action.title}
</span>
<span className="mt-1 block text-xs font-medium leading-5 text-[color:var(--text-muted)] sm:text-sm sm:leading-5">
<span className="mt-1 block text-xs font-medium leading-5 text-[color:var(--text-muted)] sm:text-[0.8rem] sm:leading-[1.3]">
{action.description}
</span>
</span>
Expand All @@ -259,7 +259,7 @@ export function ModeHomeTemplate({
</>
);
const actionClassName = cn(
"mode-home-action group grid min-h-[4.8rem] w-full grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] items-center gap-3 bg-[color:var(--surface)] px-4 py-3 text-left transition hover:bg-[color:var(--surface-subtle)] focus-visible:relative focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)] disabled:cursor-wait disabled:opacity-60 sm:min-h-[6.25rem] sm:grid-cols-[2.75rem_minmax(0,1fr)_1.25rem] sm:gap-3.5 sm:rounded-lg sm:border sm:border-[color:var(--border)] sm:px-4 sm:py-4 sm:shadow-[var(--shadow-card)] lg:px-5",
"mode-home-action group grid min-h-[4.4rem] w-full grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] items-center gap-3 bg-[color:var(--surface)] px-4 py-3 text-left transition hover:bg-[color:var(--surface-subtle)] focus-visible:relative focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)] disabled:cursor-wait disabled:opacity-60 sm:min-h-[4.75rem] sm:grid-cols-[2.75rem_minmax(0,1fr)_1rem] sm:gap-3 sm:rounded-lg sm:border sm:border-[color:var(--border)] sm:px-4 sm:py-3.5 sm:shadow-[var(--shadow-card)] lg:min-h-[4.75rem] lg:px-5",
index > 0 && "border-t border-[color:var(--border)] sm:border-t-[color:var(--border)]",
);

Expand Down
4 changes: 2 additions & 2 deletions src/components/services/services-home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import { countVerifiedRegistryRecords, useRegistryRecords } from "@/lib/use-regi
const taskCards: ModeHomeAction[] = [
{
title: "Search services",
description: "Search by need, catchment, provider, or keyword.",
description: "Need, catchment, provider, keyword.",
icon: FileSearch,
href: appModeHomeHref("services", { focus: true }),
},
{
title: "Check catchment",
description: "Confirm region, public/private, and eligibility.",
description: "Region, public/private, eligibility.",
icon: MapPinned,
href: `/services/${defaultServiceSlug() ?? ""}`,
},
Expand Down
26 changes: 15 additions & 11 deletions tests/ui-tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,15 @@ test.describe("Clinical KB tools launcher", () => {
await page.setViewportSize({ width: 1280, height: 900 });
await gotoLauncher(page, "/?mode=answer");

const initialMenu = await openAppModeMenu(page, "Answer");
await expect(initialMenu).toBeVisible();
await initialMenu.getByRole("menuitemradio", { name: /^Services\b/ }).click();
// Re-open + re-click on each retry: a single click can be swallowed while the
// launcher is still hydrating, leaving the route on /?mode=answer. Mirrors the
// Forms switch below, which already guards against the same flake.
await expect(async () => {
if (/\/services$/.test(page.url())) return;
const menu = await openAppModeMenu(page, "Answer");
await menu.getByRole("menuitemradio", { name: /^Services\b/ }).click();
await expect(page).toHaveURL(/\/services$/, { timeout: 3_000 });
}).toPass({ timeout: 20_000 });

await expect(page).toHaveURL(/\/services$/);
await expect(page.getByRole("button", { name: "Mode Services" })).toBeVisible();
Expand Down Expand Up @@ -461,7 +467,7 @@ test.describe("Clinical KB tools launcher", () => {
for (const home of [
{ path: "/?mode=answer", testId: "answer-empty-state", heading: "How can I help?", headingLevel: 2 },
{ path: "/services", testId: "services-home", heading: "Find a service", headingLevel: 1 },
{ path: "/forms", testId: "forms-home", heading: "What do you need from forms?", headingLevel: 1 },
{ path: "/forms", testId: "forms-home", heading: "Forms", headingLevel: 1 },
{ path: "/differentials", testId: "differentials-home", heading: "Differentials", headingLevel: 1 },
{ path: "/favourites", testId: "favourites-hub", heading: "Favourites command library", headingLevel: 1 },
{ path: "/applications", testId: "tools-home", heading: "Tools", headingLevel: 1 },
Expand Down Expand Up @@ -577,10 +583,10 @@ test.describe("Clinical KB tools launcher", () => {
};
if (!baseline) {
baseline = metrics;
// Compact hero mobile scale: 2.75rem icon, 1.5rem heading, 0.875rem subtitle.
// Compact hero mobile scale: 2.75rem icon, 1.45rem heading, 0.875rem subtitle.
expect(metrics.iconWidth).toBe(44);
expect(metrics.iconHeight).toBe(44);
expect(metrics.headingFontSize).toBeCloseTo(24, 1);
expect(metrics.headingFontSize).toBeCloseTo(23.2, 1);
expect(metrics.subtitleFontSize).toBeCloseTo(14, 1);
} else {
expect(metrics, `${home.path} hero metrics`).toEqual(baseline);
Expand Down Expand Up @@ -664,7 +670,7 @@ test.describe("Clinical KB tools launcher", () => {
headingLevel: 2,
},
{ path: "/services", testId: "services-home", heading: "Find a service", headingLevel: 1 },
{ path: "/forms", testId: "forms-home", heading: "What do you need from forms?", headingLevel: 1 },
{ path: "/forms", testId: "forms-home", heading: "Forms", headingLevel: 1 },
{ path: "/differentials", testId: "differentials-home", heading: "Differentials", headingLevel: 1 },
{ path: "/applications", testId: "tools-home", heading: "Tools", headingLevel: 1 },
] as const) {
Expand Down Expand Up @@ -934,15 +940,13 @@ test.describe("Clinical KB tools launcher", () => {
await expect(page).toHaveURL(/\/forms$/);
await expect(page.getByRole("button", { name: "Mode Forms" })).toBeVisible();
await expect(page.getByTestId("forms-home")).toBeVisible();
await expect(page.getByRole("heading", { level: 1, name: "What do you need from forms?" })).toBeVisible();
await expect(page.getByRole("heading", { level: 1, name: "Forms" })).toBeVisible();
await expect(page.getByTestId("services-home")).toHaveCount(0);
await expect(page.getByTestId("global-search-input")).toHaveCount(1);
const formsHomeSearch = page.getByTestId("forms-home").getByTestId("global-search-input");
await expect(formsHomeSearch).toBeVisible();
const formsSearchBox = await formsHomeSearch.boundingBox();
const formsHeadingBox = await page
.getByRole("heading", { level: 1, name: "What do you need from forms?" })
.boundingBox();
const formsHeadingBox = await page.getByRole("heading", { level: 1, name: "Forms" }).boundingBox();
expect(formsSearchBox).not.toBeNull();
expect(formsHeadingBox).not.toBeNull();
expect((formsHeadingBox?.y ?? 0) + (formsHeadingBox?.height ?? 0)).toBeLessThan(formsSearchBox?.y ?? 0);
Expand Down
Loading