feat(dashboard): surface sleep, steps, glucose and vorsorge by default

The sleep, steps and glucose strip tiles shipped default-hidden even
though their modules are on by default and rich data flows in from the
sync integrations, so accounts had to hunt through Settings to see them.
Flip the three strip tiles to tileVisible:true and the vorsorge chart-row
card to visible:true, matching the VO2max precedent: each tile self-gates
on having data, so empty accounts keep a clean dashboard. Chart rows for
the three metrics stay off — those charts live on the /insights sub-pages.

totalBodyWater, boneMass and oxygenSaturation stay hidden: they carry as
catalogue ids for the iOS layout round-trip only and have no web render
path, so a flip would surface nothing. Note that at the splice point.

Update the silhouette skeleton and layout default tests for the new
counts.
This commit is contained in:
Marc-André Bombeck committed 2026-06-22 07:00:56 +02:00
1 parent 461190e63e
commit efd0470bc1
3 files changed
+47 -18

No files matched your search

@@ -105,10 +105,11 @@ describe("resolveDashboardFirstPaintGate", () => {
describe("resolveConfiguredTileCount (v1.16.8)", () => {
it("counts only widgets that paint a strip tile, with bp counting double", () => {
// Default layout: weight, bp, pulse, bodyFat, mood, bpInTarget, vo2Max
// are tile-visible AND tile-capable; medications + recentWorkouts are
// tile-visible in the stored layout but paint NO strip tile, so
// they must not inflate the silhouette. bp paints sys + dia = 2.
expect(resolveConfiguredTileCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(8);
// plus the v1.20.0 sleep / steps / glucose flips are tile-visible AND
// tile-capable; medications + recentWorkouts are tile-visible in the
// stored layout but paint NO strip tile, so they must not inflate the
// silhouette. bp paints sys + dia = 2.
expect(resolveConfiguredTileCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(11);
});
it("ignores chart-only and iOS-pin-only widget ids", () => {
@@ -139,9 +140,10 @@ describe("resolveConfiguredTileCount (v1.16.8)", () => {
describe("resolveChartRowPlaceholderCount (v1.16.8)", () => {
it("reserves one cell per chart-visible chart-capable widget", () => {
// Default layout chart row: weight, bp, pulse, bodyFat, mood,
// medications are `visible`; bpInTarget / achievements /
// recentWorkouts carry no chart-shaped footprint.
expect(resolveChartRowPlaceholderCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(6);
// medications are `visible`, plus the v1.20.0 vorsorge flip
// (chart-row only); bpInTarget / achievements / recentWorkouts carry
// no chart-shaped footprint.
expect(resolveChartRowPlaceholderCount(DEFAULT_DASHBOARD_LAYOUT)).toBe(7);
});
it("adds the BMI cell riding the weight gate when a height is known", () => {
@@ -149,7 +151,7 @@ describe("resolveChartRowPlaceholderCount (v1.16.8)", () => {
resolveChartRowPlaceholderCount(DEFAULT_DASHBOARD_LAYOUT, {
hasHeightCm: true,
}),
).toBe(7);
).toBe(8);
});
it("respects a trimmed layout", () => {
+18 -5
View File
@@ -469,16 +469,29 @@ describe("DASHBOARD_WIDGET_CATALOGUE_IDS — 27-id catalogue", () => {
expect(new Set(DASHBOARD_WIDGET_CATALOGUE_IDS).size).toBe(36);
});
it("registers the Vorsorge widget default-off on both surfaces", () => {
// v1.18.2 — Vorsorge becomes a first-class dashboard widget like
// medications: chart-row only, default-invisible so existing
// dashboards are unchanged until the user opts in.
it("ships the sleep / steps / glucose strip tiles default-on (v1.20.0)", () => {
// These modules are on by default and their tiles self-gate on having
// data in page.tsx, so accounts that sync sleep / step / glucose data
// discover the tiles on / without hunting through Settings. The chart
// rows stay off because those charts live on the /insights sub-pages.
for (const id of ["sleep", "steps", "glucose"]) {
const widget = DEFAULT_DASHBOARD_LAYOUT.widgets.find((w) => w.id === id);
expect(widget).toBeDefined();
expect(widget?.tileVisible).toBe(true);
expect(widget?.visible).toBe(false);
}
});
it("registers the Vorsorge widget chart-row default-on (v1.20.0)", () => {
// v1.18.2 — Vorsorge is a first-class chart-row widget (no strip tile).
// v1.20.0 flipped it default-visible so preventive-care reminders
// surface on /; it self-gates and renders nothing without due reminders.
expect(DASHBOARD_WIDGET_IDS).toContain("vorsorge");
const entry = DEFAULT_DASHBOARD_LAYOUT.widgets.find(
(w) => w.id === "vorsorge",
);
expect(entry).toBeDefined();
expect(entry?.visible).toBe(false);
expect(entry?.visible).toBe(true);
expect(entry?.tileVisible).toBe(false);
});
+19 -5
View File
@@ -367,9 +367,21 @@ export const DEFAULT_DASHBOARD_LAYOUT: DashboardLayout = {
{ id: "mood", visible: true, tileVisible: true, order: 4 },
{ id: "bpInTarget", visible: true, tileVisible: true, order: 5 },
{ id: "medications", visible: true, tileVisible: true, order: 6 },
{ id: "sleep", visible: false, tileVisible: false, order: 7 },
{ id: "steps", visible: false, tileVisible: false, order: 8 },
{ id: "glucose", visible: false, tileVisible: false, order: 9 },
// v1.20.0 — sleep / steps / glucose strip tiles default-on so accounts
// that sync these signals discover them on / without hunting through
// Settings → Dashboard. Each follows the VO2max precedent: the strip
// tile is on (`tileVisible: true`) and self-gates on having data
// (`hasSleep` / `hasSteps` / `showGlucoseCards` in page.tsx), so an
// empty account still gets a clean dashboard. The chart row stays off
// (`visible: false`) — those charts live on the /insights sub-pages.
{ id: "sleep", visible: false, tileVisible: true, order: 7 },
{ id: "steps", visible: false, tileVisible: true, order: 8 },
{ id: "glucose", visible: false, tileVisible: true, order: 9 },
// totalBodyWater / boneMass / oxygenSaturation carry as catalogue ids
// for the iOS layout round-trip only — they have no web render block or
// data-floor flag in page.tsx, so flipping their defaults would surface
// nothing on web. Their data IS discoverable via the /insights sub-pages.
// Kept false until a web render path lands (see .planning/v1200-backlog.md).
{ id: "totalBodyWater", visible: false, tileVisible: false, order: 10 },
{ id: "boneMass", visible: false, tileVisible: false, order: 11 },
{ id: "oxygenSaturation", visible: false, tileVisible: false, order: 12 },
@@ -411,8 +423,10 @@ export const DEFAULT_DASHBOARD_LAYOUT: DashboardLayout = {
{ id: "falls", visible: false, tileVisible: false, order: 22 },
{ id: "walkingSteadiness", visible: false, tileVisible: false, order: 23 },
// v1.18.2 — Vorsorge summary card. Chart-row only (no strip tile), so
// `tileVisible` is forced false; default-invisible until opt-in.
{ id: "vorsorge", visible: false, tileVisible: false, order: 24 },
// `tileVisible` is forced false. v1.20.0 — flipped `visible: true` so
// preventive-care reminders surface on / by default; the card self-gates
// (see page.tsx) and shows nothing for accounts without due reminders.
{ id: "vorsorge", visible: true, tileVisible: false, order: 24 },
],
};