docs: add COMPONENT_GUIDE entries and Inventory demos for KpiStrip, SplitPane, EntityList, LogViewer, StatusText, Card title
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
|
||||
### "I need to show status"
|
||||
- Dot indicator → **StatusDot** (live, stale, dead, success, warning, error, running)
|
||||
- Inline colored status value → **StatusText** (success, warning, error, running, muted — with optional bold)
|
||||
- Labeled status → **Badge** with semantic color
|
||||
- Removable label → **Tag**
|
||||
|
||||
@@ -57,6 +58,9 @@
|
||||
- Event log → **EventFeed**
|
||||
- Processing pipeline (Gantt view) → **ProcessorTimeline**
|
||||
- Processing pipeline (flow diagram) → **RouteFlow**
|
||||
- Row of summary KPIs → **KpiStrip** (horizontal strip with colored borders, trends, sparklines)
|
||||
- Scrollable log output → **LogViewer** (timestamped, severity-colored monospace entries)
|
||||
- Searchable, selectable entity list → **EntityList** (search header, selection highlighting, pairs with SplitPane)
|
||||
|
||||
### "I need to organize content"
|
||||
- Collapsible sections (standalone) → **Collapsible**
|
||||
@@ -64,15 +68,17 @@
|
||||
- Tabbed content → **Tabs**
|
||||
- Tab switching with pill/segment style → **SegmentedTabs**
|
||||
- Side panel inspector → **DetailPanel**
|
||||
- Master/detail split layout → **SplitPane** (list on left, detail on right, configurable ratio)
|
||||
- Section with title + action → **SectionHeader**
|
||||
- Empty content placeholder → **EmptyState**
|
||||
- Grouped content box → **Card** (with optional accent)
|
||||
- Grouped content box → **Card** (with optional accent and title)
|
||||
- Grouped items with header + meta + footer → **GroupCard** (e.g., app instances)
|
||||
|
||||
### "I need to display text"
|
||||
- Code/JSON payload → **CodeBlock** (with line numbers, copy button)
|
||||
- Monospace inline text → **MonoText**
|
||||
- Keyboard shortcut hint → **KeyboardHint**
|
||||
- Colored inline status text → **StatusText** (semantic color + optional bold, see also "I need to show status")
|
||||
|
||||
### "I need to show people/users"
|
||||
- Single user avatar → **Avatar**
|
||||
@@ -115,6 +121,13 @@ Row of StatCard components (each with optional Sparkline and trend)
|
||||
Below: charts (AreaChart, LineChart, BarChart)
|
||||
```
|
||||
|
||||
### Master/detail management pattern
|
||||
```
|
||||
SplitPane + EntityList for CRUD list/detail screens (users, groups, roles)
|
||||
EntityList provides: search header, add button, selectable list
|
||||
SplitPane provides: responsive two-column layout with empty state
|
||||
```
|
||||
|
||||
### Detail/inspector pattern
|
||||
```
|
||||
DetailPanel (right slide) with Tabs for sections OR children for scrollable content
|
||||
@@ -162,7 +175,7 @@ URL-driven progressive filtering: /agents → /agents/:appId → /agents/:appId/
|
||||
| Breadcrumb | composite | Navigation path showing current location |
|
||||
| Button | primitive | Action trigger (primary, secondary, danger, ghost) |
|
||||
| ButtonGroup | primitive | Multi-select toggle group with optional colored dot indicators. Props: items (value, label, color?), value (Set), onChange |
|
||||
| Card | primitive | Content container with optional accent border |
|
||||
| Card | primitive | Content container with optional accent border and title header |
|
||||
| Checkbox | primitive | Boolean input with label |
|
||||
| CodeBlock | primitive | Syntax-highlighted code/JSON display |
|
||||
| Collapsible | primitive | Single expand/collapse section |
|
||||
@@ -174,6 +187,7 @@ URL-driven progressive filtering: /agents → /agents/:appId → /agents/:appId/
|
||||
| DetailPanel | composite | Slide-in side panel with tabs or children for scrollable content |
|
||||
| Dropdown | composite | Action menu triggered by any element |
|
||||
| EmptyState | primitive | Placeholder for empty content areas |
|
||||
| EntityList | composite | Searchable, selectable entity list with add button. Pair with SplitPane for CRUD management screens |
|
||||
| EventFeed | composite | Chronological event log with severity |
|
||||
| FilterBar | composite | Search + filter controls for data views |
|
||||
| GroupCard | composite | Card with header, meta row, children, and optional footer/alert. Used for grouping instances by application. |
|
||||
@@ -183,8 +197,10 @@ URL-driven progressive filtering: /agents → /agents/:appId → /agents/:appId/
|
||||
| InlineEdit | primitive | Click-to-edit text field. Enter saves, Escape/blur cancels. Props: value, onSave, placeholder, disabled, className |
|
||||
| Input | primitive | Single-line text input with optional icon |
|
||||
| KeyboardHint | primitive | Keyboard shortcut display |
|
||||
| KpiStrip | composite | Horizontal row of KPI cards with colored left border, trend, subtitle, optional sparkline |
|
||||
| Label | primitive | Form label with optional required asterisk |
|
||||
| LineChart | composite | Time series line visualization |
|
||||
| LogViewer | composite | Scrollable log output with timestamped, severity-colored monospace entries |
|
||||
| MenuItem | composite | Sidebar navigation item with health/count |
|
||||
| Modal | composite | Generic dialog overlay with backdrop |
|
||||
| MultiSelect | composite | Dropdown with searchable checkbox list and Apply action. Props: options, value, onChange, placeholder, searchable, disabled, className |
|
||||
@@ -202,9 +218,11 @@ URL-driven progressive filtering: /agents → /agents/:appId → /agents/:appId/
|
||||
| ShortcutsBar | composite | Keyboard shortcuts reference bar |
|
||||
| Skeleton | primitive | Loading placeholder (text, circular, rectangular) |
|
||||
| Sparkline | primitive | Inline mini chart for trends |
|
||||
| SplitPane | composite | Two-column master/detail layout with configurable ratio and empty state |
|
||||
| Spinner | primitive | Animated loading indicator |
|
||||
| StatCard | primitive | KPI card with value, trend, optional sparkline |
|
||||
| StatusDot | primitive | Colored dot for status indication |
|
||||
| StatusText | primitive | Inline colored status span (success, warning, error, running, muted) with optional bold |
|
||||
| Tabs | composite | Tabbed content switcher with optional counts |
|
||||
| Tag | primitive | Removable colored label |
|
||||
| Textarea | primitive | Multi-line text input with resize control |
|
||||
|
||||
@@ -39,6 +39,7 @@ const NAV_SECTIONS = [
|
||||
{ label: 'Spinner', href: '#spinner' },
|
||||
{ label: 'StatCard', href: '#statcard' },
|
||||
{ label: 'StatusDot', href: '#statusdot' },
|
||||
{ label: 'StatusText', href: '#statustext' },
|
||||
{ label: 'Tag', href: '#tag' },
|
||||
{ label: 'Textarea', href: '#textarea' },
|
||||
{ label: 'Toggle', href: '#toggle' },
|
||||
@@ -60,12 +61,15 @@ const NAV_SECTIONS = [
|
||||
{ label: 'DataTable', href: '#datatable' },
|
||||
{ label: 'DetailPanel', href: '#detailpanel' },
|
||||
{ label: 'Dropdown', href: '#dropdown' },
|
||||
{ label: 'EntityList', href: '#entitylist' },
|
||||
{ label: 'EventFeed', href: '#eventfeed' },
|
||||
{ label: 'FilterBar', href: '#filterbar' },
|
||||
{ label: 'GroupCard', href: '#groupcard' },
|
||||
{ label: 'KpiStrip', href: '#kpistrip' },
|
||||
{ label: 'LineChart', href: '#linechart' },
|
||||
{ label: 'LoginDialog', href: '#logindialog' },
|
||||
{ label: 'LoginForm', href: '#loginform' },
|
||||
{ label: 'LogViewer', href: '#logviewer' },
|
||||
{ label: 'MenuItem', href: '#menuitem' },
|
||||
{ label: 'Modal', href: '#modal' },
|
||||
{ label: 'MultiSelect', href: '#multi-select' },
|
||||
@@ -74,6 +78,7 @@ const NAV_SECTIONS = [
|
||||
{ label: 'RouteFlow', href: '#routeflow' },
|
||||
{ label: 'SegmentedTabs', href: '#segmented-tabs' },
|
||||
{ label: 'ShortcutsBar', href: '#shortcutsbar' },
|
||||
{ label: 'SplitPane', href: '#splitpane' },
|
||||
{ label: 'Tabs', href: '#tabs' },
|
||||
{ label: 'Toast', href: '#toast' },
|
||||
{ label: 'TreeView', href: '#treeview' },
|
||||
|
||||
@@ -12,12 +12,15 @@ import {
|
||||
DataTable,
|
||||
DetailPanel,
|
||||
Dropdown,
|
||||
EntityList,
|
||||
EventFeed,
|
||||
FilterBar,
|
||||
GroupCard,
|
||||
KpiStrip,
|
||||
LineChart,
|
||||
LoginDialog,
|
||||
LoginForm,
|
||||
LogViewer,
|
||||
MenuItem,
|
||||
Modal,
|
||||
MultiSelect,
|
||||
@@ -26,13 +29,14 @@ import {
|
||||
RouteFlow,
|
||||
SegmentedTabs,
|
||||
ShortcutsBar,
|
||||
SplitPane,
|
||||
Tabs,
|
||||
ToastProvider,
|
||||
useToast,
|
||||
TreeView,
|
||||
} from '../../../design-system/composites'
|
||||
import type { SearchResult } from '../../../design-system/composites'
|
||||
import { Button } from '../../../design-system/primitives'
|
||||
import { Avatar, Badge, Button } from '../../../design-system/primitives'
|
||||
|
||||
// ── DemoCard helper ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -178,6 +182,60 @@ const TREE_NODES = [
|
||||
},
|
||||
]
|
||||
|
||||
// ── Sample data for new composites ───────────────────────────────────────────
|
||||
|
||||
const KPI_ITEMS = [
|
||||
{
|
||||
label: 'Exchanges',
|
||||
value: '12,847',
|
||||
trend: { label: '\u2191 +8.2%', variant: 'success' as const },
|
||||
subtitle: 'Last 24h',
|
||||
sparkline: [40, 55, 48, 62, 70, 65, 78],
|
||||
borderColor: 'var(--amber)',
|
||||
},
|
||||
{
|
||||
label: 'Error Rate',
|
||||
value: '0.34%',
|
||||
trend: { label: '\u2191 +0.12pp', variant: 'error' as const },
|
||||
subtitle: 'Above threshold',
|
||||
sparkline: [10, 12, 11, 15, 18, 22, 19],
|
||||
borderColor: 'var(--error)',
|
||||
},
|
||||
{
|
||||
label: 'Avg Latency',
|
||||
value: '142ms',
|
||||
trend: { label: '\u2193 -12ms', variant: 'success' as const },
|
||||
subtitle: 'P95: 380ms',
|
||||
borderColor: 'var(--success)',
|
||||
},
|
||||
{
|
||||
label: 'Active Routes',
|
||||
value: '37',
|
||||
trend: { label: '\u00b10', variant: 'muted' as const },
|
||||
subtitle: '3 paused',
|
||||
borderColor: 'var(--running)',
|
||||
},
|
||||
]
|
||||
|
||||
const ENTITY_LIST_ITEMS = [
|
||||
{ id: '1', name: 'Alice Johnson', email: 'alice@example.com', role: 'Admin' },
|
||||
{ id: '2', name: 'Bob Chen', email: 'bob@example.com', role: 'Editor' },
|
||||
{ id: '3', name: 'Carol Smith', email: 'carol@example.com', role: 'Viewer' },
|
||||
{ id: '4', name: 'David Park', email: 'david@example.com', role: 'Editor' },
|
||||
{ id: '5', name: 'Eva Martinez', email: 'eva@example.com', role: 'Admin' },
|
||||
]
|
||||
|
||||
const LOG_ENTRIES = [
|
||||
{ timestamp: '2026-03-24T10:00:01Z', level: 'info' as const, message: 'Route timer-aggregator started successfully' },
|
||||
{ timestamp: '2026-03-24T10:00:03Z', level: 'debug' as const, message: 'Polling endpoint https://api.internal/health \u2014 200 OK' },
|
||||
{ timestamp: '2026-03-24T10:00:15Z', level: 'warn' as const, message: 'Retry queue depth at 847 \u2014 approaching threshold (1000)' },
|
||||
{ timestamp: '2026-03-24T10:00:22Z', level: 'error' as const, message: 'Exchange failed: Connection refused to jdbc:postgresql://db-primary:5432/orders' },
|
||||
{ timestamp: '2026-03-24T10:00:23Z', level: 'info' as const, message: 'Failover activated \u2014 routing to db-secondary' },
|
||||
{ timestamp: '2026-03-24T10:00:30Z', level: 'info' as const, message: 'Exchange completed in 142ms via fallback route' },
|
||||
{ timestamp: '2026-03-24T10:00:45Z', level: 'debug' as const, message: 'Metrics flush: 328 data points written to InfluxDB' },
|
||||
{ timestamp: '2026-03-24T10:01:00Z', level: 'warn' as const, message: 'Memory usage at 78% \u2014 GC scheduled' },
|
||||
]
|
||||
|
||||
// ── CompositesSection ─────────────────────────────────────────────────────────
|
||||
|
||||
export function CompositesSection() {
|
||||
@@ -221,6 +279,10 @@ export function CompositesSection() {
|
||||
// MultiSelect
|
||||
const [multiValue, setMultiValue] = useState<string[]>(['admin'])
|
||||
|
||||
// EntityList state
|
||||
const [selectedEntityId, setSelectedEntityId] = useState<string | undefined>('1')
|
||||
const [entitySearch, setEntitySearch] = useState('')
|
||||
|
||||
// LoginDialog
|
||||
const [loginDialogOpen, setLoginDialogOpen] = useState(false)
|
||||
const [loginLoading, setLoginLoading] = useState(false)
|
||||
@@ -465,6 +527,38 @@ export function CompositesSection() {
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* EntityList */}
|
||||
<DemoCard
|
||||
id="entitylist"
|
||||
title="EntityList"
|
||||
description="Searchable, selectable entity list with add button — designed to pair with SplitPane."
|
||||
>
|
||||
<div style={{ width: '100%', height: 260 }}>
|
||||
<EntityList
|
||||
items={ENTITY_LIST_ITEMS.filter(u =>
|
||||
u.name.toLowerCase().includes(entitySearch.toLowerCase())
|
||||
)}
|
||||
renderItem={(item, isSelected) => (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<Avatar name={item.name} size="sm" />
|
||||
<div>
|
||||
<div style={{ fontSize: 13, fontWeight: isSelected ? 600 : 400 }}>{item.name}</div>
|
||||
<div style={{ fontSize: 11, color: 'var(--text-muted)' }}>{item.email}</div>
|
||||
</div>
|
||||
<span style={{ marginLeft: 'auto' }}><Badge label={item.role} /></span>
|
||||
</div>
|
||||
)}
|
||||
getItemId={(item) => item.id}
|
||||
selectedId={selectedEntityId}
|
||||
onSelect={setSelectedEntityId}
|
||||
searchPlaceholder="Search users..."
|
||||
onSearch={setEntitySearch}
|
||||
addLabel="+ Add user"
|
||||
onAdd={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 11b. GroupCard */}
|
||||
<DemoCard
|
||||
id="groupcard"
|
||||
@@ -484,6 +578,17 @@ export function CompositesSection() {
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* KpiStrip */}
|
||||
<DemoCard
|
||||
id="kpistrip"
|
||||
title="KpiStrip"
|
||||
description="Horizontal row of KPI cards with coloured left border, trend indicator, subtitle, and optional sparkline."
|
||||
>
|
||||
<div style={{ width: '100%' }}>
|
||||
<KpiStrip items={KPI_ITEMS} />
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 12. FilterBar */}
|
||||
<DemoCard
|
||||
id="filterbar"
|
||||
@@ -562,6 +667,17 @@ export function CompositesSection() {
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* LogViewer */}
|
||||
<DemoCard
|
||||
id="logviewer"
|
||||
title="LogViewer"
|
||||
description="Scrollable log output with timestamped, severity-coloured monospace entries and auto-scroll."
|
||||
>
|
||||
<div style={{ width: '100%' }}>
|
||||
<LogViewer entries={LOG_ENTRIES} maxHeight={240} />
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 14. MenuItem */}
|
||||
<DemoCard
|
||||
id="menuitem"
|
||||
@@ -707,6 +823,33 @@ export function CompositesSection() {
|
||||
/>
|
||||
</DemoCard>
|
||||
|
||||
{/* SplitPane */}
|
||||
<DemoCard
|
||||
id="splitpane"
|
||||
title="SplitPane"
|
||||
description="Two-column master/detail layout with configurable ratio and empty-state placeholder."
|
||||
>
|
||||
<div style={{ width: '100%', height: 200 }}>
|
||||
<SplitPane
|
||||
list={
|
||||
<div style={{ padding: 16, fontSize: 13 }}>
|
||||
<div style={{ fontWeight: 600, marginBottom: 8 }}>Items</div>
|
||||
<div>Item A</div>
|
||||
<div>Item B</div>
|
||||
<div>Item C</div>
|
||||
</div>
|
||||
}
|
||||
detail={
|
||||
<div style={{ padding: 16, fontSize: 13 }}>
|
||||
<div style={{ fontWeight: 600, marginBottom: 8 }}>Detail View</div>
|
||||
<div>Select an item on the left to see its details here.</div>
|
||||
</div>
|
||||
}
|
||||
ratio="1:2"
|
||||
/>
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 19. Tabs */}
|
||||
<DemoCard
|
||||
id="tabs"
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
Spinner,
|
||||
StatCard,
|
||||
StatusDot,
|
||||
StatusText,
|
||||
Tag,
|
||||
Textarea,
|
||||
Toggle,
|
||||
@@ -204,12 +205,18 @@ export function PrimitivesSection() {
|
||||
<DemoCard
|
||||
id="card"
|
||||
title="Card"
|
||||
description="Surface container with optional left-border accent colour."
|
||||
description="Surface container with optional left-border accent colour and title header."
|
||||
>
|
||||
<Card><div style={{ padding: '8px 12px', fontSize: 13 }}>Plain card</div></Card>
|
||||
<Card accent="amber"><div style={{ padding: '8px 12px', fontSize: 13 }}>Amber accent</div></Card>
|
||||
<Card accent="success"><div style={{ padding: '8px 12px', fontSize: 13 }}>Success accent</div></Card>
|
||||
<Card accent="error"><div style={{ padding: '8px 12px', fontSize: 13 }}>Error accent</div></Card>
|
||||
<Card title="Throughput (msg/s)">
|
||||
<div style={{ padding: '8px 12px', fontSize: 13 }}>Card with title header and separator</div>
|
||||
</Card>
|
||||
<Card accent="amber" title="Error Rate">
|
||||
<div style={{ padding: '8px 12px', fontSize: 13 }}>Title + accent combined</div>
|
||||
</Card>
|
||||
</DemoCard>
|
||||
|
||||
{/* 6. Checkbox */}
|
||||
@@ -559,7 +566,31 @@ export function PrimitivesSection() {
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 29. Tag */}
|
||||
{/* 29. StatusText */}
|
||||
<DemoCard
|
||||
id="statustext"
|
||||
title="StatusText"
|
||||
description="Inline coloured text for status values — five semantic variants with optional bold."
|
||||
>
|
||||
<div className={styles.demoAreaColumn} style={{ width: '100%' }}>
|
||||
<div className={styles.demoAreaRow}>
|
||||
<StatusText variant="success">99.8% uptime</StatusText>
|
||||
<StatusText variant="warning">SLA at risk</StatusText>
|
||||
<StatusText variant="error">BREACH</StatusText>
|
||||
<StatusText variant="running">Processing</StatusText>
|
||||
<StatusText variant="muted">N/A</StatusText>
|
||||
</div>
|
||||
<div className={styles.demoAreaRow}>
|
||||
<StatusText variant="success" bold>99.8% uptime</StatusText>
|
||||
<StatusText variant="warning" bold>SLA at risk</StatusText>
|
||||
<StatusText variant="error" bold>BREACH</StatusText>
|
||||
<StatusText variant="running" bold>Processing</StatusText>
|
||||
<StatusText variant="muted" bold>N/A</StatusText>
|
||||
</div>
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 30. Tag */}
|
||||
<DemoCard
|
||||
id="tag"
|
||||
title="Tag"
|
||||
@@ -573,7 +604,7 @@ export function PrimitivesSection() {
|
||||
<Tag label="removable" color="primary" onRemove={() => undefined} />
|
||||
</DemoCard>
|
||||
|
||||
{/* 30. Textarea */}
|
||||
{/* 31. Textarea */}
|
||||
<DemoCard
|
||||
id="textarea"
|
||||
title="Textarea"
|
||||
@@ -582,7 +613,7 @@ export function PrimitivesSection() {
|
||||
<Textarea placeholder="Enter a description…" style={{ width: 280 }} />
|
||||
</DemoCard>
|
||||
|
||||
{/* 31. Toggle */}
|
||||
{/* 32. Toggle */}
|
||||
<DemoCard
|
||||
id="toggle"
|
||||
title="Toggle"
|
||||
@@ -602,7 +633,7 @@ export function PrimitivesSection() {
|
||||
<Toggle label="Locked off" disabled />
|
||||
</DemoCard>
|
||||
|
||||
{/* 32. Tooltip */}
|
||||
{/* 33. Tooltip */}
|
||||
<DemoCard
|
||||
id="tooltip"
|
||||
title="Tooltip"
|
||||
|
||||
Reference in New Issue
Block a user