mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 05:42:19 +00:00
fix(charts): paint empty-window state instead of erasing the card
`health-chart.tsx` returned `null` when the loaded payload was empty in the selected range, which stripped the entire widget (header + tabs + chart). On the dashboard the user saw a missing tile with no explanation. Keep the card chrome mounted and paint a `<ChartEmptyState>` with distinct copy from the < 3-points "Mehr Messtage erforderlich" branch so the two situations don't blur. Adds `charts.noDataInRange*` strings across all six locales.
This commit is contained in:
1 parent
d02deb1769
commit
54fbbf8fc8
8 files changed
+120
-2
No files matched your search
@@ -0,0 +1,77 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
/**
|
||||
* v1.4.43 W11-M6 — chart empty-window state.
|
||||
*
|
||||
* Pre-fix the chart returned `null` when the loaded data array was
|
||||
* empty in the selected range, erasing the entire card (header +
|
||||
* tabs + chart). The user saw a missing widget without explanation.
|
||||
* The fix paints a "no data in this range" empty state via
|
||||
* `<ChartEmptyState>` so the dashboard composition stays intact and
|
||||
* the user knows the chart loaded but the selected window holds no
|
||||
* measurements. The copy is distinct from the < 3-points "Mehr
|
||||
* Messtage erforderlich" branch so the two situations don't blur.
|
||||
*/
|
||||
|
||||
function buildData(rawCount: number): unknown[] {
|
||||
const points: unknown[] = [];
|
||||
Object.defineProperty(points, "rawCount", {
|
||||
value: rawCount,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false,
|
||||
});
|
||||
return points;
|
||||
}
|
||||
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({
|
||||
isAuthenticated: true,
|
||||
user: null,
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("<HealthChart> — empty-window state (W11-M6)", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("renders the no-data-in-range copy when data resolves to an empty array", async () => {
|
||||
const data = buildData(0);
|
||||
|
||||
vi.doMock("@tanstack/react-query", () => ({
|
||||
useQuery: () => ({ data, isLoading: false }),
|
||||
useQueryClient: () => ({
|
||||
cancelQueries: () => Promise.resolve(),
|
||||
getQueryData: () => undefined,
|
||||
setQueryData: () => undefined,
|
||||
invalidateQueries: () => Promise.resolve(),
|
||||
}),
|
||||
useMutation: () => ({ mutate: () => undefined, isPending: false }),
|
||||
}));
|
||||
|
||||
const { I18nProvider } = await import("@/lib/i18n/context");
|
||||
const { HealthChart } = await import("../health-chart");
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="de">
|
||||
<HealthChart types={["PULSE"]} title="Pulse" unit="bpm" />
|
||||
</I18nProvider>,
|
||||
);
|
||||
|
||||
// The new empty state must paint…
|
||||
expect(html).toContain("Keine Daten in diesem Zeitraum");
|
||||
// …and the chart card chrome (title) must still be mounted so the
|
||||
// dashboard isn't missing a tile. The pre-fix `return null` would
|
||||
// have stripped this entirely.
|
||||
expect(html).toContain("Pulse");
|
||||
// The < 3-points branch must NOT paint here — empty-window and
|
||||
// sparse-data are different situations with distinct copy.
|
||||
expect(html).not.toContain("Mehr Messtage erforderlich");
|
||||
expect(html).not.toContain("Erfasse mindestens 3 Einträge");
|
||||
|
||||
vi.doUnmock("@tanstack/react-query");
|
||||
});
|
||||
});
|
||||
@@ -1132,7 +1132,14 @@ export function HealthChart({
|
||||
return map;
|
||||
}, [chartData, types]);
|
||||
|
||||
if (!isLoading && !data?.length) return null;
|
||||
// v1.4.43 W11-M6 — keep the card shell mounted even when the loaded
|
||||
// payload is empty. Pre-fix the early `return null` here erased the
|
||||
// whole widget (header + range tabs + chart) and the user saw an
|
||||
// unexplained gap on the dashboard. The in-card render branch below
|
||||
// now paints a "no data in this range" empty state so the user
|
||||
// understands the chart loaded but the selected window holds no
|
||||
// measurements — distinct copy from the < 3-points "more days
|
||||
// needed" hint so the two situations don't blur.
|
||||
|
||||
const maxPointIndex = Math.max(0, (chartData?.length ?? 1) - 1);
|
||||
|
||||
@@ -1288,7 +1295,29 @@ export function HealthChart({
|
||||
<div className="flex h-48 items-center justify-center">
|
||||
<Loader2 className="text-primary h-6 w-6 animate-spin motion-reduce:animate-none" />
|
||||
</div>
|
||||
) : !chartData?.length ? null : (chartData?.length ?? 0) < 3 ? (
|
||||
) : !chartData?.length ? (
|
||||
// v1.4.43 W11-M6 — empty-window state.
|
||||
//
|
||||
// Pre-fix the chart silently `return null`ed when `chartData`
|
||||
// resolved empty (after-load, no points in the selected
|
||||
// range). Paired with the < 3-points hint, the user's
|
||||
// five visible chart states were:
|
||||
//
|
||||
// - skeleton (loading)
|
||||
// - 1-2 daily points / many raw → "Mehr Messtage erforderlich"
|
||||
// - 1-2 daily points / < 3 raw → "Erfasse mindestens 3 Einträge"
|
||||
// - ≥ 3 daily points → line chart
|
||||
// - 0 daily points → NOTHING
|
||||
//
|
||||
// The "nothing" branch read as a broken widget on the
|
||||
// dashboard. Paint a distinct empty state explaining the
|
||||
// selected range is empty so the user reaches for the range
|
||||
// tabs or the quick-add instead of suspecting a bug.
|
||||
<ChartEmptyState
|
||||
title={t("charts.noDataInRangeTitle")}
|
||||
description={t("charts.noDataInRangeDescription")}
|
||||
/>
|
||||
) : (chartData?.length ?? 0) < 3 ? (
|
||||
// v1.4.16 B1a — sparse-data placeholder. <3 daily points is too
|
||||
// few to render a meaningful trend; paint a friendly hint
|
||||
// instead so the dashboard doesn't look broken.
|
||||
|
||||
Reference in new issue
Block a user