From 2763a64b83c6a6ebd0b7a61c5915d60cff453a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 4 Jun 2026 13:25:55 +0200 Subject: [PATCH] fix(dashboard): hide iOS-pin-only widgets from the web settings list The eight v1.10 additive HealthKit signals (cardio recovery, six-minute walk, stair ascent/descent speed, breathing disturbances, wrist temperature, falls, walking steadiness) are writable dashboard widget ids so the native client's Home-pin PUT validates them, but the web dashboard renders strip tiles via hardcoded per-id blocks and has no render path for them. Left as-is, the web Settings -> Dashboard list would offer eight toggles that do nothing on web. Name them in a new IOS_PIN_ONLY_WIDGET_IDS set (writable-but-not- web-rendered) and filter that set out of the rendered settings list, so the toggles never appear on web while the ids stay accepted by the widgets PUT enum for the native client. Add tests asserting the eight remain writable (catalogue membership) yet are absent from the web list. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../dashboard-layout-section.test.tsx | 72 ++++++++++++++++--- .../settings/dashboard-layout-section.tsx | 14 +++- src/lib/__tests__/dashboard-layout.test.ts | 45 ++++++++++++ src/lib/dashboard-layout.ts | 29 ++++++++ 4 files changed, 147 insertions(+), 13 deletions(-) diff --git a/src/components/settings/__tests__/dashboard-layout-section.test.tsx b/src/components/settings/__tests__/dashboard-layout-section.test.tsx index 3ea8f1d2b..81edb4ead 100644 --- a/src/components/settings/__tests__/dashboard-layout-section.test.tsx +++ b/src/components/settings/__tests__/dashboard-layout-section.test.tsx @@ -4,9 +4,18 @@ import { renderToStaticMarkup } from "react-dom/server"; import { DEFAULT_DASHBOARD_LAYOUT, DASHBOARD_IOS_ONLY_WIDGET_IDS, + DASHBOARD_WIDGET_IDS, + IOS_PIN_ONLY_WIDGET_IDS, type DashboardLayout, } from "@/lib/dashboard-layout"; +// v1.11.2 HIGH-1 — the web Settings list renders one row per WRITABLE id +// (`DASHBOARD_WIDGET_IDS`) MINUS the `IOS_PIN_ONLY_WIDGET_IDS` (writable so +// the iOS pin PUT validates, but with no web render path). The default +// layout still carries all 24 writable widgets; only this subset renders. +const WEB_RENDERABLE_ROW_COUNT = + DASHBOARD_WIDGET_IDS.length - IOS_PIN_ONLY_WIDGET_IDS.length; + // Mutable holder so individual tests can inject a layout (e.g. one that // carries iOS-only ids) into the mocked `useQuery` without re-mocking // the module. Defaults to the 16-tile web default layout. @@ -88,10 +97,10 @@ describe(" — tile + chart split", () => { const html = render(); const tileSwitches = html.match(/data-slot="widget-tile-switch"/g) ?? []; const chartSwitches = html.match(/data-slot="widget-chart-switch"/g) ?? []; - // Default layout has 13 widgets — both switch counts must match - // exactly so the split control covers every widget. - expect(tileSwitches).toHaveLength(DEFAULT_DASHBOARD_LAYOUT.widgets.length); - expect(chartSwitches).toHaveLength(DEFAULT_DASHBOARD_LAYOUT.widgets.length); + // One tile + one chart switch per WEB-renderable widget (the + // iOS-pin-only ids are filtered out — they have no web render path). + expect(tileSwitches).toHaveLength(WEB_RENDERABLE_ROW_COUNT); + expect(chartSwitches).toHaveLength(WEB_RENDERABLE_ROW_COUNT); }); }); @@ -108,7 +117,7 @@ describe(" — drag-and-drop reorder", () => { it("paints a drag handle for every widget row", () => { const html = render(); const handles = html.match(/data-slot="widget-drag-handle"/g) ?? []; - expect(handles).toHaveLength(DEFAULT_DASHBOARD_LAYOUT.widgets.length); + expect(handles).toHaveLength(WEB_RENDERABLE_ROW_COUNT); }); it("each drag handle has an aria-describedby pointing to a shared hint", () => { @@ -117,7 +126,7 @@ describe(" — drag-and-drop reorder", () => { // to care about attribute ordering inside the rendered