refactor(insights): one TrendTile frame for the trend tiles

The dashboard KPI tile, the derived-insight sparkline tile, both
skeletons and the three still-building states of the vitals board each
spelled out the same tile frame by hand, and some clipped their content
while others did not. TrendTile owns the frame; the vitals board's
three building states share one BuildingTile. The rendered classes are
unchanged.
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 13:26:47 +02:00
1 parent 3aadc03424
commit a97abe64b9
6 files changed
+180 -89

No files matched your search

@@ -0,0 +1,39 @@
import { describe, expect, it } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { TrendTile } from "../trend-tile";
describe("<TrendTile>", () => {
it("paints the tile frame on the card inset and clips its content", () => {
const html = renderToStaticMarkup(<TrendTile>x</TrendTile>);
for (const c of [
"bg-card",
"border",
"rounded-xl",
"p-4",
"md:p-6",
"h-full",
"min-w-0",
"overflow-hidden",
]) {
expect(html).toContain(c);
}
expect(html).toContain('data-slot="trend-tile"');
expect(html).not.toContain("shadow");
});
it("keeps the caller's slot, state and rhythm", () => {
const html = renderToStaticMarkup(
<TrendTile
data-slot="vitals-tile"
data-state="provisional"
className="gap-3"
>
x
</TrendTile>,
);
expect(html).toContain('data-slot="vitals-tile"');
expect(html).toContain('data-state="provisional"');
expect(html).toContain("gap-3");
});
});
+33
View File
@@ -0,0 +1,33 @@
import * as React from "react";
import { cn } from "@/lib/utils";
/**
* The frame of a metric trend tile: the dashboard KPI strip (`TrendCard`),
* the derived-insight tiles (`SparklineDeltaTile`), their skeletons and the
* "still building" states of the vitals board.
*
* Seven copies spelled this frame out by hand, and they had started to
* differ (some clipped their content, some did not). The frame is a dense
* tile of its own rather than a `Card`: it sits in a grid of equal-height
* tiles (`h-full`), insets on the card padding (`p-4 md:p-6`, UI standards
* §1), carries no shadow, and clips a value that would otherwise push the
* grid cell wider (`min-w-0 overflow-hidden`).
*
* The vertical rhythm inside belongs to the tile: pass `gap-3` for a stacked
* state, or let the children set their own `mt-*` steps as the loaded tiles do.
*/
function TrendTile({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="trend-tile"
className={cn(
"bg-card border-border flex h-full w-full min-w-0 flex-col overflow-hidden rounded-xl border p-4 md:p-6",
className,
)}
{...props}
/>
);
}
export { TrendTile };