components/status/status.react.tsx
122 lines and 4 functions in DrDustinEdwards/capsomer, at commit d3966cc on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| Glyph | 11 to 52 | 7 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| glyphFor | 68 to 70 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| Status | 72 to 96 | 5 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| Pill | 118 to 129 | 4 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
Imports
It imports no other module of this repo. Imported by components/attention-list/attention-list.react.tsx, components/banner/banner.react.tsx, components/bulk-bar/bulk-bar.react.tsx, components/empty/empty.react.tsx, components/flag-list/flag-list.react.tsx, components/media/media-grid.react.tsx, components/media/media.react.tsx, components/mentions-list/mentions-list.react.tsx, components/meter/meter.react.tsx, components/people-roles/people-roles.react.tsx, components/posts-list/posts-list.react.tsx, components/publish-gate/publish-gate.react.tsx, components/usage-meter/usage-meter.react.tsx.
Source
import type { ReactNode } from "react";
export type Tone = "crit" | "warn" | "ok" | "info" | "nodata";
// A glyph is named by its tone, except running, which is an info tone with an arc.
export type GlyphName = Tone | "running";
const box = { viewBox: "0 0 16 16", "aria-hidden": true, focusable: false } as const;
// One shape per meaning, so the shape carries it without colour. Marks inside a filled
// shape are cut out (evenodd), so the glyph reads on any background, a tint included.
export function Glyph({ name }: { name: GlyphName }) {
switch (name) {
case "crit":
return (
<svg className="cap-status-glyph" {...box}>
<path fill="currentColor" fillRule="evenodd" d="M5 1h6l4 4v6l-4 4H5l-4-4V5zM7.2 4h1.6v5H7.2zM7.2 10.4h1.6V12H7.2z" />
</svg>
);
case "warn":
return (
<svg className="cap-status-glyph" {...box}>
<path fill="currentColor" fillRule="evenodd" d="M8 1.2 15.4 14H.6zM7.3 6h1.4v4H7.3zM7.3 11h1.4v1.4H7.3z" />
</svg>
);
case "info":
return (
<svg className="cap-status-glyph" {...box}>
<rect x="1.5" y="1.5" width="13" height="13" rx="2" fill="none" stroke="currentColor" strokeWidth="1.6" />
<path fill="currentColor" d="M7.2 7h1.6v5H7.2zM7.2 4h1.6v1.7H7.2z" />
</svg>
);
case "ok":
return (
<svg className="cap-status-glyph" {...box}>
<path fill="currentColor" fillRule="evenodd" d="M8 1a7 7 0 1 0 0 14A7 7 0 1 0 8 1zM3.9 9l3 3 5.2-5.6-1.4-1.4-3.8 4.2-1.6-1.6z" />
</svg>
);
case "nodata":
return (
<svg className="cap-status-glyph" {...box}>
<><circle cx="8" cy="8" r="6.2" fill="none" stroke="currentColor" strokeWidth="1.6" /><path d="M5 8h6" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></>
</svg>
);
case "running":
return (
<svg className="cap-status-glyph" {...box}>
<circle cx="8" cy="8" r="6.2" fill="none" stroke="currentColor" strokeWidth="1.6" opacity="0.35" />
<path fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" d="M8 1.8a6.2 6.2 0 0 1 6.2 6.2" />
</svg>
);
}
}
export interface StatusProps {
tone: Tone;
// Running is an info tone drawn with an arc.
running?: boolean;
// The word: "Critical", "Down", "2 blocked". Never empty: the word is the status.
children: ReactNode;
// No data says why: "the counter has not reported since 09:00".
reason?: ReactNode;
// For a table cell, where a sentence does not fit: the cell says the word and nothing
// more, the reason is read out by a screen reader (and shown on hover, when it is text),
// and the full sentence is in the row's detail. The Portal's NoData brief (design D18).
brief?: boolean;
}
function glyphFor(tone: Tone, running?: boolean): GlyphName {
return running ? "running" : tone;
}
export function Status({ tone, running, children, reason, brief }: StatusProps) {
if (brief && reason) {
return (
<span className="cap-status" data-tone={tone} title={typeof reason === "string" ? reason : undefined}>
<Glyph name={glyphFor(tone, running)} />
{children}
<span className="cap-sr-only">: {reason}</span>
</span>
);
}
const word = (
<span className="cap-status" data-tone={tone}>
<Glyph name={glyphFor(tone, running)} />
{children}
</span>
);
if (!reason) return word;
// The reason follows the word as its own sentence, with a real space between, so a
// screen reader does not run the two together.
return (
<>
{word} <span className="cap-status-reason">{reason}</span>
</>
);
}
export type BadgeVariant = "default" | "secondary" | "outline" | "destructive" | "ghost" | "link";
export interface PillProps {
// A status tone: the word and glyph on the tone's tint. Without a variant it is a status
// badge; with one, outline and ghost keep the tone's colour.
tone?: Tone;
// shadcn's badge variants. Leave out for the tinted status badge (or a quiet grey one).
variant?: BadgeVariant;
// Running is an info tone drawn with an arc.
running?: boolean;
// The word. A badge with a tone also draws that tone's glyph, so shape and word carry it.
children: ReactNode;
// A link badge: a real <a>. Its hit area grows to the target size.
href?: string;
// A button badge. Give href or onClick, not both.
onClick?: () => void;
}
// The badge: a status on its tint (tone), or one of the six variants. A tone always draws
// its glyph; a variant with no tone is just the word (or a count).
export function Pill({ tone, variant, running, children, href, onClick }: PillProps) {
const inner = (
<>
{tone ? <Glyph name={glyphFor(tone, running)} /> : null}
{children}
</>
);
const common = { className: "cap-pill", "data-tone": tone, "data-variant": variant } as const;
if (href) return <a {...common} href={href}>{inner}</a>;
if (onClick) return <button type="button" {...common} onClick={onClick}>{inner}</button>;
return <span {...common}>{inner}</span>;
}
// The same component under shadcn's name.
export const Badge = Pill;