From 30d1b3649f20dd27c5a31419b58cd8eac2290029 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Mon, 10 Aug 2026 18:54:18 +0800 Subject: [PATCH 1/2] fix(therapy): tighten mobile result cards --- .../therapy-compass/therapy-card.tsx | 56 +++++++------ ...herapy-compass-responsive-contract.test.ts | 20 ++++- tests/ui-route-coverage.spec.ts | 81 +++++++++++++++++++ 3 files changed, 131 insertions(+), 26 deletions(-) diff --git a/src/components/therapy-compass/therapy-card.tsx b/src/components/therapy-compass/therapy-card.tsx index 0b6eab6d1a..516b90c734 100644 --- a/src/components/therapy-compass/therapy-card.tsx +++ b/src/components/therapy-compass/therapy-card.tsx @@ -6,7 +6,7 @@ import { ignoreUnavailableActivation } from "@/components/ui-primitives"; import { useTcBindings } from "./bindings"; import { summarise } from "./data/select"; import type { Therapy } from "./data/types"; -import { accentControl, outlineControl, therapyBtn } from "./controls"; +import { accentControl, iconControl, outlineControl, therapyBtn } from "./controls"; import { AlertIcon, ChevronRightIcon, @@ -24,22 +24,25 @@ export function ResultCard({ therapy }: { therapy: Therapy }) { const b = useTcBindings(); const inCompare = b.isInCompare(therapy.slug); return ( -
-
-
- -
-

- {therapy.name} -

-

- {summarise(therapy.clinicalSummary, 1) || therapy.bestUsedFor || therapy.category} -

- -
+
+
+
+

+ {therapy.name} +

+

+ {summarise(therapy.clinicalSummary, 1) || therapy.bestUsedFor || therapy.category} +

+
-
+
-
+
-
+
@@ -126,7 +134,7 @@ function CardCell({ }) { return (
diff --git a/tests/therapy-compass-responsive-contract.test.ts b/tests/therapy-compass-responsive-contract.test.ts index b124b2c0db..dc6afadcf6 100644 --- a/tests/therapy-compass-responsive-contract.test.ts +++ b/tests/therapy-compass-responsive-contract.test.ts @@ -103,7 +103,6 @@ describe("Therapy Compass responsive contract", () => { }); it("marks every fixed screen/card grid for phone reflow without changing its desktop template", () => { - expect(responsiveStackCount(therapyCardSource)).toBeGreaterThanOrEqual(1); expect(homeSource).toContain("ModeHomeMain"); expect(homeSource).toContain("ModeHomeTemplate"); expect(modeHomeTemplateSource).toContain("sm:grid-cols-[repeat(auto-fit,minmax(15rem,1fr))]"); @@ -146,6 +145,22 @@ describe("Therapy Compass responsive contract", () => { expect(allScreens).toMatch(/sm:grid-cols-\[/); }); + it("lets result-card evidence and actions use the phone width without forcing the desktop grid", () => { + const resultCardSource = therapyCardSource.slice( + therapyCardSource.indexOf("export function ResultCard"), + therapyCardSource.indexOf("function CardCell"), + ); + + expect(resultCardSource).toContain("data-therapy-result-card"); + expect(resultCardSource).toContain("grid-cols-[minmax(0,1fr)_auto]"); + expect(resultCardSource).toContain("lg:grid-cols-[minmax(280px,1fr)_minmax(400px,1.35fr)_auto]"); + expect(resultCardSource).toContain("col-span-2 -mx-4"); + expect(resultCardSource).toContain("sm:grid-cols-3"); + expect(resultCardSource).toMatch(/data-therapy-result-actions\s+className="grid grid-cols-3/); + expect(resultCardSource).toContain('Open'); + expect(resultCardSource).not.toContain(" { const favouriteButton = therapyCardSource.match( //, @@ -160,7 +175,8 @@ describe("Therapy Compass responsive contract", () => { expect(favouriteButton).not.toMatch(/(^|\s)disabled(\s|=|$)/); expect(favouriteButton).toContain("onClick={ignoreUnavailableActivation}"); expect(favouriteButton).toContain('aria-label="Favourite saving is not available yet"'); - expect(favouriteButton).toContain("cursor-not-allowed"); + expect(favouriteButton).toContain("className={iconControl}"); + expect(controlsSource).toContain("aria-disabled:cursor-not-allowed"); }); it("uses complete toggle semantics and preserves full-size control hit targets", () => { diff --git a/tests/ui-route-coverage.spec.ts b/tests/ui-route-coverage.spec.ts index 0d76b29314..33ef730bbc 100644 --- a/tests/ui-route-coverage.spec.ts +++ b/tests/ui-route-coverage.spec.ts @@ -275,6 +275,87 @@ test.describe("previously uncovered production routes", () => { ); }); + test("Therapy result cards use the full phone width with a symmetric action row", async ({ page }, testInfo) => { + await page.setViewportSize({ width: 390, height: 844 }); + await gotoApp(page, "/therapy-compass/search?q=CBT&run=1"); + + const card = page.locator("[data-therapy-result-card]").first(); + await expect(card).toBeVisible({ timeout: 30_000 }); + + for (const width of [320, 390, 639, 768, 1440, 1920]) { + await page.setViewportSize({ width, height: width < 768 ? 844 : 900 }); + await expectNoHorizontalOverflow(page); + + const layout = await card.evaluate((element) => { + const bounds = element.getBoundingClientRect(); + const copy = element.querySelector("[data-therapy-result-copy]")!.getBoundingClientRect(); + const evidence = element.querySelector("[data-therapy-result-evidence]")!.getBoundingClientRect(); + const actions = element.querySelector("[data-therapy-result-actions]")!; + const buttons = [...actions.querySelectorAll("button")].map((button) => { + const buttonBounds = button.getBoundingClientRect(); + return { + left: buttonBounds.left, + right: buttonBounds.right, + top: buttonBounds.top, + width: buttonBounds.width, + height: buttonBounds.height, + }; + }); + return { + card: { left: bounds.left, right: bounds.right }, + copyLeft: copy.left, + evidence: { left: evidence.left, right: evidence.right }, + buttons, + }; + }); + + if (width < 640) { + expect(Math.abs(layout.evidence.left - layout.card.left), `${width}px evidence left edge`).toBeLessThanOrEqual( + 1, + ); + expect( + Math.abs(layout.card.right - layout.evidence.right), + `${width}px evidence right edge`, + ).toBeLessThanOrEqual(1); + expect(layout.copyLeft - layout.card.left, `${width}px copy inset`).toBeGreaterThanOrEqual(15); + expect(new Set(layout.buttons.map((button) => Math.round(button.top))).size, `${width}px button row`).toBe(1); + expect( + Math.max(...layout.buttons.map((button) => button.width)) - + Math.min(...layout.buttons.map((button) => button.width)), + `${width}px equal action widths`, + ).toBeLessThanOrEqual(1); + for (const button of layout.buttons) { + expect(button.height, `${width}px action target`).toBeGreaterThanOrEqual(48); + } + } + } + + await page.setViewportSize({ width: 390, height: 844 }); + await page.emulateMedia({ reducedMotion: "reduce" }); + const compare = card.locator("[data-therapy-result-actions] button").nth(1); + await expect(compare).toHaveAccessibleName("Compare"); + await compare.focus(); + const focusStyle = await compare.evaluate((element) => { + const style = getComputedStyle(element); + return { outlineStyle: style.outlineStyle, outlineWidth: Number.parseFloat(style.outlineWidth) }; + }); + expect(focusStyle.outlineStyle).not.toBe("none"); + expect(focusStyle.outlineWidth).toBeGreaterThanOrEqual(2); + await testInfo.attach("therapy-result-card-phone", { + body: await card.screenshot(), + contentType: "image/png", + }); + + await page.emulateMedia({ forcedColors: "active", reducedMotion: "reduce" }); + await expectNoHorizontalOverflow(page); + await expect(card.locator("[data-therapy-result-actions] button")).toHaveCount(3); + + await compare.focus(); + await page.keyboard.press("Space"); + await expect(page).toHaveURL(/\/therapy-compass\/compare$/); + await expect(page.getByRole("heading", { name: "Therapy Comparison", level: 1 })).toBeVisible(); + }); + test("DSM home renders responsively and opens comparison", async ({ page }) => { await proveRenderedRoute( page, From f9f1533ded57d2e4b644dd18249442d7c4fe7d15 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Mon, 10 Aug 2026 19:27:13 +0800 Subject: [PATCH 2/2] fix(therapy): preserve full-width mobile evidence --- src/components/therapy-compass/therapy-card.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/therapy-compass/therapy-card.tsx b/src/components/therapy-compass/therapy-card.tsx index 01ddd0ad9b..ffc98e1914 100644 --- a/src/components/therapy-compass/therapy-card.tsx +++ b/src/components/therapy-compass/therapy-card.tsx @@ -61,8 +61,8 @@ export function ResultCard({ therapy }: { therapy: Therapy }) { > -
-
+
+

{therapy.name}