@acronis-platform/ui-react 2.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/accordion-container/accordion-container.js +144 -0
- package/dist/components/ui/accordion-container/accordion-container.js.map +1 -0
- package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
- package/dist/components/ui/area-chart/area-chart.js +141 -139
- package/dist/components/ui/area-chart/area-chart.js.map +1 -1
- package/dist/components/ui/avatar/avatar.js +33 -22
- package/dist/components/ui/avatar/avatar.js.map +1 -1
- package/dist/components/ui/bar-chart/bar-chart.js +295 -293
- package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
- package/dist/components/ui/card/card.js +150 -74
- package/dist/components/ui/card/card.js.map +1 -1
- package/dist/components/ui/card-section/card-section.js +111 -0
- package/dist/components/ui/card-section/card-section.js.map +1 -0
- package/dist/components/ui/category-bar/category-bar.js +109 -93
- package/dist/components/ui/category-bar/category-bar.js.map +1 -1
- package/dist/components/ui/chart/chart-palette.js +113 -0
- package/dist/components/ui/chart/chart-palette.js.map +1 -0
- package/dist/components/ui/chart/chart.js +176 -129
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
- package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
- package/dist/components/ui/chart-state/chart-state.js +48 -34
- package/dist/components/ui/chart-state/chart-state.js.map +1 -1
- package/dist/components/ui/chart-widget/chart-widget.js +87 -0
- package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
- package/dist/components/ui/composed-chart/composed-chart.js +117 -115
- package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
- package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
- package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
- package/dist/components/ui/data-table/data-table.js +4 -2
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/empty-overlay/empty-overlay.js +49 -0
- package/dist/components/ui/empty-overlay/empty-overlay.js.map +1 -0
- package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
- package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
- package/dist/components/ui/histogram/histogram.js +94 -92
- package/dist/components/ui/histogram/histogram.js.map +1 -1
- package/dist/components/ui/input/input.js.map +1 -1
- package/dist/components/ui/label/label.js +2 -9
- package/dist/components/ui/label/label.js.map +1 -1
- package/dist/components/ui/line-chart/line-chart.js +143 -141
- package/dist/components/ui/line-chart/line-chart.js.map +1 -1
- package/dist/components/ui/metric/metric.js +74 -185
- package/dist/components/ui/metric/metric.js.map +1 -1
- package/dist/components/ui/page-content/page-content.js.map +1 -1
- package/dist/components/ui/pie-chart/pie-chart.js +201 -195
- package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
- package/dist/components/ui/radar-chart/radar-chart.js +133 -131
- package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
- package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
- package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
- package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
- package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
- package/dist/components/ui/section/section.js +157 -31
- package/dist/components/ui/section/section.js.map +1 -1
- package/dist/components/ui/stepper/stepper.js +24 -24
- package/dist/components/ui/stepper/stepper.js.map +1 -1
- package/dist/components/ui/stepper-item/stepper-item.js +43 -24
- package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
- package/dist/components/ui/table/table.js +27 -27
- package/dist/components/ui/table/table.js.map +1 -1
- package/dist/components/ui/treemap/treemap.js +133 -124
- package/dist/components/ui/treemap/treemap.js.map +1 -1
- package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
- package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
- package/dist/components/ui/wizard/wizard.js +59 -0
- package/dist/components/ui/wizard/wizard.js.map +1 -0
- package/dist/hooks/use-wizard.js +75 -0
- package/dist/hooks/use-wizard.js.map +1 -0
- package/dist/index.js +540 -525
- package/dist/index.js.map +1 -1
- package/dist/llms/accordion-container.md +181 -0
- package/dist/llms/accordion.md +124 -0
- package/dist/llms/alert.md +225 -0
- package/dist/llms/app-shell-chat.md +415 -0
- package/dist/llms/area-chart.md +355 -0
- package/dist/llms/avatar.md +208 -0
- package/dist/llms/bar-chart.md +406 -0
- package/dist/llms/breadcrumb.md +326 -0
- package/dist/llms/button-group.md +298 -0
- package/dist/llms/button-icon-input.md +142 -0
- package/dist/llms/button-icon-menu.md +209 -0
- package/dist/llms/button-icon.md +128 -0
- package/dist/llms/button-menu.md +172 -0
- package/dist/llms/button.md +185 -0
- package/dist/llms/calendar-panel.md +733 -0
- package/dist/llms/calendar.md +188 -0
- package/dist/llms/card-filter.md +214 -0
- package/dist/llms/card-section.md +293 -0
- package/dist/llms/card.md +447 -0
- package/dist/llms/carousel-dialog.md +156 -0
- package/dist/llms/category-bar.md +193 -0
- package/dist/llms/chart-state.md +190 -0
- package/dist/llms/chart-widget.md +209 -0
- package/dist/llms/chart.md +182 -0
- package/dist/llms/checkbox.md +257 -0
- package/dist/llms/chip.md +195 -0
- package/dist/llms/combobox.md +164 -0
- package/dist/llms/composed-chart.md +508 -0
- package/dist/llms/confidence-cone.md +277 -0
- package/dist/llms/data-table.md +474 -0
- package/dist/llms/date-range-picker.md +272 -0
- package/dist/llms/description-list.md +131 -0
- package/dist/llms/dialog-footer-carousel.md +147 -0
- package/dist/llms/dialog-footer-default.md +142 -0
- package/dist/llms/dialog-root.md +246 -0
- package/dist/llms/dialog-welcome.md +223 -0
- package/dist/llms/dialog.md +403 -0
- package/dist/llms/dropdown-menu.md +193 -0
- package/dist/llms/empty-overlay.md +146 -0
- package/dist/llms/empty.md +156 -0
- package/dist/llms/field.md +185 -0
- package/dist/llms/filter-cards.md +157 -0
- package/dist/llms/filter-chips.md +323 -0
- package/dist/llms/filter-search.md +512 -0
- package/dist/llms/form.md +154 -0
- package/dist/llms/funnel-chart.md +333 -0
- package/dist/llms/grid.md +80 -0
- package/dist/llms/histogram.md +190 -0
- package/dist/llms/input-date-picker.md +203 -0
- package/dist/llms/input-otp.md +173 -0
- package/dist/llms/input-password.md +197 -0
- package/dist/llms/input-search.md +170 -0
- package/dist/llms/input-select.md +278 -0
- package/dist/llms/input-text-area.md +271 -0
- package/dist/llms/input-text.md +192 -0
- package/dist/llms/line-chart.md +369 -0
- package/dist/llms/link.md +209 -0
- package/dist/llms/loading.md +130 -0
- package/dist/llms/meter.md +177 -0
- package/dist/llms/metric.md +204 -0
- package/dist/llms/number-field.md +122 -0
- package/dist/llms/page-content.md +69 -0
- package/dist/llms/page-header.md +182 -0
- package/dist/llms/pagination.md +133 -0
- package/dist/llms/pie-chart.md +307 -0
- package/dist/llms/popover.md +252 -0
- package/dist/llms/progress-circle.md +126 -0
- package/dist/llms/progress.md +111 -0
- package/dist/llms/radar-chart.md +316 -0
- package/dist/llms/radial-bar-chart.md +319 -0
- package/dist/llms/radio.md +255 -0
- package/dist/llms/resizable.md +195 -0
- package/dist/llms/sankey-chart.md +208 -0
- package/dist/llms/scatter-chart.md +239 -0
- package/dist/llms/scroll-area.md +183 -0
- package/dist/llms/section.md +401 -0
- package/dist/llms/select.md +210 -0
- package/dist/llms/separator.md +87 -0
- package/dist/llms/sheet.md +202 -0
- package/dist/llms/sidebar-primary.md +360 -0
- package/dist/llms/sidebar-secondary.md +417 -0
- package/dist/llms/skeleton.md +81 -0
- package/dist/llms/slider.md +97 -0
- package/dist/llms/spinner.md +100 -0
- package/dist/llms/stack.md +80 -0
- package/dist/llms/stepper-item.md +309 -0
- package/dist/llms/stepper.md +251 -0
- package/dist/llms/switch.md +140 -0
- package/dist/llms/table.md +436 -0
- package/dist/llms/tabs.md +197 -0
- package/dist/llms/tag.md +139 -0
- package/dist/llms/timeline.md +361 -0
- package/dist/llms/timer.md +259 -0
- package/dist/llms/toast.md +241 -0
- package/dist/llms/toggle-group.md +121 -0
- package/dist/llms/toolbar.md +453 -0
- package/dist/llms/tooltip.md +164 -0
- package/dist/llms/treemap.md +254 -0
- package/dist/llms/trend-indicator.md +132 -0
- package/dist/llms/widget-placeholder.md +169 -0
- package/dist/llms/wizard.md +412 -0
- package/dist/llms.txt +142 -0
- package/dist/react.js +540 -525
- package/dist/react.js.map +1 -1
- package/dist/src/components/ui/accordion-container/accordion-container.d.ts +44 -0
- package/dist/src/components/ui/accordion-container/index.d.ts +1 -0
- package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
- package/dist/src/components/ui/avatar/avatar.d.ts +27 -8
- package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
- package/dist/src/components/ui/button/button.d.ts +1 -1
- package/dist/src/components/ui/card/card.d.ts +69 -8
- package/dist/src/components/ui/card/index.d.ts +1 -1
- package/dist/src/components/ui/card-section/card-section.d.ts +50 -0
- package/dist/src/components/ui/card-section/index.d.ts +1 -0
- package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
- package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
- package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
- package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
- package/dist/src/components/ui/chart/chart.d.ts +66 -15
- package/dist/src/components/ui/chart/index.d.ts +4 -2
- package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
- package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
- package/dist/src/components/ui/chart-state/index.d.ts +1 -0
- package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
- package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
- package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
- package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
- package/dist/src/components/ui/dialog/dialog.d.ts +1 -1
- package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +20 -0
- package/dist/src/components/ui/empty-overlay/index.d.ts +2 -0
- package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
- package/dist/src/components/ui/grid/grid.d.ts +1 -1
- package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
- package/dist/src/components/ui/label/index.d.ts +1 -1
- package/dist/src/components/ui/label/label.d.ts +1 -5
- package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
- package/dist/src/components/ui/metric/metric.d.ts +13 -14
- package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
- package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
- package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
- package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
- package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
- package/dist/src/components/ui/section/index.d.ts +1 -1
- package/dist/src/components/ui/section/section.d.ts +75 -6
- package/dist/src/components/ui/sheet/sheet.d.ts +1 -1
- package/dist/src/components/ui/stack/stack.d.ts +1 -1
- package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
- package/dist/src/components/ui/tag/tag.d.ts +1 -1
- package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
- package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
- package/dist/src/components/ui/wizard/index.d.ts +1 -0
- package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/use-wizard.d.ts +80 -0
- package/dist/src/index.d.ts +5 -3
- package/dist/ui-react.css +1 -1
- package/package.json +12 -6
- package/dist/components/ui/app-shell/app-shell.js +0 -75
- package/dist/components/ui/app-shell/app-shell.js.map +0 -1
- package/dist/components/ui/auth-layout/auth-layout.js +0 -56
- package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
- package/dist/components/ui/collapsible/collapsible.js +0 -22
- package/dist/components/ui/collapsible/collapsible.js.map +0 -1
- package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
- package/dist/src/components/ui/app-shell/index.d.ts +0 -1
- package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
- package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
- package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
- package/dist/src/components/ui/collapsible/index.d.ts +0 -1
- package/dist/src/components/ui/label/label.docs.d.ts +0 -10
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# CategoryBar
|
|
2
|
+
|
|
3
|
+
> A single horizontal bar split into proportional colored segments — one part-to-whole across a handful of categories in one row (onboarding stages, certification status, a pass/fail split). Each segment's width is its value over the sum of all values; segment colors are caller-supplied via `config`. A plain flex composition (not a recharts chart) with one CVA axis (`size`), an optional count/% legend, and per-segment hover tooltips. Design-pending v1 — no chart token tier exists yet.
|
|
4
|
+
|
|
5
|
+
**Status:** draft · **Category:** data-display · **Since:** v0.60.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { CategoryBar } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<CategoryBar
|
|
17
|
+
config={config}
|
|
18
|
+
data={[
|
|
19
|
+
{ key: 'passed', value: 68 },
|
|
20
|
+
{ key: 'warnings', value: 22 },
|
|
21
|
+
{ key: 'failed', value: 10 },
|
|
22
|
+
]}
|
|
23
|
+
showLegend
|
|
24
|
+
/>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A single horizontal bar split into proportional colored segments — a
|
|
28
|
+
part-to-whole across a handful of categories, in one row. Give it a `data` array
|
|
29
|
+
(`key` + `value`) and a per-segment `config`; each segment's width is its value
|
|
30
|
+
over the total.
|
|
31
|
+
|
|
32
|
+
Segment colors come from the `palette` prop — a dataviz palette resolving to the
|
|
33
|
+
`--ui-dataviz-*` tokens, `categorical` by default — not from `config`.
|
|
34
|
+
|
|
35
|
+
> **Design-pending v1.** A plain flex composition (no recharts, no demo
|
|
36
|
+
> playground). Chrome is reconciled with Figma later; Code Connect is deferred.
|
|
37
|
+
|
|
38
|
+
## When to use
|
|
39
|
+
|
|
40
|
+
- A single-level part-to-whole where the **relative size** of each category
|
|
41
|
+
matters and you want it in one compact row (onboarding stages, certification
|
|
42
|
+
status, a pass/warn/fail split).
|
|
43
|
+
- As a header/summary widget above a table or in a dashboard card.
|
|
44
|
+
|
|
45
|
+
## When not to use
|
|
46
|
+
|
|
47
|
+
- Ranked single values that each need their own labelled row — use `Meter`
|
|
48
|
+
stacked into a bar list.
|
|
49
|
+
- Precise value comparison across many categories — a bar chart reads better.
|
|
50
|
+
- A flow across two or more levels — use `SankeyChart`.
|
|
51
|
+
- More than ~6 segments, or very uneven values — thin slivers become unreadable;
|
|
52
|
+
lean on the legend or a different chart.
|
|
53
|
+
|
|
54
|
+
## Variants
|
|
55
|
+
|
|
56
|
+
One CVA axis: `size` (`sm` / `md` / `lg`) sets the track height. Everything else
|
|
57
|
+
is plain props: `showLegend`, `showTooltip`, `valueFormatter`, `defaultOpenIndex`.
|
|
58
|
+
|
|
59
|
+
## Example
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { CategoryBar } from '@acronis-platform/ui-react';
|
|
63
|
+
import type { ChartConfig } from '@acronis-platform/ui-react';
|
|
64
|
+
|
|
65
|
+
const config = {
|
|
66
|
+
passed: {
|
|
67
|
+
label: 'Passed',
|
|
68
|
+
color: 'var(--ui-background-status-strong-success)',
|
|
69
|
+
},
|
|
70
|
+
warnings: {
|
|
71
|
+
label: 'Warnings',
|
|
72
|
+
color: 'var(--ui-background-status-strong-warning)',
|
|
73
|
+
},
|
|
74
|
+
failed: {
|
|
75
|
+
label: 'Failed',
|
|
76
|
+
color: 'var(--ui-background-status-strong-danger)',
|
|
77
|
+
},
|
|
78
|
+
} satisfies ChartConfig;
|
|
79
|
+
|
|
80
|
+
<CategoryBar
|
|
81
|
+
config={config}
|
|
82
|
+
data={[
|
|
83
|
+
{ key: 'passed', value: 68 },
|
|
84
|
+
{ key: 'warnings', value: 22 },
|
|
85
|
+
{ key: 'failed', value: 10 },
|
|
86
|
+
]}
|
|
87
|
+
showLegend
|
|
88
|
+
/>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`showLegend` renders a color dot + label per segment plus each segment's value
|
|
92
|
+
and share of the total. Segment `key`s must be unique and CSS-safe;
|
|
93
|
+
`config[key].label` carries the human-readable text.
|
|
94
|
+
|
|
95
|
+
## Props
|
|
96
|
+
|
|
97
|
+
| Prop | Type | Default | Required | Description |
|
|
98
|
+
|------|------|---------|----------|-------------|
|
|
99
|
+
| `data` | `ReadonlyArray<{ key: string; value: number }>` | — | yes | Ordered segments (left → right). Each `key` indexes `config` for the segment's label and color; each segment's width is its `value` over the sum of all values. |
|
|
100
|
+
| `config` | `ChartConfig` | — | yes | Segments take their colour from the container's `palette`; each entry, keyed by the segment's `key`, maps that segment to a `label` and an optional `tone`. Resolved into the `--color-<key>` custom properties the segments fill from. |
|
|
101
|
+
| `size` | `'sm' | 'md' | 'lg'` | `'md'` | no | Track height. |
|
|
102
|
+
| `showLegend` | `boolean` | `false` | no | Render a legend below the bar — a color dot + label per segment, its value, and its share of the total as a percentage. |
|
|
103
|
+
| `showTooltip` | `boolean` | `true` | no | Wrap each segment in a hover tooltip (dot + label + value + %). |
|
|
104
|
+
| `tooltipContent` | `(segment: { key; value; label; percent; color }) => ReactNode` | — | no | Custom tooltip content per segment — replaces the default (dot + label + `value · %`) inside the same card. Ignored when showTooltip is false. `label` is the resolved config label (any node, not just a string) and `color` is the segment's `var(--color-<key>)` reference, so it follows whichever palette colour the container resolved for that segment. |
|
|
105
|
+
| `valueFormatter` | `(value: number) => string` | `toLocaleString()` | no | Format the numeric value shown in the legend and tooltip. |
|
|
106
|
+
| `defaultOpenIndex` | `number` | — | no | Render the tooltip for this segment index initially open (for visual regression / testing; the tooltip is otherwise hover-only). |
|
|
107
|
+
| `aria-label` | `string` | — | no | Accessible summary of the whole bar. Defaults to a locale-neutral `label value` list built from the data; pass a fuller sentence if you have one. |
|
|
108
|
+
|
|
109
|
+
## Behavior
|
|
110
|
+
|
|
111
|
+
`CategoryBar` renders a single bar split into proportional colored segments from
|
|
112
|
+
a `data` array and a per-segment `config`. It is a plain flex composition, not a
|
|
113
|
+
recharts chart.
|
|
114
|
+
|
|
115
|
+
```gherkin
|
|
116
|
+
Scenario: Render segments proportional to value
|
|
117
|
+
Given data with segments (each a key + value) and a config mapping each key to a label and color
|
|
118
|
+
Then one segment renders per datum, its width its value over the sum of all values
|
|
119
|
+
And each segment is filled from its config color
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```gherkin
|
|
123
|
+
Scenario: Size variant
|
|
124
|
+
Given size is sm, md (default), or lg
|
|
125
|
+
Then the track renders at the corresponding height
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```gherkin
|
|
129
|
+
Scenario: Legend
|
|
130
|
+
Given showLegend is true
|
|
131
|
+
Then a legend renders below the bar with a color dot + label per segment
|
|
132
|
+
And each segment's value plus its share of the total as a percentage
|
|
133
|
+
And when showLegend is false no legend renders
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```gherkin
|
|
137
|
+
Scenario: Tooltip on hover
|
|
138
|
+
Given showTooltip is true
|
|
139
|
+
When the user hovers a segment
|
|
140
|
+
Then a card shows a color dot, the segment's label, and its value + percentage
|
|
141
|
+
And defaultOpenIndex renders that segment's tooltip initially open
|
|
142
|
+
And tooltipContent replaces the default card body with a per-segment render
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```gherkin
|
|
146
|
+
Scenario: Value formatting
|
|
147
|
+
Given a valueFormatter
|
|
148
|
+
Then the legend and tooltip show the formatted value
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```gherkin
|
|
152
|
+
Scenario: Accessible summary
|
|
153
|
+
Given no aria-label
|
|
154
|
+
Then the bar exposes a role="img" with a generated "<label> <value>" summary
|
|
155
|
+
Given an aria-label
|
|
156
|
+
Then that string is used instead
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```gherkin
|
|
160
|
+
Scenario: All-zero data
|
|
161
|
+
Given every segment value is 0
|
|
162
|
+
Then no segment takes width, each share reads 0%, and nothing divides by zero
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Accessibility
|
|
166
|
+
|
|
167
|
+
`CategoryBar` is a data visualization: a single bar of proportional segments.
|
|
168
|
+
|
|
169
|
+
## Text alternative
|
|
170
|
+
|
|
171
|
+
- The track carries `role="img"` with an `aria-label`. By default the label is a
|
|
172
|
+
locale-neutral `"<label> <value>"` list built from the data (e.g. `"Registered
|
|
173
|
+
42, Trained 32, Certified 26"`); pass a fuller `aria-label` sentence when you
|
|
174
|
+
have one. This is the screen-reader equivalent of the bar.
|
|
175
|
+
- **Do not rely on color alone.** The legend (`showLegend`) and the per-segment
|
|
176
|
+
tooltip both carry the text label, so the meaning survives without color
|
|
177
|
+
perception.
|
|
178
|
+
|
|
179
|
+
## Tooltip
|
|
180
|
+
|
|
181
|
+
- The per-segment tooltip is a **hover/pointer** affordance layered on the
|
|
182
|
+
`role="img"` bar; the `aria-label` already conveys the same information to
|
|
183
|
+
assistive tech, so the tooltip is supplementary rather than the sole source.
|
|
184
|
+
- Because the segments sit inside a `role="img"`, their tooltip triggers are not
|
|
185
|
+
exposed as separate focusable controls — keeping a single, coherent image
|
|
186
|
+
semantic instead of a row of unlabeled controls.
|
|
187
|
+
|
|
188
|
+
## Contrast
|
|
189
|
+
|
|
190
|
+
- Segment fills are caller-supplied tokens; pick colors that stay distinguishable
|
|
191
|
+
from each other and from the track background in both light and dark themes.
|
|
192
|
+
Adjacent segments should not read as one block — the legend disambiguates when
|
|
193
|
+
hues are close.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# ChartState
|
|
2
|
+
|
|
3
|
+
> A shared loading / empty / error placeholder for the chart types, rendered in place of the chart inside the same sized slot. A compact status block — a leading glyph over a centered label, with an optional retry action for the error state. The `empty` state draws the silhouette the design defines for each chart type (`variant`), plus an optional "Widget description" line; `loading` and `error` share one treatment. Distinct from `WidgetPlaceholder`, which is the composable skeleton for a widget that isn't a chart — see behavior.md.
|
|
4
|
+
|
|
5
|
+
**Status:** draft · **Category:** data-display · **Since:** v0.58.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { ChartState } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<ChartState state="loading" />
|
|
17
|
+
<ChartState state="empty" variant="bar" description="No results for the selected range" />
|
|
18
|
+
<ChartState
|
|
19
|
+
state="error"
|
|
20
|
+
action={<Button variant="ghost" onClick={refetch}>Try again</Button>}
|
|
21
|
+
/>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
A shared loading / empty / error placeholder for the chart types. Render it in
|
|
25
|
+
place of a chart — inside the same sized slot — while data is loading, when
|
|
26
|
+
there is nothing to plot, or when the fetch failed.
|
|
27
|
+
|
|
28
|
+
> **Design-pending v1.** Ported from the Figma InputSelect dropdown states (the
|
|
29
|
+
> reference node for this task) and kept visually in step with the shipped
|
|
30
|
+
> `InputSelectStatus`. There is no `--ui-chart-*` token tier yet, so it themes
|
|
31
|
+
> from existing semantic tokens directly.
|
|
32
|
+
|
|
33
|
+
## When to use
|
|
34
|
+
|
|
35
|
+
- A chart cannot render yet (data in flight), has no data to plot, or failed to
|
|
36
|
+
load — swap the chart for the matching `ChartState`.
|
|
37
|
+
- You want the same placeholder across every chart type, rather than each chart
|
|
38
|
+
hand-rolling its own.
|
|
39
|
+
|
|
40
|
+
## When not to use
|
|
41
|
+
|
|
42
|
+
- A full-page empty state (illustration, title, description, primary action) —
|
|
43
|
+
use the larger `Empty` component instead.
|
|
44
|
+
- A bare loading indicator with no surrounding message — use `Spinner` directly.
|
|
45
|
+
|
|
46
|
+
## Example
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { ChartState, ChartContainer, Button } from '@acronis-platform/ui-react';
|
|
50
|
+
|
|
51
|
+
function Sales({ status, data, config, refetch }) {
|
|
52
|
+
if (status === 'loading') return <ChartState state="loading" />;
|
|
53
|
+
if (status === 'error')
|
|
54
|
+
return (
|
|
55
|
+
<ChartState
|
|
56
|
+
state="error"
|
|
57
|
+
action={
|
|
58
|
+
<Button variant="ghost" onClick={refetch}>
|
|
59
|
+
Try again
|
|
60
|
+
</Button>
|
|
61
|
+
}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
if (!data.length) return <ChartState state="empty" />;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<ChartContainer config={config} className="h-[300px] w-[500px]">
|
|
68
|
+
{/* …the chart… */}
|
|
69
|
+
</ChartContainer>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The three states share one compact layout: a leading glyph (spinner / inbox /
|
|
75
|
+
warning) over a centered label. Pass `message` to override a state's default
|
|
76
|
+
label, and `action` to add a retry affordance to the error state.
|
|
77
|
+
|
|
78
|
+
## Props
|
|
79
|
+
|
|
80
|
+
| Prop | Type | Default | Required | Description |
|
|
81
|
+
|------|------|---------|----------|-------------|
|
|
82
|
+
| `state` | `'loading' | 'empty' | 'error'` | — | yes | Which placeholder to show. Drives the leading artwork and the default label (loading → spinner + "Data is loading…"; empty → variant silhouette or no artwork + "No data found"; error → warning glyph + "Something went wrong"). |
|
|
83
|
+
| `variant` | `'area' | 'bar' | 'line' | 'donut' | 'radial' | 'funnel' | 'radar' | 'sankey' | 'scatter' | 'treemap' | 'table' | 'text'` | — | no | Which chart the slot holds. The `empty` state then draws that type's silhouette instead of the generic inbox glyph — the design defines one per chart type. `donut` and `radial` share a ring. Ignored by `loading` and `error`, which share one treatment. |
|
|
84
|
+
| `description` | `ReactNode` | — | no | Text shown below the illustration or glyph. Defaults to `"Data is loading…"` / `"No data found"` / `"Something went wrong"` based on `state`. Pass a value to override for any state. |
|
|
85
|
+
| `action` | `ReactNode` | — | no | Trailing action rendered for the `error` state only (e.g. a "Try again" button). |
|
|
86
|
+
|
|
87
|
+
## Behavior
|
|
88
|
+
|
|
89
|
+
### Rendering per state
|
|
90
|
+
|
|
91
|
+
- **Given** `state="loading"`, **when** rendered, **then** it shows the shared
|
|
92
|
+
`Spinner` (lg) above the label "Data is loading…" and announces the label via
|
|
93
|
+
its `role="status"` live region (no `aria-busy`, which could defer that
|
|
94
|
+
announcement).
|
|
95
|
+
- **Given** `state="empty"`, **when** rendered, **then** it shows the label
|
|
96
|
+
"No data found" (no artwork unless a `variant` is passed).
|
|
97
|
+
- **Given** `state="error"`, **when** rendered, **then** it shows the warning
|
|
98
|
+
glyph above the label "Something went wrong" and announces as an alert.
|
|
99
|
+
|
|
100
|
+
### Label override
|
|
101
|
+
|
|
102
|
+
- **Given** a `description` prop, **when** rendered in any state, **then** the
|
|
103
|
+
supplied text replaces that state's default label.
|
|
104
|
+
|
|
105
|
+
### Action
|
|
106
|
+
|
|
107
|
+
- **Given** an `action` node, **when** `state="error"`, **then** the action is
|
|
108
|
+
rendered after the label (e.g. a "Try again" button).
|
|
109
|
+
- **Given** an `action` node, **when** `state` is `loading` or `empty`, **then**
|
|
110
|
+
the action is **not** rendered.
|
|
111
|
+
|
|
112
|
+
### Layout
|
|
113
|
+
|
|
114
|
+
- **Given** any state, **when** placed in a sized slot (the same box a chart
|
|
115
|
+
would occupy), **then** the block fills it (`size-full`) and centers its
|
|
116
|
+
content, with a `min-h-32` floor so it stays legible in an unsized parent.
|
|
117
|
+
|
|
118
|
+
### Per-type empty states
|
|
119
|
+
|
|
120
|
+
`empty` is the one state the design draws per chart type: an area silhouette for
|
|
121
|
+
an area chart, a ring for a donut, a funnel for a funnel. An empty widget then
|
|
122
|
+
still says what it _would_ have shown, which a single generic glyph can't.
|
|
123
|
+
|
|
124
|
+
`variant` selects it. `donut` and `radial` share one ring — a radial-bar widget
|
|
125
|
+
with no data has nothing to tell it apart. Without a `variant` the empty state
|
|
126
|
+
shows no artwork — just the text.
|
|
127
|
+
|
|
128
|
+
The geometry is exported from the Figma instances rather than redrawn, and every
|
|
129
|
+
path is `currentColor`: the tone is set once on the container, so brand and theme
|
|
130
|
+
overrides reach the artwork. Two of the eleven silhouettes use a second, fainter
|
|
131
|
+
tone (the table and metric-list ones tint their header row darker than the body
|
|
132
|
+
rows); that hierarchy is kept as `fill-opacity` on the same colour rather than a
|
|
133
|
+
second token.
|
|
134
|
+
|
|
135
|
+
The caption is **one line, in one style**. Figma styles it `body/default` on
|
|
136
|
+
`text/onSurface/primary` — 14/24, centred, _not_ muted — which is what the root
|
|
137
|
+
already applies, so the description and the fallback status label read
|
|
138
|
+
identically. For a per-type empty state that line is the `description`: the mockups draw the silhouette over "Widget description" with no
|
|
139
|
+
status label, because the artwork already says "no data" and the sentence worth
|
|
140
|
+
the space is what the widget _would_ show. With no `description` it falls back to
|
|
141
|
+
the status label, so a caller that only sets `variant` still reads sensibly.
|
|
142
|
+
|
|
143
|
+
`loading` and `error` deliberately keep one shared treatment — the design only
|
|
144
|
+
redesigned `empty`.
|
|
145
|
+
|
|
146
|
+
### Not `WidgetPlaceholder`
|
|
147
|
+
|
|
148
|
+
Two components, two jobs, and they are not interchangeable:
|
|
149
|
+
|
|
150
|
+
- **`ChartState`** is the placeholder _inside_ a chart's slot. It fills whatever
|
|
151
|
+
gives the slot its height, renders one of three states, and is what a chart
|
|
152
|
+
widget swaps in for its plot.
|
|
153
|
+
- **`WidgetPlaceholder`** is a composable skeleton with its own header, footer
|
|
154
|
+
and action parts, plus an `interactive` affordance — for a dashboard widget
|
|
155
|
+
that isn't a chart, or a whole tile that is still being set up.
|
|
156
|
+
|
|
157
|
+
`WidgetPlaceholder` predates the per-type empty states and is used by consuming
|
|
158
|
+
MFEs, so it stays.
|
|
159
|
+
|
|
160
|
+
## Accessibility
|
|
161
|
+
|
|
162
|
+
- The root is a **live region** so a screen reader announces the state change
|
|
163
|
+
when a chart swaps its content in:
|
|
164
|
+
- `loading` / `empty` → `role="status"` with `aria-live="polite"`.
|
|
165
|
+
- `error` → `role="alert"` with `aria-live="assertive"`.
|
|
166
|
+
- On `loading` the shared `Spinner` is rendered `aria-hidden` so the root's own
|
|
167
|
+
live region (with the label) announces once, rather than the Spinner's
|
|
168
|
+
`role="status"` + sr-only "Loading…" nesting inside it and double-announcing.
|
|
169
|
+
- The root does **not** carry `aria-busy`. It is itself the live region, and some
|
|
170
|
+
screen readers defer announcing a busy region's content until `aria-busy`
|
|
171
|
+
clears; because this placeholder is unmounted (swapped for the chart) rather
|
|
172
|
+
than un-busied, a busy flag would risk swallowing the "Data is loading…"
|
|
173
|
+
announcement. Put `aria-busy` on the chart-slot **container** instead if a busy
|
|
174
|
+
signal is needed (same guidance as `Skeleton`).
|
|
175
|
+
- The live-region attributes (`role` / `aria-live`) are applied after the prop
|
|
176
|
+
spread, so the state's a11y contract is not clobbered by a consumer-passed
|
|
177
|
+
attribute.
|
|
178
|
+
- The label text is a real `<p>`, so the state message is always in the
|
|
179
|
+
accessible tree — the glyph is decorative and does not need its own label.
|
|
180
|
+
- **Do not rely on the glyph color alone** to convey the state — the label
|
|
181
|
+
carries the meaning. Icon colors (`--ui-glyph-on-status-info` /
|
|
182
|
+
`--ui-glyph-on-status-warning`) and the label (`--ui-text-on-surface-primary`)
|
|
183
|
+
resolve to semantic tokens that meet contrast in both themes.
|
|
184
|
+
- If the error state offers a retry, pass an actual interactive control as
|
|
185
|
+
`action` (a `Button`) so it is focusable and keyboard-operable.
|
|
186
|
+
|
|
187
|
+
## Contrast
|
|
188
|
+
|
|
189
|
+
Label and glyph colors resolve to semantic `--ui-*` tokens that meet contrast on
|
|
190
|
+
the surface in both light and dark themes.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# ChartWidget
|
|
2
|
+
|
|
3
|
+
> The dashboard card a chart sits in: a `Card` with the standard widget header (title, optional filter chip and the ⋯ actions menu) and an optional metric readout above the plot. Adds the one thing a `Card` doesn't know about — the body's loading / empty / error placeholder — and re-implements nothing else: its `header` is `CardHeaderProps`, spread. It declares no size: the grid sizes the card and the widget passes that height down, so a `size-full` chart fills whatever the header leaves. The per-type chart components stay card-less, so a chart is still usable outside a widget.
|
|
4
|
+
|
|
5
|
+
**Status:** draft · **Category:** data-display · **Since:** v2.1.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { ChartWidget } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<ChartWidget
|
|
17
|
+
header={{
|
|
18
|
+
title: 'Active alerts by severity',
|
|
19
|
+
extras: <Tag variant="info">Last 6 months</Tag>,
|
|
20
|
+
actions: <ButtonIcon aria-label="Widget actions"><EllipsisIcon size={16} /></ButtonIcon>,
|
|
21
|
+
}}
|
|
22
|
+
className="h-[300px] w-[592px]"
|
|
23
|
+
>
|
|
24
|
+
<AreaChart config={config} data={data} dataKeys={['sessions']} xKey="month" className="size-full" />
|
|
25
|
+
</ChartWidget>
|
|
26
|
+
|
|
27
|
+
<ChartWidget header={{ title: 'Severity' }} state="empty" bodyClassName="h-[252px]" />
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The dashboard card a chart sits in.
|
|
31
|
+
|
|
32
|
+
Every chart in the Figma library is drawn inside the same card: a header with a
|
|
33
|
+
title, an optional filter chip and the ⋯ actions menu, then an optional metric
|
|
34
|
+
readout, then the plot. `ChartWidget` is that composition — and only that.
|
|
35
|
+
|
|
36
|
+
## What it adds
|
|
37
|
+
|
|
38
|
+
One thing a `Card` legitimately doesn't know about: **what the body shows while
|
|
39
|
+
there is no plot.** `state="loading" | "empty" | "error"` renders `ChartState` in
|
|
40
|
+
place of the chart, and `error` also gives the Card its error border — one prop,
|
|
41
|
+
not two.
|
|
42
|
+
|
|
43
|
+
## What it doesn't add
|
|
44
|
+
|
|
45
|
+
A header. `header` is typed `CardHeaderProps` and spread onto `CardHeader`, so
|
|
46
|
+
the whole header surface — including the parts this component never mentions
|
|
47
|
+
(`isDraggable`, `hasRename`, `isCollapsible`, …) — comes from `Card` and stays
|
|
48
|
+
in sync with it.
|
|
49
|
+
|
|
50
|
+
Nor a chart. The per-type chart components stay card-less, which keeps them
|
|
51
|
+
usable outside a widget.
|
|
52
|
+
|
|
53
|
+
## Why it takes no `size`
|
|
54
|
+
|
|
55
|
+
The Figma sets carry `size` = `sm` / `md` / `lg`, but it only changes the frame
|
|
56
|
+
width (288 / 592 / 896). The height is the dashboard grid's, and the widget
|
|
57
|
+
passes it down: the header takes what it needs, and a `size-full` chart fills
|
|
58
|
+
the rest of the card. See `behavior.md`.
|
|
59
|
+
|
|
60
|
+
## Files
|
|
61
|
+
|
|
62
|
+
| File | What's in it |
|
|
63
|
+
| ------------------ | ------------------------------------------------------------------ |
|
|
64
|
+
| `index.yaml` | Identity, category, status, `since`. |
|
|
65
|
+
| `api.yaml` | The prop contract and the per-framework adapters. |
|
|
66
|
+
| `anatomy.yaml` | Parts, the schematic, and the four body states. |
|
|
67
|
+
| `behavior.md` | Composition, how the height reaches the plot, state, localization. |
|
|
68
|
+
| `accessibility.md` | Roles, the placeholder's live region, header controls. |
|
|
69
|
+
| `tokens.yaml` | The `--ui-*` tokens the widget references. |
|
|
70
|
+
|
|
71
|
+
## No Figma Code Connect
|
|
72
|
+
|
|
73
|
+
Deliberately absent. `ChartWidget` has no node of its own: in Figma the shape
|
|
74
|
+
lives inside each chart's own component set (ChartArea `8174:22232`, ChartBar
|
|
75
|
+
`8700:55606`, …), which wrap the `_CardLegacy` instance — and that maps to
|
|
76
|
+
`Card`, which is already connected.
|
|
77
|
+
|
|
78
|
+
So the connection belongs on the per-type chart components, whose examples
|
|
79
|
+
should show `<ChartWidget>` wrapping their plot. A `.figma.tsx` here would have
|
|
80
|
+
to point at one arbitrary chart set and would read as if the widget were that
|
|
81
|
+
chart.
|
|
82
|
+
|
|
83
|
+
## Props
|
|
84
|
+
|
|
85
|
+
| Prop | Type | Default | Required | Description |
|
|
86
|
+
|------|------|---------|----------|-------------|
|
|
87
|
+
| `header` | `CardHeaderProps` | — | no | The card header, forwarded verbatim to `CardHeader` — `title`, `description`, `extras` (a filter chip), `actions` (the ⋯ menu), `isDraggable`, `isSwitchable`, `hasAvatar`, `hasRename`, `isCollapsible`. Omit for a header-less widget. |
|
|
88
|
+
| `orientation` | `'vertical' | 'horizontal'` | `vertical` | no | How the `metric` and plot are arranged inside the card body. `vertical` (default) — metric above the plot, full width; the metric takes its natural height and the plot fills the rest. `horizontal` — metric and plot side by side, each `flex-1`; use for compact (sm/288px) widgets where vertical space is at a premium. |
|
|
89
|
+
| `metric` | `ReactNode` | — | no | The readout beside or above the plot (depending on `orientation`), typically a `Metric`. |
|
|
90
|
+
| `variant` | `'area' | 'bar' | 'line' | 'donut' | 'radial' | 'funnel' | 'radar' | 'sankey' | 'scatter' | 'treemap' | 'table' | 'text'` | — | no | Which chart the widget holds. Only used while `state="empty"`, to pick the silhouette for that chart type. Forwarded to `ChartState`. |
|
|
91
|
+
| `state` | `'loading' | 'empty' | 'error'` | — | no | Renders `ChartState` in place of the plot, keeping the header. `error` additionally gives the Card its error border. |
|
|
92
|
+
| `stateDescription` | `ReactNode` | — | no | Text below the placeholder illustration or glyph. Forwarded to `ChartState` as `description`. Defaults to the per-state built-in ("Data is loading…" / "No data found" / "Something went wrong"). |
|
|
93
|
+
| `stateAction` | `ReactNode` | — | no | Trailing action on the `error` placeholder — e.g. a retry button. |
|
|
94
|
+
| `bodyClassName` | `string` | — | no | Class for the card body. Rarely needed — the body already fills the card. For the case where neither the grid nor the content gives a height, e.g. a placeholder-only widget outside a sized cell. |
|
|
95
|
+
| `children` | `ReactNode` | — | no | The chart. Replaced by the placeholder while `state` is set. |
|
|
96
|
+
| `render` | `useRender.RenderProp` | — | no | Replace the rendered `<div>` with another element or component (Base UI composition), forwarded to `Card`. How a dashboard gives each widget a landmark role — `render={<section aria-label="Sessions" />}` — which is the one thing the widget can't infer for itself. |
|
|
97
|
+
|
|
98
|
+
## Behavior
|
|
99
|
+
|
|
100
|
+
### Composition, not a container
|
|
101
|
+
|
|
102
|
+
`ChartWidget` is a `Card` plus two things a card doesn't know about: what the
|
|
103
|
+
body shows while there's no plot, and how much height to reserve for it.
|
|
104
|
+
Everything else is `Card`'s.
|
|
105
|
+
|
|
106
|
+
The `header` prop is typed `CardHeaderProps` and spread onto `CardHeader`. That
|
|
107
|
+
is the whole header contract — title, description, the `extras` filter chip, the
|
|
108
|
+
`actions` ⋯ menu, the drag handle, the switch, the avatar, rename, and the
|
|
109
|
+
collapse trigger. None of it is re-declared here, so a header feature added to
|
|
110
|
+
`Card` reaches this component without a change, and can't drift out of sync.
|
|
111
|
+
|
|
112
|
+
The per-type chart components stay **card-less**. They are the plot; the card is
|
|
113
|
+
composition. That keeps `<AreaChart>` usable inside a table cell, a popover or a
|
|
114
|
+
`Metric`'s sparkline slot, where a card would be wrong.
|
|
115
|
+
|
|
116
|
+
### No size of its own
|
|
117
|
+
|
|
118
|
+
The Figma sets carry a `size` axis of `sm` / `md` / `lg`, but measuring all eight
|
|
119
|
+
shows it only changes the frame **width** — 288 / 592 / 896. And every card, and
|
|
120
|
+
every card body, is `HUG` vertically: the height is whatever the content adds up
|
|
121
|
+
to.
|
|
122
|
+
|
|
123
|
+
So the widget declares no size — it _passes one down_. The dashboard grid gives
|
|
124
|
+
the card a height; the card is a full-height flex column; the header is
|
|
125
|
+
`shrink-0`; the body takes the rest; and the plot slot takes what the metric row
|
|
126
|
+
leaves. A chart given `size-full` therefore occupies the whole remaining card.
|
|
127
|
+
|
|
128
|
+
`min-h-0` appears at every step of that chain, and it is load-bearing: without
|
|
129
|
+
it a flex child refuses to shrink below its content's height, so a tall chart
|
|
130
|
+
would push the card past the height the grid gave it instead of fitting inside.
|
|
131
|
+
|
|
132
|
+
Dropped into a parent with no definite height, `h-full` resolves to `auto` and
|
|
133
|
+
the card hugs its content instead — so a standalone widget still works, with the
|
|
134
|
+
chart bringing its own height. `bodyClassName` covers the remaining gap: a
|
|
135
|
+
placeholder-only widget outside a sized cell, where neither the grid nor the
|
|
136
|
+
content can give a height.
|
|
137
|
+
|
|
138
|
+
An earlier draft of this component reserved a fixed body height per chart type,
|
|
139
|
+
reverse-engineered from the mockup frames (252 for area, 205 for funnel, …).
|
|
140
|
+
That was wrong: those numbers are what a hug layout happened to produce, not a
|
|
141
|
+
design constraint, and hard-coding them would have fought the grid.
|
|
142
|
+
|
|
143
|
+
### State
|
|
144
|
+
|
|
145
|
+
`state` renders `ChartState` **instead of** `children`. The header stays.
|
|
146
|
+
|
|
147
|
+
`state="error"` also maps to `Card`'s `hasError`, so the error border and the
|
|
148
|
+
error placeholder come from one prop. A caller can't get one without the other,
|
|
149
|
+
which is what the mockups draw.
|
|
150
|
+
|
|
151
|
+
`ChartState` owns the live-region contract (`role="status"` for loading/empty,
|
|
152
|
+
`role="alert"` for error), so the announcement follows the placeholder rather
|
|
153
|
+
than being re-declared on the widget.
|
|
154
|
+
|
|
155
|
+
### Localization
|
|
156
|
+
|
|
157
|
+
Every string is the caller's: the header's `title` / `description`, the metric's
|
|
158
|
+
own labels, `stateDescription`, and the label inside `stateAction`. The only
|
|
159
|
+
literals are `ChartState`'s per-state defaults, which `stateDescription` overrides.
|
|
160
|
+
|
|
161
|
+
## Accessibility
|
|
162
|
+
|
|
163
|
+
## Roles and structure
|
|
164
|
+
|
|
165
|
+
The widget itself is a plain `div` (a `Card`) and adds no role: it is a grouping
|
|
166
|
+
surface, and the meaning lives in what it holds.
|
|
167
|
+
|
|
168
|
+
A dashboard that wants each widget to be a landmark uses `render`, which the
|
|
169
|
+
widget forwards to `Card`:
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
<ChartWidget
|
|
173
|
+
render={<section aria-label="Sessions" />}
|
|
174
|
+
header={{ title: 'Sessions' }}
|
|
175
|
+
>
|
|
176
|
+
{chart}
|
|
177
|
+
</ChartWidget>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The accessible name has to come from the caller: the header's `title` is visible
|
|
181
|
+
text inside the region, not a name for it, so a landmark without an
|
|
182
|
+
`aria-label` (or `aria-labelledby` pointing at the title) is an unnamed region.
|
|
183
|
+
|
|
184
|
+
The heading text comes from `CardHeader`'s `title`. It is the widget's visible
|
|
185
|
+
name, so a chart inside should not repeat it in its own accessible name.
|
|
186
|
+
|
|
187
|
+
## The placeholder is the live region
|
|
188
|
+
|
|
189
|
+
While `state` is set, `ChartState` is the live region — `role="status"`
|
|
190
|
+
(`aria-live="polite"`) for loading and empty, `role="alert"`
|
|
191
|
+
(`aria-live="assertive"`) for error. The widget deliberately does **not** add
|
|
192
|
+
`aria-busy` around it: the placeholder is unmounted and replaced by the plot
|
|
193
|
+
rather than un-busied, and some assistive tech defers announcing a busy region's
|
|
194
|
+
content, which would swallow the loading announcement.
|
|
195
|
+
|
|
196
|
+
A consumer that wants a busy signal sets `aria-busy` on the element it controls
|
|
197
|
+
— the widget accepts it as a passthrough prop.
|
|
198
|
+
|
|
199
|
+
## Header controls
|
|
200
|
+
|
|
201
|
+
The header's affordances are `Card`'s and carry its labelling: the drag handle,
|
|
202
|
+
switch, rename button and collapse trigger each take a label prop with a
|
|
203
|
+
default, and the `actions` slot is the caller's own control (an icon button
|
|
204
|
+
needs its own `aria-label` — the mockups' ⋯ menu has no visible text).
|
|
205
|
+
|
|
206
|
+
## Colour is not the only signal
|
|
207
|
+
|
|
208
|
+
`state="error"` changes the card's border colour, but the placeholder also
|
|
209
|
+
carries an icon and a message, so the error is not conveyed by colour alone.
|