diff --git a/src/components/charts/chart-range-tabs.tsx b/src/components/charts/chart-range-tabs.tsx index 4c8528606..79a4cc11f 100644 --- a/src/components/charts/chart-range-tabs.tsx +++ b/src/components/charts/chart-range-tabs.tsx @@ -51,6 +51,7 @@ export function ChartRangeTabs({ const { t } = useTranslations(); return ( "chart-range-tab"} diff --git a/src/components/custom-metrics/custom-metric-chart.tsx b/src/components/custom-metrics/custom-metric-chart.tsx index 3834a72f1..ddd7d7498 100644 --- a/src/components/custom-metrics/custom-metric-chart.tsx +++ b/src/components/custom-metrics/custom-metric-chart.tsx @@ -140,6 +140,7 @@ export function CustomMetricChart({
({ value: r.key, label: t(`customMetrics.chart.range.${r.key}`), diff --git a/src/components/insights/mood/mood-dimension-trends.tsx b/src/components/insights/mood/mood-dimension-trends.tsx index d3f187c72..efbf1ad07 100644 --- a/src/components/insights/mood/mood-dimension-trends.tsx +++ b/src/components/insights/mood/mood-dimension-trends.tsx @@ -141,6 +141,7 @@ export function MoodDimensionTrends({ return (
({ value: String(option), label: t("insights.mood.dimensions.window", { days: option }), diff --git a/src/components/insights/period-narrative-card.tsx b/src/components/insights/period-narrative-card.tsx index 8602b8bbc..e464b3ea4 100644 --- a/src/components/insights/period-narrative-card.tsx +++ b/src/components/insights/period-narrative-card.tsx @@ -177,6 +177,7 @@ export function PeriodNarrativeCard({
{/* Calm segmented control, no chart. */}
({ value: r.key, label: t(`labs.chart.range.${r.key}`), diff --git a/src/components/medications/detail/efficacy/efficacy-tab.tsx b/src/components/medications/detail/efficacy/efficacy-tab.tsx index 1e7b7e24c..a4d1cdde4 100644 --- a/src/components/medications/detail/efficacy/efficacy-tab.tsx +++ b/src/components/medications/detail/efficacy/efficacy-tab.tsx @@ -260,6 +260,7 @@ function TargetCard({
", () => { expect(out).toContain("min-h-9"); }); + it("fills the chosen segment in the primary colour, on no track", () => { + const out = html(); + const segments = out.match(/]*>/g) ?? []; + expect(segments).toHaveLength(2); + const chosen = segments.filter((b) => b.includes('aria-checked="true"')); + expect(chosen).toHaveLength(1); + expect(chosen[0]).toContain("bg-primary text-primary-foreground"); + const other = segments.find((b) => b.includes('aria-checked="false"')); + expect(other).not.toContain("bg-primary"); + const group = out.match(/]*role="radiogroup"[^>]*>/)?.[0]; + expect(group).not.toContain("bg-muted"); + }); + + it("sets a chart header's range row in the small type", () => { + expect(html()).toContain("text-sm"); + const small = html({ size: "sm" }); + expect(small).toContain("text-xs"); + expect(small).not.toContain("text-sm"); + expect(small.match(/pointer-coarse:min-h-11/g)?.length).toBe(2); + }); + it("renders pressed buttons in toggle mode", () => { const out = html({ mode: "toggle" }); expect(out).toContain('role="group"'); diff --git a/src/components/ui/segmented.tsx b/src/components/ui/segmented.tsx index cb4910f7a..3ce63c16b 100644 --- a/src/components/ui/segmented.tsx +++ b/src/components/ui/segmented.tsx @@ -3,8 +3,15 @@ /** * The segmented control: a short row of mutually exclusive choices on one * track (zoom level, chronicle grouping, cards or table, recent or all, - * Paperless-ngx or Papra). One look everywhere: a muted track, the chosen - * segment raised on the background colour, never coloured. + * Paperless-ngx or Papra). One look everywhere, the one the dashboard chart + * tabs always had: no track, the chosen segment filled in the primary colour, + * the others plain until hovered. A muted track with a raised segment was + * tried and read as heavier than the card around it. + * + * Two sizes: `md` (default) for a choice that is the content of its area (a + * view switch, a form sheet, a detail page's sections), `sm` for a range row + * in a chart header, where the segments sit beside the chart's title and + * must not outweigh it. * * Two semantics, one chrome: * @@ -59,6 +66,8 @@ export interface SegmentedProps { iconOnly?: boolean; /** Spread the segments over the full width. */ stretch?: boolean; + /** `sm` for a range row in a chart header; see the module comment. */ + size?: "sm" | "md"; disabled?: boolean; className?: string; /** @@ -78,6 +87,7 @@ export function Segmented({ mode = "radio", iconOnly = false, stretch = false, + size = "md", disabled = false, className, wrapOption, @@ -100,7 +110,7 @@ export function Segmented({ aria-disabled={disabled || undefined} data-slot={slot} className={cn( - "bg-muted inline-flex shrink-0 items-center gap-0.5 rounded-lg p-0.5", + "inline-flex shrink-0 items-center gap-1", stretch && "flex w-full", className, )} @@ -135,14 +145,17 @@ export function Segmented({ onChange(option.value); }} className={cn( - "focus-visible:ring-ring/50 inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium transition-colors outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none", + "focus-visible:ring-ring/50 inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none", + size === "sm" ? "text-xs" : "text-sm", iconOnly ? "size-9 pointer-coarse:size-11" - : "min-h-9 px-3 pointer-coarse:min-h-11", + : size === "sm" + ? "min-h-9 px-2 sm:px-3 pointer-coarse:min-h-11 pointer-coarse:min-w-11" + : "min-h-9 px-3 pointer-coarse:min-h-11", stretch && "flex-1", checked - ? "bg-background text-foreground shadow-sm" - : "text-foreground/75 hover:text-foreground dark:text-tab-inactive dark:hover:text-foreground", + ? "bg-primary text-primary-foreground hover:bg-primary/90" + : "text-foreground hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", )} > {Icon ? (