mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 05:42:19 +00:00
fix(day): head the since-last-visit card with TileHeader at the compact density
The card hand-built an uppercase h3 and ran a density of its own. It now takes TileHeader size="sm" (which gains an h3 heading for a card under a section that owns the h2) and the compact card rhythm.
This commit is contained in:
1 parent
b7e48e933c
commit
ad9c9cd0cd
3 files changed
+42
-20
No files matched your search
@@ -17,7 +17,8 @@ import {
|
||||
} from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { TileHeader } from "@/components/insights/tile-header";
|
||||
import { QueryErrorRow } from "@/components/ui/query-error-row";
|
||||
import type { EncounterDTO } from "@/lib/encounters/dto";
|
||||
import { apiGet } from "@/lib/api/api-fetch";
|
||||
@@ -35,7 +36,7 @@ import {
|
||||
import { getUnitForType } from "@/lib/measurements/unit-map";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
|
||||
import { DAY_SECTION_LABEL, dayTitle, useNotableText } from "./day-sections";
|
||||
import { dayTitle, useNotableText } from "./day-sections";
|
||||
import { DayLink } from "./day-link";
|
||||
import { dateKeyOfInstant } from "./day-url";
|
||||
import { tileKeyOf } from "./day-values-model";
|
||||
@@ -212,24 +213,27 @@ export function SinceLastVisit({
|
||||
const nextDay = dateKeyOfInstant(next.occurredAt, timeZone);
|
||||
|
||||
return (
|
||||
<Card className="gap-3 py-3 md:gap-3 md:py-4">
|
||||
<Card className="gap-2 py-3 md:py-4">
|
||||
<CardHeader>
|
||||
<TileHeader
|
||||
size="sm"
|
||||
icon={Stethoscope}
|
||||
id={`since-last-visit-${next.id}`}
|
||||
titleAs="h3"
|
||||
title={t("day.sinceLastVisit.title", { date: lastLabel })}
|
||||
right={
|
||||
<span className="text-muted-foreground text-xs">
|
||||
{tCount("day.sinceLastVisit.count", rows.length)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<section
|
||||
data-slot="since-last-visit"
|
||||
aria-labelledby={`since-last-visit-${next.id}`}
|
||||
className="space-y-2"
|
||||
>
|
||||
<div className="flex items-baseline justify-between gap-3">
|
||||
<h3
|
||||
id={`since-last-visit-${next.id}`}
|
||||
className={DAY_SECTION_LABEL}
|
||||
>
|
||||
{t("day.sinceLastVisit.title", { date: lastLabel })}
|
||||
</h3>
|
||||
<span className="text-muted-foreground shrink-0 text-xs">
|
||||
{tCount("day.sinceLastVisit.count", rows.length)}
|
||||
</span>
|
||||
</div>
|
||||
<ul className="border-border divide-border divide-y rounded-lg border px-3.5">
|
||||
{visible.map((row) => (
|
||||
<li
|
||||
|
||||
@@ -297,5 +297,20 @@ describe("<VisitsSection> since the last visit", () => {
|
||||
expect(html).toContain('data-slot="since-last-visit"');
|
||||
expect(html).toContain('data-slot="since-last-visit-row"');
|
||||
expect(upcomingCards(html)).toBe(upcomingCards(baseline) + 1);
|
||||
// The card heads like every compact card: TileHeader at the small size,
|
||||
// a real heading under the section's own, at the compact density.
|
||||
const card = html.slice(
|
||||
html.lastIndexOf(
|
||||
'<div data-slot="card"',
|
||||
html.indexOf("since-last-visit-"),
|
||||
),
|
||||
);
|
||||
expect(card).toMatch(
|
||||
/^<div data-slot="card" class="[^"]*gap-2 py-3 md:py-4/,
|
||||
);
|
||||
expect(card).toContain('data-slot="tile-header"');
|
||||
expect(card).toMatch(
|
||||
/<h3 id="since-last-visit-[^"]+" data-slot="card-title" class="[^"]*text-sm/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -58,9 +58,11 @@ interface TileHeaderProps {
|
||||
* owning block (the recovery blocks) or a grid of sibling tiles (dashboard
|
||||
* tiles, insights overview grids) both nest correctly as `h1 → h2, h2, …`;
|
||||
* just don't nest a `titleAs="h2"` tile inside another heading's own
|
||||
* subsection. Default (unset) keeps every other tile a plain CardTitle.
|
||||
* subsection. `"h3"` is the same for a card that sits inside a section
|
||||
* which already owns an `h2` (the since-last-visit card under the visits
|
||||
* section). Default (unset) keeps every other tile a plain CardTitle.
|
||||
*/
|
||||
titleAs?: "h2";
|
||||
titleAs?: "h2" | "h3";
|
||||
}
|
||||
|
||||
export function TileHeader({
|
||||
@@ -73,6 +75,7 @@ export function TileHeader({
|
||||
id,
|
||||
titleAs,
|
||||
}: TileHeaderProps) {
|
||||
const Heading = titleAs ?? "h2";
|
||||
const titleClassNames = cn(
|
||||
size === "sm" ? "text-sm" : "text-base",
|
||||
titleClassName,
|
||||
@@ -93,16 +96,16 @@ export function TileHeader({
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : null}
|
||||
{titleAs === "h2" ? (
|
||||
{titleAs ? (
|
||||
// Heading semantics with the exact CardTitle visual treatment so the
|
||||
// tile can carry a real `<h2>` where it owns the subpage's heading.
|
||||
<h2
|
||||
// tile can carry a real heading where it owns one.
|
||||
<Heading
|
||||
id={id}
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", titleClassNames)}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
</Heading>
|
||||
) : (
|
||||
<CardTitle id={id} className={titleClassNames}>
|
||||
{title}
|
||||
|
||||
Reference in new issue
Block a user