Explain number
A popover that shows where a number came from: the source, the formula, and the rows it was computed from.
Takes the provenance half of a traced() result. The trigger is a plain button with an accessible name, so the popover opens with Enter or Space and closes on Escape like any other. Column headings are written from the row keys — "sessionSeconds" heads a column as "Session seconds", an acronym like MRR is left alone — and numeric columns are right-aligned on a fixed advance. It admits to sampling: "Showing 8 of 30" appears whenever fewer rows are on show than the number was computed from. Compute with traced on the server and pass the provenance down; only the kept rows travel.
Install
npx shadcn@latest add @vibra/explain-numberNeeds the @vibra registry in your components.json — set it up once.
Examples
import { formatMetric, traced } from "@/lib/metric"
import { ExplainNumber } from "@/components/ui/explain-number"
// Twelve invoices, eight of them settled. A formula has to describe the rows it
// is shown beside — this is the component built to prove that — so the trace
// runs over the paid ones and says so, rather than claiming a `where` clause
// over rows that carry no status at all.
const INVOICES = Array.from({ length: 12 }, (_, index) => ({
invoice: `inv_10${String(index + 1).padStart(2, "0")}`,
customer: ["Northwind", "Wavelength Energy", "Granite Retail", "Beacon Retail"][index % 4],
amount: 1250 + index * 315,
status: index % 3 === 2 ? "open" : "paid",
}))
const PAID = INVOICES.filter((invoice) => invoice.status === "paid")
const { value, provenance } = traced(
"paid invoices",
PAID,
"sum(amount)",
(rows) => rows.reduce((total, row) => total + row.amount, 0),
{ columns: ["invoice", "customer", "amount"], sample: 5 }
)
const formatted = formatMetric({ kind: "money", value, precision: 0 })
export default function ExplainNumberDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-1 panel p-4">
<div className="text-sm text-muted-foreground">Collected this month</div>
<div className="flex items-center gap-1">
<span className="text-2xl font-semibold tracking-tight tabular-nums">{formatted}</span>
<ExplainNumber provenance={provenance} value={formatted} />
</div>
</div>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| provenance | Provenance | — | Where the number came from — the provenance half of a traced() result. |
| value | React.ReactNode | — | The number itself, already formatted, shown at the top of the popover. |
| triggerLabel | string | "Explain this number" | The trigger button's accessible name. |
| side | "top" | "right" | "bottom" | "left" | "bottom" | Which side of the trigger the popover opens on. |
| align | "start" | "center" | "end" | "start" | How the popover lines up against the trigger. |
Dependencies
Source
"use client"
import * as React from "react"
import { SigmaIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { formatNumber } from "@/lib/format"
import { isSampled, type Provenance, type ProvenanceValue } from "@/lib/metric"
import { Button } from "@/components/ui/button"
import {
Popover,
PopoverContent,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
// A key is written the way a heading is: acronyms kept (MRR), camelCase and
// snake_case split into words, and only the first one capitalised — so
// "sessionSeconds" heads a column as "Session seconds", not "Session Seconds".
/** Writes a row key as a column heading. */
export function columnLabel(key: string): string {
const words = key
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
.replace(/[_-]+/g, " ")
.split(/\s+/)
.filter(Boolean)
.map((word) => (word === word.toUpperCase() ? word : word.toLowerCase()))
const [first = "", ...rest] = words
return [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(" ")
}
/** Numbers are right-aligned on a fixed advance; everything else reads left. */
function isNumeric(value: ProvenanceValue): boolean {
return typeof value === "number"
}
function cellText(value: ProvenanceValue): string {
if (value === null || value === undefined) return "—"
if (typeof value === "number") return formatNumber(value)
if (typeof value === "boolean") return value ? "yes" : "no"
return value
}
// The trigger button is the root: className and the rest of the props land on
// it, the way DatePicker's does.
export type ExplainNumberProps = Omit<
React.ComponentProps<typeof Button>,
"value" | "children" | "render" | "variant" | "size"
> & {
/** Where the number came from — the output of `traced`. */
provenance: Provenance
/** The number itself, already formatted, shown at the top of the popover. */
value?: React.ReactNode
/** The trigger's accessible name. */
triggerLabel?: string
side?: React.ComponentProps<typeof PopoverContent>["side"]
align?: React.ComponentProps<typeof PopoverContent>["align"]
}
/**
* The rows a number was computed from, in a popover: how many there were, where
* they came from, the formula, and the first few rows themselves. A dashboard
* figure that a reader cannot check is a figure they have to trust; this is the
* check.
*/
function ExplainNumber({
className,
provenance,
value,
triggerLabel = "Explain this number",
side = "bottom",
align = "start",
...props
}: ExplainNumberProps) {
const { source, formula, columns, rows, total } = provenance
const showTable = columns.length > 0 && rows.length > 0
return (
<Popover>
<PopoverTrigger
render={
<Button
type="button"
data-slot="explain-number"
variant="ghost"
size="icon-xs"
aria-label={triggerLabel}
className={cn("text-muted-foreground", className)}
{...props}
>
<SigmaIcon aria-hidden="true" />
</Button>
}
/>
<PopoverContent
side={side}
align={align}
className="w-[min(22rem,calc(100vw-2rem))] gap-2 p-3"
>
<PopoverHeader>
<PopoverTitle>How this number is computed</PopoverTitle>
</PopoverHeader>
{value !== undefined ? (
<div
data-slot="explain-number-value"
className="text-xl font-semibold tracking-tight tabular-nums"
>
{value}
</div>
) : null}
<p data-slot="explain-number-source" className="text-xs text-muted-foreground">
{`${formatNumber(total, { maximumFractionDigits: 0 })} ${total === 1 ? "row" : "rows"} from ${source}`}
</p>
<code
data-slot="explain-number-formula"
className="rounded-md bg-muted px-2 py-1 font-mono text-xs break-words text-foreground"
>
{formula}
</code>
{showTable ? (
<Table
data-slot="explain-number-rows"
className="text-xs [&_td]:px-2 [&_td]:py-1 [&_th]:h-7 [&_th]:px-2"
>
<TableHeader>
<TableRow>
{columns.map((column) => (
<TableHead
key={column}
className={cn(
"text-xs font-medium whitespace-nowrap",
isNumeric(rows[0]?.[column]) && "text-end"
)}
>
{columnLabel(column)}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row, index) => (
<TableRow key={index}>
{columns.map((column) => (
<TableCell
key={column}
className={cn(
"whitespace-nowrap",
isNumeric(row[column]) ? "text-end tabular-nums" : "text-muted-foreground"
)}
>
{cellText(row[column])}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
) : null}
{isSampled(provenance) ? (
<p data-slot="explain-number-sample" className="text-xs text-muted-foreground">
{`Showing ${formatNumber(rows.length, { maximumFractionDigits: 0 })} of ${formatNumber(total, { maximumFractionDigits: 0 })}`}
</p>
) : null}
</PopoverContent>
</Popover>
)
}
export { ExplainNumber }