Files
HealthLog/src/components/charts/chart-empty-state.tsx
T
Marc-André Bombeck 930fa81124 fix(a11y): switch names, contrast floors, and chart-toggle pressed state
- Admin SettingsToggle: name the Radix switch via aria-label so it no longer
  announces as a bare 'switch, on/off' (one shared component, six call sites).
- Replace the phantom text-destructive-foreground / text-warning-foreground
  utilities with the vetted destructive pattern (bg-destructive text-white
  dark:bg-destructive/60) and a readable foreground on the warning wash — no
  more dead classes riding on cascade order.
- Drop the sub-AA opacity tiers: lab sparkline + titration marker off
  muted/70, state icons (query-error, chart empty/error, med sort) off
  muted/60 to the full token so they clear the light-theme floor.
- Chart range toggles expose aria-pressed (lab, custom-metric, health, mood,
  compliance) so the active window is conveyed to assistive tech.
2026-07-08 23:51:22 +02:00

67 lines
2.3 KiB
TypeScript

"use client";
import type { ReactElement, ReactNode } from "react";
import { LineChart as LineChartIcon } from "lucide-react";
import { cn } from "@/lib/utils";
/**
* Sparse-data empty-state for chart wrappers (B1a v1.4.16).
*
* Per the maintainer's research §1A — "personal-baseline framing first" — when a
* user has only 1-2 data points there's nothing useful to plot, so we
* paint a friendly hint instead of a near-empty canvas. The hint reuses
* the `lucide-react/LineChart` glyph the rest of the app uses for chart
* surfaces, so it visually matches the chart-card shell that wraps it.
*
* Design constraints:
* - Same outer footprint as the painted chart (240-280 px tall) so
* the dashboard layout doesn't reflow when a chart drops to / out
* of the empty state.
* - i18n-driven copy — the wrapper passes already-translated strings.
* - SSR-safe — pure markup, tested via `renderToStaticMarkup`.
*/
export interface ChartEmptyStateProps {
/** Translated headline, e.g. "Add more measurements to see trends". */
title: string;
/**
* Optional secondary line. Sometimes useful to nudge the user toward
* a specific action ("3 measurements minimum"); often omitted.
*/
description?: ReactNode;
/**
* Optional explicit height in pixels for mini / mounted-in-a-strip
* contexts. Absent, the card sizes through the same
* `--chart-height` / `--chart-height-md` variables the painted chart
* reads (240 px mobile / 280 px md+ by default), so a per-mount
* override resizes the empty state too.
*/
height?: number;
}
export function ChartEmptyState({
title,
description,
height,
}: ChartEmptyStateProps): ReactElement {
return (
<div
data-slot="chart-empty-state"
className={cn(
"border-border/40 bg-muted/10 text-muted-foreground flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed px-6 text-center",
height === undefined &&
"h-[var(--chart-height,240px)] md:h-[var(--chart-height-md,280px)]",
)}
style={height === undefined ? undefined : { height }}
>
<LineChartIcon
className="text-muted-foreground h-8 w-8"
aria-hidden="true"
/>
<p className="text-sm font-medium">{title}</p>
{description ? <p className="text-xs">{description}</p> : null}
</div>
);
}