mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
Verified UI-consistency findings from the campaign's design audit, each pixel-faithful except the intended correction: - insights hero band: off-scale py-5/gap-5 → the standard scale; H1 font-semibold → font-bold; sm:text-[28px] → sm:text-3xl. - medication-compliance-chart + recent-achievements-card: hand-rolled muted h-4 icon + text-sm headers → the standard TileHeader primitive so they match sibling tiles. - achievements stat tiles + clinician-view shells: p-5 → p-4 md:p-6. - two text-primary header glyphs (research-mode dialog, enroll-mfa) → text-foreground (never accent on a header glyph); the deliberate badge-boxed icons left untouched. - SheetHeader now reserves the trailing close-X gutter like DialogHeader, closing the latent overlap class for future raw-Sheet consumers. - five raw Dialogs on phone-reachable surfaces → ResponsiveSheet. - new bg-card-scoped healthlog/spacing-scale lint (flags p-5/gap-5 etc. only on card shells; list-marker insets + half-steps excluded) at error, with the five offenders it flagged swept. - dropped the stale CLAUDE.md '18 PascalCase outliers' line (0 remain). Every data-slot preserved. No migration.
168 lines
5.3 KiB
JavaScript
168 lines
5.3 KiB
JavaScript
/**
|
|
* Unit tests for the `healthlog/spacing-scale` rule — pt-* / pb-* slot
|
|
* padding on CardHeader/CardContent is banned (the Card is gap-based;
|
|
* density lives on the Card itself). Covers modifier prefixes,
|
|
* cn()/ternary/template collection, the safe-area non-match, and the
|
|
* scope gating shared with the sibling rules.
|
|
*/
|
|
|
|
import { describe, it } from "vitest";
|
|
import { RuleTester } from "eslint";
|
|
import rule from "../spacing-scale.js";
|
|
|
|
RuleTester.describe = describe;
|
|
RuleTester.it = it;
|
|
RuleTester.itOnly = it.only;
|
|
|
|
const ruleTester = new RuleTester({
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: "module",
|
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
|
},
|
|
});
|
|
|
|
const APP_FILE = "/repo/src/components/insights/example.tsx";
|
|
|
|
ruleTester.run("spacing-scale", rule, {
|
|
valid: [
|
|
// Density on the Card itself is the sanctioned pattern.
|
|
{
|
|
code: '<Card className="gap-2 py-3 md:py-4"><CardContent>x</CardContent></Card>',
|
|
filename: APP_FILE,
|
|
},
|
|
// Slots without className, or with unrelated classes, are fine.
|
|
{ code: "<CardHeader />", filename: APP_FILE },
|
|
{
|
|
code: '<CardContent className="px-0 space-y-4" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// Other paddings on slots are not this rule's business.
|
|
{
|
|
code: '<CardContent className="pl-2 pr-2" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// Safe-area helpers are not scale steps and never match.
|
|
{
|
|
code: '<CardContent className="pb-safe" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// pt-/pb- on arbitrary elements stays legal — the contract is about
|
|
// the gap-based Card slots only.
|
|
{ code: '<div className="pb-2" />', filename: APP_FILE },
|
|
// Outside the enforced roots nothing matches.
|
|
{
|
|
code: '<CardHeader className="pb-2" />',
|
|
filename: "/repo/scripts/tool.tsx",
|
|
},
|
|
// Test files are exempt.
|
|
{
|
|
code: '<CardHeader className="pb-2" />',
|
|
filename: "/repo/src/components/__tests__/example.test.tsx",
|
|
},
|
|
// The card-shell `5` check is scoped to the bg-card+border pair, so the
|
|
// justified list-marker inset never trips it (no shell).
|
|
{
|
|
code: '<ul className="list-disc pl-5 space-y-1" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// A directional inset ON a shell is out of scope (pl-5/pr-5 not banned).
|
|
{
|
|
code: '<div className="bg-card border-border rounded-xl border pl-5" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// Form-body rhythm without a shell surface is fine.
|
|
{
|
|
code: '<form className="space-y-5" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// A shell already on-scale is fine.
|
|
{
|
|
code: '<div className="bg-card border-border rounded-xl border p-4 md:p-6" />',
|
|
filename: APP_FILE,
|
|
},
|
|
// bg-card without a border is not treated as a shell.
|
|
{
|
|
code: '<div className="bg-card p-5" />',
|
|
filename: APP_FILE,
|
|
},
|
|
],
|
|
invalid: [
|
|
{
|
|
code: '<CardHeader className="pb-2" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
{
|
|
code: '<CardContent className="pt-0" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
// Modifier-prefixed and fractional steps.
|
|
{
|
|
code: '<CardHeader className="md:pb-1.5" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
// Arbitrary-value padding.
|
|
{
|
|
code: '<CardContent className="pt-[10px]" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
// Collected through cn() args…
|
|
{
|
|
code: '<CardHeader className={cn("pb-3", extra)} />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
// …ternaries…
|
|
{
|
|
code: '<CardContent className={dense ? "pt-2" : "pt-4"} />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }, { messageId: "slotPadding" }],
|
|
},
|
|
// …and template literals.
|
|
{
|
|
code: "<CardHeader className={`pb-2 ${extra}`} />",
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
// Member-expression component tails match too.
|
|
{
|
|
code: '<UI.CardHeader className="pb-2" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "slotPadding" }],
|
|
},
|
|
// Card-shell `5` step: bg-card + border + p-5 on one element.
|
|
{
|
|
code: '<div className="bg-card border-border rounded-xl border p-5" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "cardShellStep5" }],
|
|
},
|
|
// …py-5, …space-y-5, …gap-5 on a shell all trip it.
|
|
{
|
|
code: '<section className="bg-card rounded-lg border py-5" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "cardShellStep5" }],
|
|
},
|
|
{
|
|
code: '<div className="bg-card border-border rounded-xl border gap-5 flex" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "cardShellStep5" }],
|
|
},
|
|
// Split across cn() args — the join still sees the shell + the step-5.
|
|
{
|
|
code: '<div className={cn("bg-card border rounded-xl", "p-5")} />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "cardShellStep5" }],
|
|
},
|
|
// Modifier-prefixed step on a shell.
|
|
{
|
|
code: '<div className="bg-card border-border rounded-xl border sm:p-5" />',
|
|
filename: APP_FILE,
|
|
errors: [{ messageId: "cardShellStep5" }],
|
|
},
|
|
],
|
|
});
|