From d9f0051ef8335817e7fa29aeb02ee6324331df39 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:00:23 +0800 Subject: [PATCH 1/2] feat(ui): replace mobile search filter rails with selects --- src/components/applications-launcher-page.tsx | 42 +++---- .../clinical-dashboard/differentials-home.tsx | 19 +++- .../document-search-results.tsx | 22 +++- .../medication-prescribing-workspace.tsx | 18 ++- .../search-results-header-band.tsx | 107 ++++++++++++++++-- .../factsheets/factsheets-search-page.tsx | 17 ++- .../formulation/formulation-home-page.tsx | 38 ++++++- .../services/services-navigator-page.tsx | 22 ++++ .../specifiers/specifiers-home-page.tsx | 27 ++++- .../therapy-compass/screens/search-screen.tsx | 55 ++++++++- tests/search-results-header-band.dom.test.tsx | 48 +++++++- tests/ui-accessibility.spec.ts | 57 ++++++---- tests/ui-formulation.spec.ts | 5 +- tests/ui-smoke.spec.ts | 31 +++-- tests/ui-specifiers.spec.ts | 15 ++- tests/ui-stress.spec.ts | 9 +- tests/ui-tools.spec.ts | 86 ++++++++------ 17 files changed, 490 insertions(+), 128 deletions(-) diff --git a/src/components/applications-launcher-page.tsx b/src/components/applications-launcher-page.tsx index f7770bc72..009abe5c5 100644 --- a/src/components/applications-launcher-page.tsx +++ b/src/components/applications-launcher-page.tsx @@ -24,7 +24,10 @@ import { import { type FormEvent, useMemo, useState } from "react"; import { ModeHomeHero, ModeHomeVerificationFooter } from "@/components/mode-home-template"; -import { SearchResultsHeaderBand } from "@/components/clinical-dashboard/search-results-header-band"; +import { + MobileResultFilterControl, + SearchResultsHeaderBand, +} from "@/components/clinical-dashboard/search-results-header-band"; import { useSearchCommand } from "@/components/clinical-dashboard/search-command-context"; import { useFavouritesAccess } from "@/components/clinical-dashboard/use-favourites-access"; import { cn, toneInfo, toneSuccess, toneWarning } from "@/components/ui-primitives"; @@ -392,34 +395,15 @@ function FilterTabs({ ); })} -
- {mobileFilters.map((filter) => { - const active = filter.id === activeFilter || (filter.id === "all" && activeFilter === "saved"); - return ( - - ); - })} -
+ ({ value: filter.id, label: filter.label }))} + onChange={onFilterChange} + /> ); } diff --git a/src/components/clinical-dashboard/differentials-home.tsx b/src/components/clinical-dashboard/differentials-home.tsx index 03b353317..246d1cc43 100644 --- a/src/components/clinical-dashboard/differentials-home.tsx +++ b/src/components/clinical-dashboard/differentials-home.tsx @@ -24,7 +24,10 @@ import { } from "lucide-react"; import { ModeHomeTemplate, ModeHomeVerificationFooter } from "@/components/mode-home-template"; -import { SearchResultsHeaderBand } from "@/components/clinical-dashboard/search-results-header-band"; +import { + MobileResultFilterControl, + SearchResultsHeaderBand, +} from "@/components/clinical-dashboard/search-results-header-band"; import { UniversalSearchAlsoMatches } from "@/components/clinical-dashboard/universal-search-also-matches"; import { useDifferentialSearch } from "@/components/clinical-dashboard/use-differential-catalog"; import { useResultSort } from "@/components/use-result-sort"; @@ -908,6 +911,20 @@ function SearchResultsView({ sortValue={sortValue} onSortChange={setSortValue} filterLabel="Filter differential result type" + mobileControls={ + + } filterControls={ onResultTypeChange(tab.key)} className={cn( - "inline-flex min-h-tap shrink-0 items-center gap-1.5 rounded-lg border px-2.5 text-2xs font-bold transition motion-reduce:transition-none sm:min-h-9 sm:text-xs", + "inline-flex min-h-tap shrink-0 items-center gap-1.5 rounded-lg border px-2.5 text-2xs font-bold transition motion-reduce:transition-none sm:text-xs", "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]", active ? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)]" @@ -903,6 +906,21 @@ function DocumentSearchResultsPanelImpl({ ) : null } filterLabel="Filter documents by source type" + mobileControls={ + showResultsControls && resultTabs.length > 1 ? ( + ({ + value: tab.key, + label: `${tab.label} (${tab.count})`, + }))} + onChange={setActiveResultType} + /> + ) : null + } filterControls={ showResultsControls && resultTabs.length > 1 ? ( ({ + value: filter.id, + label: `${filter.label} (${counts[filter.id]})`, + }))} + onChange={setActiveFilter} + /> + } filterControls={} /> diff --git a/src/components/clinical-dashboard/search-results-header-band.tsx b/src/components/clinical-dashboard/search-results-header-band.tsx index 02fd49504..272d19a7f 100644 --- a/src/components/clinical-dashboard/search-results-header-band.tsx +++ b/src/components/clinical-dashboard/search-results-header-band.tsx @@ -23,6 +23,7 @@ export function SearchResultsHeaderBand({ onSortChange, onSaveSearch, utilityControls, + mobileControls, filterControls, filterLabel = "Filter search results", headingLevel = 2, @@ -39,6 +40,8 @@ export function SearchResultsHeaderBand({ onSaveSearch?: () => void; /** Page-specific actions that belong beside sort/view controls. */ utilityControls?: ReactNode; + /** Compact page-specific controls shown in the utility row below `sm`. */ + mobileControls?: ReactNode; /** Page-specific filters rendered as a full-width row within the shared ribbon. */ filterControls?: ReactNode; filterLabel?: string; @@ -56,7 +59,8 @@ export function SearchResultsHeaderBand({ const displayQuery = query.trim() || "All"; const statusLabel = loading ? "Searching…" : `${matchCount} ${matchCount === 1 ? "match" : "matches"}`; const hasUtilities = - visibleScopes.length > 0 || Boolean(onSortChange || onViewChange || onSaveSearch || utilityControls); + visibleScopes.length > 0 || + Boolean(onSortChange || onViewChange || onSaveSearch || utilityControls || mobileControls); const QueryHeading = headingLevel === 1 ? "h1" : "h2"; return ( @@ -133,14 +137,43 @@ export function SearchResultsHeaderBand({ ))} - {onSortChange ? ( - - ) : null} + {onSortChange && mobileControls ? ( +
+ +
+ {mobileControls} +
+
+ ) : ( + <> + {onSortChange ? ( + + ) : null} + {mobileControls ? ( +
+ {mobileControls} +
+ ) : null} + + )} {utilityControls} {onViewChange ? (
{filterControls}
@@ -225,7 +261,7 @@ export function ResultSortControl({ return (