DashboardModule
The frame of a dashboard module for lists and charts, with a scope line, rows, facts, notes, service rows and a footer action.
Dashboard is the frame of one module on a dashboard: a card with an <h2> title, an
optional scope line such as "Last 7 days", a body and a footer with the module's actions.
Dashboard is the same frame for a chart. The other parts fill the body: a list of
rows, a list of facts, a note, a row per service.
Recent changes
Last 7 days
- Address of Ada Jansen changed
- New participant: Bram de Vries
- Scheme rates for 2027 added
Services
- StatementsRunning
- PaymentsDelayed
- Last import
- Today 06:00
Open questions
Open
There are no open questions.
New participants
Per month
// apps/docs/examples/ui/dashboard-module.tsximport { DashboardChartModule, DashboardModule, DashboardModuleFacts, DashboardModuleGroupDivider, DashboardModuleNote, DashboardModuleRows, DashboardServiceRow,} from '@workspace/active-ui/components/composed/dashboard-module';import {Button} from '@workspace/active-ui/components/primitives/button';
const MONTHS = [ {month: 'Jul', count: 42}, {month: 'Aug', count: 35}, {month: 'Sep', count: 58}, {month: 'Oct', count: 49},];
/** * A small dashboard: a list module with a scope and a footer action, a * service-status module with a fact list below a divider, an empty module * with a note, and a chart module. The chart is a stand-in drawn with plain * elements; a real one comes from a chart library. The data is fictitious. */export default function DashboardModuleExample() { const viewAll = ( <Button size="sm" variant="outline"> View all </Button> ); return ( <div className="grid w-full gap-4 lg:grid-cols-2"> <DashboardModule title="Recent changes" description="Last 7 days" footerActions={viewAll} > <DashboardModuleRows label="Recent changes"> <li className="py-3 text-sm">Address of Ada Jansen changed</li> <li className="py-3 text-sm">New participant: Bram de Vries</li> <li className="py-3 text-sm">Scheme rates for 2027 added</li> </DashboardModuleRows> </DashboardModule> <DashboardModule title="Services"> <DashboardModuleRows label="Services"> <DashboardServiceRow label="Statements" tone="success" status="Running" /> <DashboardServiceRow label="Payments" tone="warning" status="Delayed" meta="Checked 10:42" metaDateTime="2026-10-20T10:42" href="#payments" /> </DashboardModuleRows> <DashboardModuleGroupDivider /> <DashboardModuleFacts> <div className="flex justify-between py-3 text-sm"> <dt className="text-muted-foreground">Last import</dt> <dd>Today 06:00</dd> </div> </DashboardModuleFacts> </DashboardModule> <DashboardModule title="Open questions" description="Open"> <DashboardModuleNote>There are no open questions.</DashboardModuleNote> </DashboardModule> <DashboardChartModule title="New participants" description="Per month"> <div className="flex h-40 items-end gap-3 px-4 pb-4" role="img" aria-label="New participants per month: July 42, August 35, September 58, October 49" > {MONTHS.map(({month, count}) => ( <div key={month} className="flex flex-1 flex-col items-center gap-1" > <div className="bg-primary/70 w-full rounded-sm" style={{height: `${count * 2}px`}} /> <span className="text-muted-foreground text-xs">{month}</span> </div> ))} </div> </DashboardChartModule> </div> );}The pieces#
Dashboard: title,Module description(the scope of the selection, not an explanation),footer(zero to two buttons; no footer without them) and the body. A minimum height from theActions lgwidth keeps a short module from collapsing.Dashboard: title, scope and the chart; no footer. The chart itself comes from your chart library.Chart Module Dashboard: aModule Rows <ul>named bylabel, with lines between the rows. Rows are<li>elements without their own horizontal padding.Dashboard: the same lines withModule Facts <dl>semantics, for established values.Dashboard: a muted line for "there is nothing" or "it could not be read".Module Note Dashboard: a line between two groups in one module, hidden from assistive technology.Module Group Divider Dashboard: a service on the left and one StatusBadge on the right, with an optional provenance line (Service Row meta, a<time>withmeta) and a link.Date Time
Usage#
import { DashboardModule, DashboardModuleRows,} from '@workspace/active-ui/components/composed/dashboard-module';
<DashboardModule title="Recent changes" description="Last 7 days" footerActions={viewAll}> <DashboardModuleRows label="Recent changes"> {changes.map((change) => ( <li key={change.id} className="py-3 text-sm"> {change.text} </li> ))} </DashboardModuleRows></DashboardModule>;What your application provides#
The titles, scope lines, rows and actions, translated and formatted; the mapping from service states to tones.
The grid around the modules;
lg:auto-gives a row of modules one height.rows- fr The chart, with its own accessible description.
Accessibility#
Each module title is an
<h2>, so the dashboard reads as sections below the page's<h1>.A row list needs its
label: the module title is outside the list.A service row's state is a word in the badge, not only a colour.
API#
DashboardModule
Props
- Type
ReactNode
The body: Dashboard, Dashboard or a note.
- Type
string
The already translated module title. Renders as <h2>.
- Type
string
For placement (lg:col-), not for meaning.
- Type
string
The scope of the selection, already translated. Short, and only what a reader needs to know which rows they are seeing: "Last 7 days", "Open", "Financial year 2026".
The same strict test as for {@link DashboardChartModuleProps.description}, and on the same grounds. If the sentence changes the meaning of the selection, it is scope and belongs here. If it explains what a notification is, how the module works or what the reader should conclude, it is commentary and does not belong here. No help text, no instructions, no warnings.
Why a list module carries it too. A list module labels its fields with columns and rows, but not its selection: a bounded summary over a rolling window shows three rows without saying anywhere that there is a window, and then an empty list reads as "nothing has ever happened" instead of "nothing in the last seven days". That is the same unnameability for which the chart module carries the line.
Omitting it is the norm. A module whose set is complete, or whose selection follows from its title, does not get it.
Takes only the props listed.
DashboardChartModule
Props
- Type
ReactNode
The chart. This shell renders nothing of the visualisation itself.
- Type
string
The already translated module title. Renders as <h2>, as for a list.
- Type
string
For placement (lg:col-), not for meaning.
- Type
string
The scope of the visualisation, already translated. Short, and only what a reader needs in order to interpret the chart.
What belongs here: "Last 7 days", "Per month", "Financial year 2026", "Active participants per status". Each one a statement about which data is shown, and without that statement the chart is ambiguous - a bar chart of mutations says nothing as long as the period is unknown.
What does not belong here: help text, instructions, warnings, a long explanation or promotional copy. The test is strict and simple: if the sentence changes the meaning of the axes or the selection, it is scope; if it explains what the reader should conclude from the chart, it is commentary and does not belong here.
Why the chart module carries the line. A chart has no column headers and cannot name its own selection; there the line is content rather than commentary. Dashboard carries it under the same test (see Dashboard). What stays excluded on both is a free explanatory line.
Takes only the props listed.
DashboardModuleRows
Props
- Type
ReactNode
<li> elements. Give them **no px- of their own**: it is on the list.
- Type
string
The accessible name of the list. Required, not optional: the module title sits outside the list, so without a name a screen reader announces "list with 5 items" without saying of what.
- Type
string
Classes for the list.
Takes only the props listed.
DashboardModuleFacts
DashboardModuleNote
Props
- Type
ReactNode
The translated notice.
Takes only the props listed.
DashboardServiceRow
Props
- Type
string
The already translated service label, on the left. This component does not translate.
- Type
string
The already translated state label, on the right in the badge. Required: colour must never be the only carrier of meaning (WCAG 1.4.1).
- Type
StatusTone
The generic UI tone of the state. Mapping from the app's state model is the caller's job: this component does not know what degraded, OFF or unknown mean.
- Type
string
The destination of this service, when a detail surface actually exists. The label then becomes a link; the row gets no chevron, no extra action text and no second hint. Omitting it yields plain text: a row without a destination does not pretend to have one.
- Type
LinkComponent
The host's link for Dashboard, for example next/ so navigation stays client-side. Without it the label is a plain <a href>. Only meaningful with href.
- Type
string
An already translated, short provenance line for the state: when it was established, or that the evidence is missing or stale. Muted, to the left of the badge. This is not a measurement and not a second status word: it says nothing about the service and everything about the evidence. Omitting it draws nothing.
- Type
string
The machine-readable moment behind meta, ISO 8601, for the date attribute of <time>. Only meaningful together with meta.
Takes only the props listed.
Parts that take only their elementโs attributes
Dashboard: no props.Module Group Divider
Types
The props of Dashboard, each described in its prop list on this page.
interface DashboardModuleProps {
title: string;
description?: string;
footerActions?: React.ReactNode;
className?: string;
children: React.ReactNode;
}The props of Dashboard, each described in its prop list on this page.
interface DashboardChartModuleProps {
title: string;
description?: string;
className?: string;
children: React.ReactNode;
}The props of Dashboard, each described in its prop list on this page.
interface DashboardModuleRowsProps {
label: string;
children: React.ReactNode;
className?: string;
}The props of Dashboard, each described in its prop list on this page.
interface DashboardModuleFactsProps {
children: React.ReactNode;
className?: string;
}The props of Dashboard, each described in its prop list on this page.
interface DashboardServiceRowProps {
label: string;
tone: StatusTone;
status: string;
href?: string;
linkComponent?: LinkComponent;
meta?: string;
metaDateTime?: string;
}