Merge the final display polish into release/v1.42.0

This commit is contained in:
Marc-André Bombeck committed 2026-10-09 17:25:35 +02:00
commit dcfec1f35d
144 files changed
+4866 -2817

No files matched your search

@@ -336,8 +336,8 @@ describe("<DashboardLayoutSection> — native-only widget group (issue #581)", (
const html = render(<DashboardLayoutSection id="dashboard-layout" />);
expect(html).toContain('data-widget-id="bmi"');
expect(html).toContain('aria-label="BMI — Tile"');
expect(html).toContain('aria-label="BMI — Chart"');
expect(html).toContain('aria-label="BMI: Tile"');
expect(html).toContain('aria-label="BMI: Chart"');
// Reflects the stored flags rather than inventing a default.
const row = html.match(
/data-widget-id="bmi"[\s\S]*?(?=data-slot="native-widget-row"|$)/,
@@ -448,7 +448,7 @@ describe("<DashboardLayoutSection> — iOS-pin-only ids hidden from web (v1.11.2
html.indexOf('data-slot="native-only-widgets"'),
);
for (const id of IOS_PIN_ONLY_WIDGET_IDS) {
expect(sortableList).not.toContain(`${pinOnlyLabels[id]} — `);
expect(sortableList).not.toContain(`${pinOnlyLabels[id]}: `);
// ...and each is reachable exactly once, in the native-only group.
expect(html).toContain(`data-widget-id="${id}"`);
}
@@ -465,11 +465,11 @@ describe("<DashboardLayoutSection> — iOS-pin-only ids hidden from web (v1.11.2
describe("<DashboardLayoutSection> — disabled-module widget toggles", () => {
// The achievements widget label drives its switch aria-label; it is the
// canonical disabled-module probe (label resolves to "Achievements").
const ACHIEVEMENTS_ARIA = "Achievements — ";
const ACHIEVEMENTS_ARIA = "Achievements: ";
// Mood is a module-owned widget too; weight is a core widget with NO module
// entry, so it must survive any module-off state.
const MOOD_ARIA = "Mood — ";
const WEIGHT_ARIA = "Weight — ";
const MOOD_ARIA = "Mood: ";
const WEIGHT_ARIA = "Weight: ";
it("hides a widget toggle whose owning module is disabled", () => {
authState.modules = { achievements: false };
@@ -105,7 +105,7 @@ describe("IntegrationStatusPill", () => {
<IntegrationStatusPill state="parked" lastSyncAt={null} />,
);
expect(html).toContain("Paused");
expect(html).toContain("Paused — reconnect");
expect(html).toContain("Paused: reconnect");
expect(html).toContain('data-state="parked"');
});
@@ -115,7 +115,7 @@ describe("IntegrationStatusPill", () => {
"de",
);
expect(html).toContain("Pausiert");
expect(html).toContain("Pausiert — neu verbinden");
expect(html).toContain("Pausiert: neu verbinden");
});
// The three states the server verdict added. `stale` and `stalled` both
@@ -233,7 +233,7 @@ describe("IntegrationsSection — single-status-display contract (A5)", () => {
// The pill flips to `data-state="parked"` and the resume banner
// renders with the reconnect CTA. The legacy `error` reconnect text
// (red) must NOT appear because the user-facing copy is the lighter
// "Paused — reconnect" string, not "Error — reconnect".
// "Paused: reconnect" string, not "Error — reconnect".
it("Withings parked state surfaces the parked pill + resume CTA", () => {
setIntegrationStatus({
threshold: 3,
@@ -256,7 +256,7 @@ describe("IntegrationsSection — single-status-display contract (A5)", () => {
const html = render();
expect(html).toContain('data-state="parked"');
expect(html).toContain("Paused");
expect(html).toContain("Paused — reconnect");
expect(html).toContain("Paused: reconnect");
expect(html).toContain('data-testid="withings-parked-banner"');
expect(html).toContain('data-testid="withings-resume-button"');
// The lastError is surfaced under the pill so the operator sees
@@ -190,9 +190,9 @@ describe("<MoodSection> — SSR smoke", () => {
const groupRows = html.match(/data-slot="mood-group-row"/g);
expect(groupRows?.length ?? 0).toBe(2);
// One kebab per CUSTOM group row; the seeded row carries none.
const kebabs = html.match(/More options — Garten/g);
const kebabs = html.match(/More options: Garten/g);
expect(kebabs?.length ?? 0).toBeGreaterThanOrEqual(1);
expect(html).not.toContain("More options — Feelings");
expect(html).not.toContain("More options: Feelings");
});
it("renders custom tags by decrypted label, never the raw key", () => {
@@ -206,7 +206,7 @@ describe("<MoodSection> — SSR smoke", () => {
expect(html).toMatch(
/data-slot="mood-tag-manage-row"[^>]*data-hidden="true"/,
);
expect(html).toContain("Show tag — stressed");
expect(html).toContain("Show tag: stressed");
});
it("surfaces the usage count badge", () => {
@@ -219,7 +219,7 @@ describe("<MoodSection> — SSR smoke", () => {
expect(html).toContain('data-slot="mood-archived-row"');
expect(html).toContain("Altes Tag");
expect(html).toContain("Restore");
expect(html).toContain("Delete permanently — Altes Tag");
expect(html).toContain("Delete permanently: Altes Tag");
// The active custom tag must NOT appear in the archived card.
const archivedCard = html.slice(
html.indexOf('data-slot="mood-archived-tags-card"'),
@@ -900,7 +900,7 @@ function SortableWidgetRow({
type="button"
{...attributes}
{...listeners}
aria-label={`${labels.dragHandle} — ${labels.widgetLabel}`}
aria-label={`${labels.dragHandle}: ${labels.widgetLabel}`}
// v1.4.47 W4 — `aria-describedby` is set after `{...attributes}`
// so our shared hint paragraph wins over dnd-kit's own announcer
// hookup. The announcer still fires on drag-start / drag-over /
@@ -930,7 +930,7 @@ function SortableWidgetRow({
<Switch
checked={tileChecked}
onCheckedChange={(v) => onToggleTile(widget.id, v)}
aria-label={`${labels.widgetLabel} — ${labels.tileColumn}`}
aria-label={`${labels.widgetLabel}: ${labels.tileColumn}`}
disabled={disabled}
data-slot="widget-tile-switch"
/>
@@ -939,7 +939,7 @@ function SortableWidgetRow({
<Switch
checked={widget.visible}
onCheckedChange={(v) => onToggleChart(widget.id, v)}
aria-label={`${labels.widgetLabel} — ${labels.chartColumn}`}
aria-label={`${labels.widgetLabel}: ${labels.chartColumn}`}
disabled={disabled}
data-slot="widget-chart-switch"
/>
@@ -1012,7 +1012,7 @@ function StaticWidgetRow({
<Switch
checked={tileChecked}
onCheckedChange={(v) => onToggleTile(widget.id, v)}
aria-label={`${labels.widgetLabel} — ${labels.tileColumn}`}
aria-label={`${labels.widgetLabel}: ${labels.tileColumn}`}
disabled={disabled}
data-slot="widget-tile-switch"
/>
@@ -1021,7 +1021,7 @@ function StaticWidgetRow({
<Switch
checked={widget.visible}
onCheckedChange={(v) => onToggleChart(widget.id, v)}
aria-label={`${labels.widgetLabel} — ${labels.chartColumn}`}
aria-label={`${labels.widgetLabel}: ${labels.chartColumn}`}
disabled={disabled}
data-slot="widget-chart-switch"
/>
@@ -402,7 +402,7 @@ function EcgManagerRow({
onClick={onToggle}
disabled={disabled}
aria-pressed={visible}
aria-label={`${visible ? hideLabel : showLabel} — ${title}`}
aria-label={`${visible ? hideLabel : showLabel}: ${title}`}
title={visible ? hideLabel : showLabel}
data-slot="insights-pill-order-eye"
data-visible={visible ? "true" : "false"}
@@ -463,7 +463,7 @@ function SortablePillRow({
type="button"
{...attributes}
{...listeners}
aria-label={`${dragHandleLabel} — ${title}`}
aria-label={`${dragHandleLabel}: ${title}`}
title={dragHandleLabel}
disabled={disabled}
data-slot="insights-pill-order-handle"
@@ -481,7 +481,7 @@ function SortablePillRow({
onClick={() => onToggle(id, !visible)}
disabled={disabled}
aria-pressed={visible}
aria-label={`${visible ? hideLabel : showLabel} — ${title}`}
aria-label={`${visible ? hideLabel : showLabel}: ${title}`}
title={visible ? hideLabel : showLabel}
data-slot="insights-pill-order-eye"
data-visible={visible ? "true" : "false"}
@@ -63,7 +63,7 @@ describe("OAuthProviderCard — parked + test + data-link parity", () => {
expect(html).toContain('data-state="parked"');
expect(html).toContain('data-testid="polar-parked-banner"');
expect(html).toContain('data-testid="polar-resume-button"');
expect(html).toContain("Paused — reconnect");
expect(html).toContain("Paused: reconnect");
// The parked banner uses the same warning treatment as the WHOOP card.
expect(html).toContain("border-warning/30 bg-warning/10");
});
@@ -157,7 +157,7 @@ describe("OAuthProviderCard — per-user BYO credentials form (v1.17.1)", () =>
syncHealth: { verdict: "pending_first_sync", since: null },
},
});
expect(html).toContain("Saved — enter new to replace");
expect(html).toContain("Saved. Enter new to replace");
});
});