From 4b50ac8b6abd642284e19cd36644395c67f71a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 8 Jul 2026 00:03:04 +0200 Subject: [PATCH] feat(documents): make filter dropdowns responsive and update the e2e flows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give each facet trigger a leading icon and show the text label from sm up (and on phones only while the facet is active, truncated), so three facets plus the search never overflow a 360-430px row — the bar stays one line without a horizontal scroller. Drop the now-dead filter.groupLabel key and point the vault e2e flows at the dropdown menu items instead of the removed inline chips. --- e2e/documents.spec.ts | 27 +++++---- messages/de.json | 1 - messages/en.json | 1 - messages/es.json | 1 - messages/fr.json | 1 - messages/it.json | 1 - messages/pl.json | 1 - .../documents/document-filter-bar.tsx | 56 ++++++++++++++++--- 8 files changed, 66 insertions(+), 23 deletions(-) diff --git a/e2e/documents.spec.ts b/e2e/documents.spec.ts index 9d9cfb339..e11580fe6 100644 --- a/e2e/documents.spec.ts +++ b/e2e/documents.spec.ts @@ -108,7 +108,7 @@ test.describe("document vault", () => { // ── §1A — the 30-second doctor flow, three independent routes ───────── - test("doctor flow A: the type chip reaches the MRT report", async ({ + test("doctor flow A: the type filter reaches the MRT report", async ({ page, }) => { await page.goto("/documents"); @@ -116,7 +116,11 @@ test.describe("document vault", () => { page.getByRole("heading", { name: "Documents" }), ).toBeVisible(); - await page.getByRole("button", { name: "Imaging", exact: true }).click(); + // Open the type dropdown, tick Imaging (multi-select stays open), then + // dismiss the menu. + await page.getByRole("button", { name: "All types" }).click(); + await page.getByRole("menuitemcheckbox", { name: "Imaging" }).click(); + await page.keyboard.press("Escape"); // Autumn 2025 sits below the newest sections — scroll the window down. await scrollUntilVisible(page, openButton(page, "MRT Knie")); await openButton(page, "MRT Knie").click(); @@ -128,13 +132,15 @@ test.describe("document vault", () => { await expect(sheet.locator("iframe")).toBeVisible({ timeout: 5_000 }); }); - test("doctor flow B: the condition chip reaches the MRT report", async ({ + test("doctor flow B: the condition filter reaches the MRT report", async ({ page, }) => { await page.goto("/documents"); + // Open the condition dropdown and pick the linked episode (single-select + // → the menu closes on choice). + await page.locator('[data-slot="document-condition-filter"]').click(); await page - .locator('[data-slot="document-filter-bar"]') - .getByRole("button", { name: "Knie", exact: true }) + .getByRole("menuitemcheckbox", { name: "Knie", exact: true }) .click(); // The Knie filter narrows the corpus to the linked MRT trio. @@ -162,12 +168,13 @@ test.describe("document vault", () => { page, }) => { await page.goto("/documents?kind=IMAGING&year=2025"); + // A deep-linked facet surfaces on its dropdown trigger label. await expect( - page.getByRole("button", { name: "Imaging", exact: true }), - ).toHaveAttribute("aria-pressed", "true"); + page.locator('[data-slot="document-type-filter"]'), + ).toContainText("Imaging"); await expect( - page.getByRole("button", { name: "2025", exact: true }), - ).toHaveAttribute("aria-pressed", "true"); + page.locator('[data-slot="document-year-filter"]'), + ).toContainText("2025"); await scrollUntilVisible(page, openButton(page, "MRT Knie")); await page.goto(`/documents?episode=${KNIE_EPISODE_ID}`); @@ -345,7 +352,7 @@ test.describe("document vault", () => { await expect(openButton(page, "MRT Knie")).toBeVisible(); // Tab from the search into the grid's single roving slot (bounded walk - // across the chip rail), then arrows move the active card. + // across the filter dropdowns), then arrows move the active card. let reachedGrid = false; for (let i = 0; i < 40; i++) { await page.keyboard.press("Tab"); diff --git a/messages/de.json b/messages/de.json index 3e310cb6e..0e996f26f 100644 --- a/messages/de.json +++ b/messages/de.json @@ -8044,7 +8044,6 @@ "filter": { "searchLabel": "Dokumente durchsuchen", "searchPlaceholder": "Titel oder Dateiname suchen…", - "groupLabel": "Dokumente filtern", "typeGroup": "Dokumenttyp", "conditionGroup": "Erkrankung", "yearGroup": "Jahr", diff --git a/messages/en.json b/messages/en.json index 40376a2a6..bcc1379c8 100644 --- a/messages/en.json +++ b/messages/en.json @@ -8044,7 +8044,6 @@ "filter": { "searchLabel": "Search documents", "searchPlaceholder": "Search title or filename…", - "groupLabel": "Filter documents", "typeGroup": "Document type", "conditionGroup": "Condition", "yearGroup": "Year", diff --git a/messages/es.json b/messages/es.json index 8ff0241c5..8ef149419 100644 --- a/messages/es.json +++ b/messages/es.json @@ -8044,7 +8044,6 @@ "filter": { "searchLabel": "Buscar documentos", "searchPlaceholder": "Buscar por título o nombre de archivo…", - "groupLabel": "Filtrar documentos", "typeGroup": "Tipo de documento", "conditionGroup": "Afección", "yearGroup": "Año", diff --git a/messages/fr.json b/messages/fr.json index 0fa043dde..5df61b975 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -8044,7 +8044,6 @@ "filter": { "searchLabel": "Rechercher des documents", "searchPlaceholder": "Rechercher un titre ou un nom de fichier…", - "groupLabel": "Filtrer les documents", "typeGroup": "Type de document", "conditionGroup": "Affection", "yearGroup": "Année", diff --git a/messages/it.json b/messages/it.json index 3b5ed6961..306772067 100644 --- a/messages/it.json +++ b/messages/it.json @@ -8044,7 +8044,6 @@ "filter": { "searchLabel": "Cerca documenti", "searchPlaceholder": "Cerca titolo o nome file…", - "groupLabel": "Filtra documenti", "typeGroup": "Tipo di documento", "conditionGroup": "Patologia", "yearGroup": "Anno", diff --git a/messages/pl.json b/messages/pl.json index 06dba2454..e4add0d95 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -8044,7 +8044,6 @@ "filter": { "searchLabel": "Szukaj dokumentów", "searchPlaceholder": "Szukaj tytułu lub nazwy pliku…", - "groupLabel": "Filtruj dokumenty", "typeGroup": "Typ dokumentu", "conditionGroup": "Schorzenie", "yearGroup": "Rok", diff --git a/src/components/documents/document-filter-bar.tsx b/src/components/documents/document-filter-bar.tsx index c3f068873..74312ce90 100644 --- a/src/components/documents/document-filter-bar.tsx +++ b/src/components/documents/document-filter-bar.tsx @@ -19,7 +19,15 @@ * sticks below the top edge inside the shell's scroll container (sticky, * translucent background, border-b — no viewport-height tricks). */ -import { ChevronDown, ScanSearch, Search, X } from "lucide-react"; +import { + Activity, + Calendar, + ChevronDown, + ListFilter, + ScanSearch, + Search, + X, +} from "lucide-react"; import type { RefObject } from "react"; import { Button } from "@/components/ui/button"; @@ -42,6 +50,19 @@ const TRIGGER_ACTIVE = "border-primary/40 bg-primary/10 text-foreground"; /** Shared trigger chrome: compact, fixed, never shrinks below its label. */ const TRIGGER_CLASSES = "shrink-0 gap-1.5 font-normal"; +/** + * The facet label is the whole point of the trigger — but three text labels + * plus the search will not fit a 360px phone. So the label shows from `sm` + * up, and on a phone only when the facet is ACTIVE (truncated), while the + * leading icon always carries the facet identity. The trailing chevron is + * desktop-only chrome. Net: inactive phone triggers collapse to a single + * icon, the row stays one line at every width, and the accessible name is + * pinned via `aria-label` so an icon-only trigger still announces its state. + */ +function facetLabelClass(active: boolean): string { + return cn("max-w-28 truncate", active ? "inline" : "hidden sm:inline"); +} + export interface ConditionChip { episodeId: string; name: string; @@ -150,10 +171,17 @@ export function DocumentFilterBar({ variant="outline" size="sm" data-slot="document-type-filter" + aria-label={typeLabel} className={cn(TRIGGER_CLASSES, kindCount > 0 && TRIGGER_ACTIVE)} > - {typeLabel} - + + 0)}> + {typeLabel} + + - {conditionLabel} - + + + {conditionLabel} + + - {yearLabel} - + + + {yearLabel} + +