Tiered Test Display

A staged test-results panel. Each tier groups a set of related checks and rolls them up into a single status (passed, failing, pending). The first failing tier gates the ones after it — subsequent tiers render in a muted pending row so the reader fixes the earliest failure first. When every tier passes, a success banner renders above the list.

Preview
  • ascending order

  • no duplicates

    Found duplicate at index 2
Customize
Tiers
0
1

Installation

npx shadcn@latest add https://craftbits.dev/r/tiered-test-display.json

Usage

import { TieredTestDisplay } from "@craft-bits/core";
 
<TieredTestDisplay
  tiers={[
    { label: "Shape", tests: [{ name: "returns array", status: "passed" }] },
    {
      label: "Values",
      tests: [
        { name: "ascending order", status: "failed", message: "Got [3, 1, 2]" },
      ],
    },
    { label: "Exact", tests: [{ name: "matches reference", status: "passed" }] },
  ]}
/>

Pass an empty tiers array to show the muted fallback, or override the fallback with your own node:

<TieredTestDisplay tiers={[]} emptyFallback={<p>No tests run yet.</p>} />

Understanding the component

  1. Tiers gate each other. The rollup walks tiers in order. Once a tier is failing, every later tier becomes pending regardless of its tests — its row is dimmed and not expandable.
  2. Each tier auto-rolls up. A tier is passed only when every test in it passed. Any non-passed test flips the tier to failing. Empty tiers count as passed.
  3. First failing tier auto-expands. When a tier is failing and no defaultExpanded is set, it opens on mount. Other tiers stay collapsed by default.
  4. Tests are flat. Each test is { name, status, message? }. The message slot accepts any node — paragraphs, expected/got blocks, links.
  5. Success banner is a slot. When every tier passes, a default "All checks passed." banner renders above the list. Pass allPassedBanner={null} to suppress, or replace it with your own node.

Variants

All passing

<TieredTestDisplay
  tiers={[
    { label: "Shape", tests: [{ name: "returns array", status: "passed" }] },
    { label: "Values", tests: [{ name: "ascending", status: "passed" }] },
  ]}
/>

Middle tier failing

<TieredTestDisplay
  tiers={[
    { label: "Shape", tests: [{ name: "returns array", status: "passed" }] },
    {
      label: "Values",
      tests: [{ name: "ascending", status: "failed", message: "Got [3,1,2]" }],
    },
    { label: "Exact", tests: [{ name: "matches reference", status: "passed" }] },
  ]}
/>

Compact density

<TieredTestDisplay density="compact" tiers={tiers} />

Props

PropTypeDefaultDescription
tiersreadonly { label; tests; id?; defaultExpanded? }[]requiredOrdered tiers. Each tier rolls up to one status; the first failing tier gates the rest.
density'cosy' | 'compact''cosy'Vertical rhythm between tier rows.
allPassedBannerReactNode | nullbuilt-in success bannerBanner shown when every tier passes. null suppresses.
emptyFallbackReactNode | null"Run tests to see results."Rendered when tiers is empty. null renders nothing.
classNamestringMerged onto the root via cn().
...restHTMLAttributes<HTMLDivElement>Any other root attribute.

Accessibility

  • The root is a polite live region (role="region" + aria-live="polite") so streaming test updates surface to assistive tech without interrupting the user.
  • Each tier toggle is a real button with aria-expanded / aria-controls tying it to its body. Pending tiers are disabled and a short status message reads "Fix the previous tier first".
  • The success and failure tick glyphs are aria-hidden; the tier label and the passed/total counter carry the semantic weight.
  • Chevron, status pop, and content height transitions all degrade to instant transitions under prefers-reduced-motion: reduce.
  • Status colours read from the cb semantic tokens (--cb-success, --cb-error, --cb-fg-muted) and clear WCAG AA contrast on the default surface.

Credits

  • Extracted from: craftingattention (app/src/lessons/primitives/lab/TieredTestDisplay.tsx). The original coupled to the project's TestSummary / LabTestMeta schema, ran diagnosis-pattern matching, and rendered an inline ReferenceDiffCard. craft-bits drops all of that — the API now accepts pre-rolled tiers of flat { name, status, message? } tests, exposes density and slot-based banners, and consumes cb tokens for every colour and spring.