@czi-sds/mcp 0.16.0 → 0.18.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/README.md +2 -1
- package/data/component-docs/Accordion.md +291 -0
- package/data/component-docs/Autocomplete.md +525 -0
- package/data/component-docs/Banner.md +231 -0
- package/data/component-docs/Button.md +224 -0
- package/data/component-docs/ButtonDropdown.md +256 -0
- package/data/component-docs/ButtonGroup.md +320 -0
- package/data/component-docs/ButtonToggle.md +397 -0
- package/data/component-docs/Callout.md +268 -0
- package/data/component-docs/CellBasic.md +122 -0
- package/data/component-docs/CellComponent.md +63 -0
- package/data/component-docs/CellHeader.md +128 -0
- package/data/component-docs/ComplexFilter.md +294 -0
- package/data/component-docs/ContentCard.md +406 -0
- package/data/component-docs/Dialog.md +651 -0
- package/data/component-docs/Dropdown.md +155 -0
- package/data/component-docs/DropdownMenu.md +949 -0
- package/data/component-docs/HeatmapChart.md +782 -0
- package/data/component-docs/Hero.md +363 -0
- package/data/component-docs/Icon.md +283 -0
- package/data/component-docs/InputCheckbox.md +205 -0
- package/data/component-docs/InputDropdown.md +482 -0
- package/data/component-docs/InputRadio.md +173 -0
- package/data/component-docs/InputSearch.md +81 -0
- package/data/component-docs/InputSlider.md +189 -0
- package/data/component-docs/InputText.md +110 -0
- package/data/component-docs/InputToggle.md +141 -0
- package/data/component-docs/IntentMessage.md +293 -0
- package/data/component-docs/Legend.md +442 -0
- package/data/component-docs/Link.md +23 -0
- package/data/component-docs/List.md +300 -0
- package/data/component-docs/LoadingIndicator.md +198 -0
- package/data/component-docs/Menu.md +298 -0
- package/data/component-docs/MenuItem.md +100 -0
- package/data/component-docs/NavigationFooter.md +165 -0
- package/data/component-docs/NavigationHeader.md +500 -0
- package/data/component-docs/NavigationJumpTo.md +414 -0
- package/data/component-docs/Notification.md +288 -0
- package/data/component-docs/Pagination.md +67 -0
- package/data/component-docs/Panel.md +636 -0
- package/data/component-docs/Popover.md +448 -0
- package/data/component-docs/PreComposedTable.md +2947 -0
- package/data/component-docs/ProteinStructureViewer.md +1044 -0
- package/data/component-docs/SegmentedControl.md +396 -0
- package/data/component-docs/StackedBarChart.md +475 -0
- package/data/component-docs/Table.md +2000 -0
- package/data/component-docs/TableHeader.md +53 -0
- package/data/component-docs/TableRow.md +74 -0
- package/data/component-docs/Tabs.md +455 -0
- package/data/component-docs/Tag.md +647 -0
- package/data/component-docs/TagFilter.md +131 -0
- package/data/component-docs/Tooltip.md +561 -0
- package/data/component-docs/TooltipCondensed.md +203 -0
- package/data/component-docs/TooltipTable.md +145 -0
- package/data/component-docs/index.json +267 -0
- package/data/component-list.json +2 -5
- package/data/component-props/Accordion.json +31 -11
- package/data/component-props/Alert.json +3 -3
- package/data/component-props/Autocomplete.json +150 -15
- package/data/component-props/Banner.json +31 -17
- package/data/component-props/Button.json +45 -20
- package/data/component-props/ButtonDropdown.json +36 -17
- package/data/component-props/ButtonDropdownLegacy.json +11 -11
- package/data/component-props/ButtonGroup.json +32 -11
- package/data/component-props/ButtonIcon.json +10 -10
- package/data/component-props/ButtonLegacy.json +19 -19
- package/data/component-props/ButtonToggle.json +32 -19
- package/data/component-props/ButtonToggleLegacy.json +11 -11
- package/data/component-props/Callout.json +58 -17
- package/data/component-props/CellBasic.json +94 -41
- package/data/component-props/CellComponent.json +23 -7
- package/data/component-props/CellHeader.json +62 -19
- package/data/component-props/ComplexFilter.json +52 -21
- package/data/component-props/ContentCard.json +70 -47
- package/data/component-props/Dialog.json +47 -14
- package/data/component-props/Dropdown.json +68 -31
- package/data/component-props/DropdownMenu.json +128 -23
- package/data/component-props/HeatmapChart.json +88 -12
- package/data/component-props/Hero.json +80 -39
- package/data/component-props/Icon.json +20 -9
- package/data/component-props/InputCheckbox.json +36 -14
- package/data/component-props/InputDropdown.json +75 -27
- package/data/component-props/InputRadio.json +31 -13
- package/data/component-props/InputSearch.json +41 -18
- package/data/component-props/InputSlider.json +46 -19
- package/data/component-props/InputText.json +26 -19
- package/data/component-props/InputToggle.json +26 -9
- package/data/component-props/IntentMessage.json +38 -9
- package/data/component-props/Link.json +13 -7
- package/data/component-props/LoadingIndicator.json +11 -3
- package/data/component-props/MenuItem.json +38 -19
- package/data/component-props/MenuSelect.json +8 -8
- package/data/component-props/NavigationFooter.json +49 -19
- package/data/component-props/NavigationHeader.json +150 -37
- package/data/component-props/NavigationJumpTo.json +33 -7
- package/data/component-props/Notification.json +39 -21
- package/data/component-props/Pagination.json +39 -11
- package/data/component-props/Panel.json +62 -9
- package/data/component-props/Popover.json +80 -7
- package/data/component-props/PreComposedTable.json +99 -27
- package/data/component-props/ProteinStructureViewer.json +117 -0
- package/data/component-props/SegmentedControl.json +42 -3
- package/data/component-props/StackedBarChart.json +116 -42
- package/data/component-props/TableRow.json +38 -19
- package/data/component-props/Tabs.json +66 -9
- package/data/component-props/Tag.json +34 -17
- package/data/component-props/TagFilter.json +10 -3
- package/data/component-props/Tooltip.json +49 -18
- package/data/component-props/TooltipCondensed.json +18 -9
- package/data/component-props/TooltipTable.json +17 -7
- package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
- package/data/resources/figma-rules.md +2 -2
- package/data/tailwind.json +20 -14
- package/dist/stdio.js +112 -208
- package/package.json +9 -5
- package/data/component-props/ButtonDropdownV2.json +0 -34
- package/data/component-props/ButtonNew.json +0 -47
- package/data/component-props/ButtonToggleV2.json +0 -44
- package/data/component-props/ButtonV2.json +0 -43
- package/data/zeroheight-cache.json +0 -10259
- package/data/zeroheight-exports/Accordion.md +0 -774
- package/data/zeroheight-exports/Banner.md +0 -691
- package/data/zeroheight-exports/Borders.md +0 -117
- package/data/zeroheight-exports/Buttons.md +0 -1854
- package/data/zeroheight-exports/Callout.md +0 -931
- package/data/zeroheight-exports/Colors.md +0 -1755
- package/data/zeroheight-exports/Content-Card.md +0 -309
- package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
- package/data/zeroheight-exports/Control-Inputs.md +0 -2818
- package/data/zeroheight-exports/Corners.md +0 -104
- package/data/zeroheight-exports/Cover-page.md +0 -3
- package/data/zeroheight-exports/Dialog.md +0 -1367
- package/data/zeroheight-exports/Downloads.md +0 -236
- package/data/zeroheight-exports/Drop-Shadows.md +0 -79
- package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
- package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
- package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
- package/data/zeroheight-exports/Field-Inputs.md +0 -1405
- package/data/zeroheight-exports/Filters.md +0 -1111
- package/data/zeroheight-exports/Getting-Started.md +0 -1157
- package/data/zeroheight-exports/Hero.md +0 -414
- package/data/zeroheight-exports/Icons.md +0 -2067
- package/data/zeroheight-exports/Intent.md +0 -86
- package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
- package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
- package/data/zeroheight-exports/Introduction.md +0 -115
- package/data/zeroheight-exports/Link.md +0 -432
- package/data/zeroheight-exports/Lists.md +0 -986
- package/data/zeroheight-exports/Loading-Indicator.md +0 -41
- package/data/zeroheight-exports/Navigation.md +0 -1190
- package/data/zeroheight-exports/Notification.md +0 -1038
- package/data/zeroheight-exports/Panel.md +0 -1030
- package/data/zeroheight-exports/README.md +0 -122
- package/data/zeroheight-exports/Responsive-Design.md +0 -330
- package/data/zeroheight-exports/Search-Input.md +0 -91
- package/data/zeroheight-exports/Search.md +0 -822
- package/data/zeroheight-exports/Segmented-Control.md +0 -609
- package/data/zeroheight-exports/Spacing.md +0 -297
- package/data/zeroheight-exports/Table.md +0 -5345
- package/data/zeroheight-exports/Tabs.md +0 -1324
- package/data/zeroheight-exports/Tags.md +0 -1545
- package/data/zeroheight-exports/Text-Link.md +0 -268
- package/data/zeroheight-exports/Theming.md +0 -362
- package/data/zeroheight-exports/Tooltips.md +0 -2015
- package/data/zeroheight-exports/Typography.md +0 -800
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# StackedBarChart
|
|
2
|
+
|
|
3
|
+
A single horizontal bar split into labelled segments, for showing what a total is made of.
|
|
4
|
+
|
|
5
|
+
> **Ships separately:** StackedBarChart comes from `@czi-sds/data-viz`, not `@czi-sds/components`. See the Data Viz overview for installation and peer dependencies.
|
|
6
|
+
|
|
7
|
+
## Source Code
|
|
8
|
+
|
|
9
|
+
The component's source code in the SDS codebase can be found [here](https://github.com/chanzuckerberg/sci-components/blob/main/packages/data-viz/src/core/StackedBarChart/index.tsx).
|
|
10
|
+
|
|
11
|
+
## Import
|
|
12
|
+
|
|
13
|
+
**React TypeScript**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { StackedBarChart } from "@czi-sds/data-viz";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Code examples
|
|
20
|
+
|
|
21
|
+
### **Default StackedBarChart**
|
|
22
|
+
|
|
23
|
+
A proportional bar: values become shares of the whole, the legend reads them as percentages, and the badge counts the categories.
|
|
24
|
+
|
|
25
|
+
**Example: DefaultStackedBarChart**
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// Data and a width are all the chart needs. In the default proportional mode
|
|
29
|
+
// the values become shares of their own sum, so the segments always fill the
|
|
30
|
+
// bar, and the legend reads them back as rounded percentages.
|
|
31
|
+
//
|
|
32
|
+
// The title is what makes room for the badge beside it, which counts the
|
|
33
|
+
// categories on its own until something is selected.
|
|
34
|
+
|
|
35
|
+
import { StackedBarChart } from "@czi-sds/data-viz";
|
|
36
|
+
|
|
37
|
+
const DATA = [
|
|
38
|
+
{ name: "Transcriptomic", value: 117 },
|
|
39
|
+
{ name: "Prosthetics", value: 130 },
|
|
40
|
+
{ name: "Epigenomics", value: 100 },
|
|
41
|
+
{ name: "Spatial Transcriptomics", value: 78 },
|
|
42
|
+
{ name: "Imaging", value: 61 },
|
|
43
|
+
{ name: "Sequencing", value: 34 },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
function App() {
|
|
47
|
+
return (
|
|
48
|
+
<div className="app">
|
|
49
|
+
<StackedBarChart data={DATA} title="Modality" width="420px" />
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default App;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Cumulative mode
|
|
58
|
+
|
|
59
|
+
The same data measured against a known maximum, so the bar reads as progress and the gap left over is drawn as Remaining. Counted values need a `unit` to mean anything.
|
|
60
|
+
|
|
61
|
+
**Example: CumulativeStackedBarChart**
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
// The same data read against a known total instead of its own sum. maxAmount
|
|
65
|
+
// is what the full bar stands for, so the segments no longer fill it and the
|
|
66
|
+
// gap becomes the grey Remaining segment, which nothing can interact with.
|
|
67
|
+
//
|
|
68
|
+
// A share of a total the reader cannot see means little, so a cumulative bar
|
|
69
|
+
// usually counts instead of showing percentages: legendValueFormat="count"
|
|
70
|
+
// with a unit to say what is being counted.
|
|
71
|
+
|
|
72
|
+
import { StackedBarChart } from "@czi-sds/data-viz";
|
|
73
|
+
|
|
74
|
+
const DATA = [
|
|
75
|
+
{ name: "Processed", value: 117 },
|
|
76
|
+
{ name: "In review", value: 130 },
|
|
77
|
+
{ name: "Queued", value: 100 },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
function App() {
|
|
81
|
+
return (
|
|
82
|
+
<div className="app">
|
|
83
|
+
<StackedBarChart
|
|
84
|
+
data={DATA}
|
|
85
|
+
legendValueFormat="count"
|
|
86
|
+
maxAmount={500}
|
|
87
|
+
mode="cumulative"
|
|
88
|
+
remainingLabel="Not yet submitted"
|
|
89
|
+
title="Datasets"
|
|
90
|
+
unit="datasets"
|
|
91
|
+
width="420px"
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default App;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Selection
|
|
101
|
+
|
|
102
|
+
One selection driving two charts, so the difference between `"dim"` and `"hide"` is visible in a single pass. Selection lives in the parent; the buttons show how to change it from outside the chart, which is also what gives the keyboard a way in.
|
|
103
|
+
|
|
104
|
+
**Example: SelectableStackedBarChart**
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
// Selection is controlled, so the chart draws whatever selectedIndices holds
|
|
108
|
+
// and asks the parent to change it through onSelectionChange. That makes it
|
|
109
|
+
// straightforward to point two charts at one selection, which is the clearest
|
|
110
|
+
// way to see what selectionBehavior does: dim keeps the shape of the whole and
|
|
111
|
+
// fades what is not selected, while hide drops those segments and lets the
|
|
112
|
+
// rest grow into the space.
|
|
113
|
+
//
|
|
114
|
+
// Clicking a segment or a legend item is pointer-only, so the buttons are not
|
|
115
|
+
// just a convenience: they are how a keyboard reaches the selection at all.
|
|
116
|
+
|
|
117
|
+
import { useState } from "react";
|
|
118
|
+
import {
|
|
119
|
+
Button,
|
|
120
|
+
fontBodyXs,
|
|
121
|
+
getSemanticColors,
|
|
122
|
+
getSpaces,
|
|
123
|
+
type CommonThemeProps,
|
|
124
|
+
} from "@czi-sds/components";
|
|
125
|
+
import { StackedBarChart } from "@czi-sds/data-viz";
|
|
126
|
+
import styled from "@emotion/styled";
|
|
127
|
+
|
|
128
|
+
const DATA = [
|
|
129
|
+
{ name: "Transcriptomic", value: 117 },
|
|
130
|
+
{ name: "Prosthetics", value: 130 },
|
|
131
|
+
{ name: "Epigenomics", value: 100 },
|
|
132
|
+
{ name: "Imaging", value: 61 },
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
const Stack = styled.div<CommonThemeProps>`
|
|
136
|
+
${(props) => {
|
|
137
|
+
const spaces = getSpaces(props);
|
|
138
|
+
|
|
139
|
+
return `
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
gap: ${spaces?.xl}px;
|
|
143
|
+
`;
|
|
144
|
+
}}
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
const Row = styled.div<CommonThemeProps>`
|
|
148
|
+
${(props) => {
|
|
149
|
+
const spaces = getSpaces(props);
|
|
150
|
+
|
|
151
|
+
return `
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-wrap: wrap;
|
|
154
|
+
gap: ${spaces?.xxl}px;
|
|
155
|
+
`;
|
|
156
|
+
}}
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
const Controls = styled.div<CommonThemeProps>`
|
|
160
|
+
${(props) => {
|
|
161
|
+
const spaces = getSpaces(props);
|
|
162
|
+
|
|
163
|
+
return `
|
|
164
|
+
display: flex;
|
|
165
|
+
gap: ${spaces?.s}px;
|
|
166
|
+
`;
|
|
167
|
+
}}
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
const Caption = styled.p<CommonThemeProps>`
|
|
171
|
+
${fontBodyXs}
|
|
172
|
+
|
|
173
|
+
${(props) => {
|
|
174
|
+
const semanticColors = getSemanticColors(props);
|
|
175
|
+
const spaces = getSpaces(props);
|
|
176
|
+
|
|
177
|
+
return `
|
|
178
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
179
|
+
margin: 0 0 ${spaces?.s}px;
|
|
180
|
+
`;
|
|
181
|
+
}}
|
|
182
|
+
`;
|
|
183
|
+
|
|
184
|
+
function App() {
|
|
185
|
+
const [selectedIndices, setSelectedIndices] = useState<number[]>([1]);
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div className="app">
|
|
189
|
+
<Stack>
|
|
190
|
+
<Controls>
|
|
191
|
+
<Button
|
|
192
|
+
onClick={() => setSelectedIndices(DATA.map((_, index) => index))}
|
|
193
|
+
sdsStyle="outline"
|
|
194
|
+
sdsType="primary"
|
|
195
|
+
>
|
|
196
|
+
Select all
|
|
197
|
+
</Button>
|
|
198
|
+
<Button
|
|
199
|
+
disabled={selectedIndices.length === 0}
|
|
200
|
+
onClick={() => setSelectedIndices([])}
|
|
201
|
+
sdsStyle="minimal"
|
|
202
|
+
sdsType="secondary"
|
|
203
|
+
>
|
|
204
|
+
Clear
|
|
205
|
+
</Button>
|
|
206
|
+
</Controls>
|
|
207
|
+
|
|
208
|
+
<Row>
|
|
209
|
+
<div>
|
|
210
|
+
<Caption>selectionBehavior="dim"</Caption>
|
|
211
|
+
<StackedBarChart
|
|
212
|
+
data={DATA}
|
|
213
|
+
onSelectionChange={setSelectedIndices}
|
|
214
|
+
selectedIndices={selectedIndices}
|
|
215
|
+
title="Modality"
|
|
216
|
+
width="300px"
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div>
|
|
221
|
+
<Caption>selectionBehavior="hide"</Caption>
|
|
222
|
+
<StackedBarChart
|
|
223
|
+
data={DATA}
|
|
224
|
+
onSelectionChange={setSelectedIndices}
|
|
225
|
+
selectedIndices={selectedIndices}
|
|
226
|
+
selectionBehavior="hide"
|
|
227
|
+
title="Modality"
|
|
228
|
+
width="300px"
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
231
|
+
</Row>
|
|
232
|
+
</Stack>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export default App;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Segment tooltips
|
|
241
|
+
|
|
242
|
+
A data item carrying `tooltip` content opens a TooltipTable on hover, which is where the detail behind a segment belongs.
|
|
243
|
+
|
|
244
|
+
**Example: StackedBarChartWithTooltips**
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
// A segment shows nothing but its size, so anything behind the number belongs
|
|
248
|
+
// in a tooltip. Giving a data item tooltip content opens a TooltipTable above
|
|
249
|
+
// its segment on hover; the shape is TooltipTable's own, a list of sections
|
|
250
|
+
// each holding dataRows of label and value.
|
|
251
|
+
//
|
|
252
|
+
// Tooltips are per item, so a category with nothing to add simply leaves the
|
|
253
|
+
// prop off, as Prosthetics does here.
|
|
254
|
+
|
|
255
|
+
import { StackedBarChart } from "@czi-sds/data-viz";
|
|
256
|
+
|
|
257
|
+
const DATA = [
|
|
258
|
+
{
|
|
259
|
+
name: "Transcriptomic",
|
|
260
|
+
tooltip: {
|
|
261
|
+
data: [
|
|
262
|
+
{
|
|
263
|
+
dataRows: [
|
|
264
|
+
{ label: "Bulk RNA-seq", value: 48 },
|
|
265
|
+
{ label: "Single cell", value: 39 },
|
|
266
|
+
{ label: "Single nucleus", value: 30 },
|
|
267
|
+
],
|
|
268
|
+
label: "Transcriptomic",
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
value: 117,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "Imaging",
|
|
276
|
+
tooltip: {
|
|
277
|
+
data: [
|
|
278
|
+
{
|
|
279
|
+
dataRows: [
|
|
280
|
+
{ label: "Fluorescence microscopy", value: 22 },
|
|
281
|
+
{ label: "Confocal imaging", value: 15 },
|
|
282
|
+
{ label: "Electron microscopy", value: 14 },
|
|
283
|
+
{ label: "Light sheet imaging", value: 10 },
|
|
284
|
+
],
|
|
285
|
+
label: "Imaging",
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
value: 61,
|
|
290
|
+
},
|
|
291
|
+
{ name: "Prosthetics", value: 130 },
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
function App() {
|
|
295
|
+
return (
|
|
296
|
+
<div className="app">
|
|
297
|
+
<StackedBarChart data={DATA} title="Modality" width="420px" />
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export default App;
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Colors
|
|
306
|
+
|
|
307
|
+
Explicit colors beside a generated palette. Give the categories that must stay recognisable a `color` of their own, and tune the rest through `colorGeneratorOptions`.
|
|
308
|
+
|
|
309
|
+
**Example: StackedBarChartColors**
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
// Segments without a color of their own take one from a generated cubehelix
|
|
313
|
+
// palette, which is regenerated whenever a new category appears. That is fine
|
|
314
|
+
// for a one-off breakdown, but it means a category is not guaranteed the same
|
|
315
|
+
// color across two charts or across a change of data.
|
|
316
|
+
//
|
|
317
|
+
// Where a category has to stay recognisable, give it a color. Where the set as
|
|
318
|
+
// a whole just needs to sit better with the page, leave the colors generated
|
|
319
|
+
// and move the palette with colorGeneratorOptions.
|
|
320
|
+
|
|
321
|
+
import {
|
|
322
|
+
fontBodyXs,
|
|
323
|
+
getSemanticColors,
|
|
324
|
+
getSpaces,
|
|
325
|
+
type CommonThemeProps,
|
|
326
|
+
} from "@czi-sds/components";
|
|
327
|
+
import { StackedBarChart } from "@czi-sds/data-viz";
|
|
328
|
+
import styled from "@emotion/styled";
|
|
329
|
+
|
|
330
|
+
const NAMED_COLORS = [
|
|
331
|
+
{ color: "#0B6CCC", name: "Passed", value: 220 },
|
|
332
|
+
{ color: "#F5C700", name: "Flagged", value: 60 },
|
|
333
|
+
{ color: "#C41E3A", name: "Failed", value: 35 },
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
const GENERATED = [
|
|
337
|
+
{ name: "Transcriptomic", value: 117 },
|
|
338
|
+
{ name: "Prosthetics", value: 130 },
|
|
339
|
+
{ name: "Epigenomics", value: 100 },
|
|
340
|
+
{ name: "Imaging", value: 61 },
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
const Row = styled.div<CommonThemeProps>`
|
|
344
|
+
${(props) => {
|
|
345
|
+
const spaces = getSpaces(props);
|
|
346
|
+
|
|
347
|
+
return `
|
|
348
|
+
display: flex;
|
|
349
|
+
flex-wrap: wrap;
|
|
350
|
+
gap: ${spaces?.xxl}px;
|
|
351
|
+
`;
|
|
352
|
+
}}
|
|
353
|
+
`;
|
|
354
|
+
|
|
355
|
+
const Caption = styled.p<CommonThemeProps>`
|
|
356
|
+
${fontBodyXs}
|
|
357
|
+
|
|
358
|
+
${(props) => {
|
|
359
|
+
const semanticColors = getSemanticColors(props);
|
|
360
|
+
const spaces = getSpaces(props);
|
|
361
|
+
|
|
362
|
+
return `
|
|
363
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
364
|
+
margin: 0 0 ${spaces?.s}px;
|
|
365
|
+
`;
|
|
366
|
+
}}
|
|
367
|
+
`;
|
|
368
|
+
|
|
369
|
+
function App() {
|
|
370
|
+
return (
|
|
371
|
+
<div className="app">
|
|
372
|
+
<Row>
|
|
373
|
+
<div>
|
|
374
|
+
<Caption>A color on each item</Caption>
|
|
375
|
+
<StackedBarChart
|
|
376
|
+
data={NAMED_COLORS}
|
|
377
|
+
title="QC status"
|
|
378
|
+
width="300px"
|
|
379
|
+
/>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<div>
|
|
383
|
+
<Caption>A generated palette, starting from green</Caption>
|
|
384
|
+
<StackedBarChart
|
|
385
|
+
colorGeneratorOptions={{ rotations: 0.5, start: 140 }}
|
|
386
|
+
data={GENERATED}
|
|
387
|
+
title="Modality"
|
|
388
|
+
width="300px"
|
|
389
|
+
/>
|
|
390
|
+
</div>
|
|
391
|
+
</Row>
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export default App;
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## How it is built
|
|
400
|
+
|
|
401
|
+
Unlike HeatmapChart, this chart does not use ECharts. It is plain DOM: the bar is a flex row of divs whose `flex-grow` carries each segment's share, so the chart inherits SDS colors and spacing directly and animates with CSS. Two things come from `@czi-sds/components`: the Legend below the bar, and the TooltipTable a segment opens when its data item carries a `tooltip`.
|
|
402
|
+
|
|
403
|
+
## Modes
|
|
404
|
+
|
|
405
|
+
`mode` decides what the full width of the bar means, and it is the first thing to settle when adding the chart.
|
|
406
|
+
|
|
407
|
+
- `"proportional"` (the default) treats the data as the whole. Segments are sized against the sum of the values and always fill the bar, so the chart reads as a breakdown: how the total splits, not how large it is.
|
|
408
|
+
|
|
409
|
+
- `"cumulative"` sizes segments against `maxAmount` instead, so the bar reads as progress towards a known total. When the values add up to less than `maxAmount`, the gap is drawn as a grey "Remaining" segment, which is labelled with `remainingLabel` and is never interactive. Leaving `maxAmount` off makes the mode behave like `"proportional"`, since the sum becomes the maximum and no gap is left.
|
|
410
|
+
|
|
411
|
+
Values are only ever shown in the legend, never on the bar, and only when `showLegendValues` is on. In proportional mode the natural format is the default `"percentage"`; a cumulative bar usually wants `legendValueFormat="count"` with a `unit`, so the legend reads "117 datasets" rather than a share of a total the reader cannot see.
|
|
412
|
+
|
|
413
|
+
## Selection
|
|
414
|
+
|
|
415
|
+
Selection is controlled: the chart renders whatever `selectedIndices` holds and never changes it itself. Clicking a segment or a legend item calls `onSelectionChange` with the indices it thinks should be selected next, and the parent decides what to do with them. Without that callback the chart is inert: clicks still reach `onSegmentClick` and `onLegendItemClick`, but nothing is ever selected.
|
|
416
|
+
|
|
417
|
+
`selectionBehavior` chooses what a selection does to the bar. `"dim"` drops everything unselected to 20% opacity, keeping the shape of the whole intact. `"hide"` removes unselected segments instead: in proportional mode the remaining ones regrow to fill the bar, and in cumulative mode they keep their size and the Remaining segment absorbs the difference.
|
|
418
|
+
|
|
419
|
+
The badge beside the title tracks all of this on its own (the total when nothing is selected, "3 of 7" during a partial selection) unless you pass `badge` to say otherwise or `hideBadge` to remove it. It is drawn only when there is a `title` to sit next to.
|
|
420
|
+
|
|
421
|
+
## Behavior and accessibility
|
|
422
|
+
|
|
423
|
+
- Legend items are the accessible way into the chart. Each is a `role="button"` with a tab stop and an accessible name built from its label and value, and it reports its selected state through `aria-pressed`. They do not respond to Enter or Space, though, so keyboard users can reach the legend but cannot select from it. Where selection matters, give the chart a keyboard path of its own, as the example below does with a button.
|
|
424
|
+
|
|
425
|
+
- Bar segments are pointer-only: they carry no role and take no focus, so everything they offer (hover, click, tooltip) has to be reachable somewhere else too. They are also thin by default (16px), which makes them a small target.
|
|
426
|
+
|
|
427
|
+
- Nothing about a segment is conveyed by anything except its color, so the legend is what makes the chart readable. Turning `showLegend` off leaves a bar that cannot be interpreted at all unless the surrounding page names the parts.
|
|
428
|
+
|
|
429
|
+
- Colors are generated when data items do not carry their own, using the cubehelix palette from `generateDiscreteColors` and adapting to the light and dark themes. The palette is regenerated whenever a new category appears, so a category's color is stable while the data holds still but not across datasets. Pass `color` on each item where a category must always look the same, or tune the generated set with `colorGeneratorOptions`.
|
|
430
|
+
|
|
431
|
+
- Segments are keyed by `name`, which is what lets them animate in, out, and resize when the data changes. Two items sharing a name confuse that tracking, so names must be unique within a chart.
|
|
432
|
+
|
|
433
|
+
- A data item may set `disabled` to opt out of every interaction, on both its segment and its legend item. Passing an empty `data` array is safe: the bar is drawn as an empty grey track.
|
|
434
|
+
|
|
435
|
+
## Props
|
|
436
|
+
|
|
437
|
+
The chart spreads any remaining props onto its root div, so standard HTML attributes such as `className`, `id`, and `data-testid` work as usual.
|
|
438
|
+
|
|
439
|
+
| Name | Type | Default | Description |
|
|
440
|
+
| ------------------------ | ---------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
441
|
+
| `data` | `StackedBarChartDataItem[]` | - (required) | The segments, in the order they are drawn. See the table below for the shape of an item. |
|
|
442
|
+
| `mode` | `"proportional"` \| `"cumulative"` | `"proportional"` | Whether the bar shows a breakdown of the data (segments always fill it) or progress towards `maxAmount`. |
|
|
443
|
+
| `maxAmount` | `number` | sum of values | Cumulative mode only. The value the full bar represents. Any difference between it and the sum of the data is drawn as the Remaining segment. |
|
|
444
|
+
| `title` | `string` | - | A heading above the bar. It is also what makes room for the badge; with no title, no badge is drawn. |
|
|
445
|
+
| `badge` | `string` | the selection count | Overrides the badge text, which otherwise counts the data and the selection ("7", or "3 of 7"). |
|
|
446
|
+
| `hideBadge` | `boolean` | `false` | Removes the badge, leaving the title on its own. |
|
|
447
|
+
| `width` | `number` \| `string` | `"100%"` | Any CSS width; a number is read as pixels. It sets the width of the whole chart, legend included. |
|
|
448
|
+
| `barHeight` | `number` | `16` | Height of the bar in pixels. Values below 1 are clamped to 1, so the bar cannot disappear. |
|
|
449
|
+
| `showLegend` | `boolean` | `true` | Draws the legend below the bar. It is the only place segment names appear, and the only part of the chart a keyboard can reach. |
|
|
450
|
+
| `showLegendValues` | `boolean` | `true` | Shows each item's value beside its name in the legend. |
|
|
451
|
+
| `legendValueFormat` | `"percentage"` \| `"count"` | `"percentage"` | `"percentage"` shows the segment's share of the bar, rounded. `"count"` shows the raw value followed by the item's `unit`, or the chart's `unit` if the item has none. |
|
|
452
|
+
| `unit` | `string` | - | The unit appended to counted values, for items that do not carry one of their own. |
|
|
453
|
+
| `remainingLabel` | `string` | `"Remaining"` | Name of the grey gap segment in cumulative mode. |
|
|
454
|
+
| `remainingUnit` | `string` | the chart's unit | A unit for the Remaining segment's value alone. |
|
|
455
|
+
| `selectedIndices` | `number[]` | `[]` | Indices into `data` that are currently selected. The chart is controlled: it draws this and never changes it. |
|
|
456
|
+
| `onSelectionChange` | `function` | - | `(selectedIndices: number[], selectedData: StackedBarChartDataItem[]) => void`. Called with the next selection when a segment or legend item is clicked. Without it, clicking selects nothing. |
|
|
457
|
+
| `selectionBehavior` | `"dim"` \| `"hide"` | `"dim"` | What a selection does to the unselected segments: fade them to 20% opacity, or drop them from the bar and let the rest take the space. |
|
|
458
|
+
| `colorGeneratorOptions` | `DiscreteColorGeneratorOptions` | - | Tunes the generated palette (start hue, rotations, lightness range, gamma). Items with their own `color` are untouched by it. |
|
|
459
|
+
| `onSegmentClick` | `function` | - | `(item: StackedBarChartDataItem, index: number) => void`. Fires on a segment click, alongside any selection change rather than instead of it. |
|
|
460
|
+
| `onLegendItemClick` | `function` | - | `(item: StackedBarChartDataItem, index: number) => void`. The same, for a click on a legend item. |
|
|
461
|
+
| `onSegmentMouseEnter` | `function` | - | `(item: StackedBarChartDataItem, index: number) => void`. Called when the pointer enters a segment. The chart already syncs its own hover highlight between bar and legend, so this is for driving something outside it. |
|
|
462
|
+
| `onSegmentMouseLeave` | `function` | - | `(item: StackedBarChartDataItem, index: number) => void`. Called when the pointer leaves a segment. |
|
|
463
|
+
| `onLegendItemMouseEnter` | `function` | - | `(item: StackedBarChartDataItem, index: number) => void`. Called when the pointer enters a legend item. |
|
|
464
|
+
| `onLegendItemMouseLeave` | `function` | - | `(item: StackedBarChartDataItem, index: number) => void`. Called when the pointer leaves a legend item. |
|
|
465
|
+
|
|
466
|
+
### StackedBarChartDataItem
|
|
467
|
+
|
|
468
|
+
| Name | Type | Default | Description |
|
|
469
|
+
| ---------- | -------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
470
|
+
| `name` | `string` | - (required) | The segment's label in the legend, and the key the animations track it by. Must be unique within the chart. |
|
|
471
|
+
| `value` | `number` | - (required) | The quantity the segment stands for. |
|
|
472
|
+
| `color` | `string` | generated | Any CSS color. Without it the segment takes a generated palette color, which can shift as categories come and go. |
|
|
473
|
+
| `unit` | `string` | the chart's unit | A unit for this item's counted value, for data that mixes units across categories. |
|
|
474
|
+
| `disabled` | `boolean` | `false` | Takes the segment and its legend item out of every interaction: no hover, no tooltip, no selection. |
|
|
475
|
+
| `tooltip` | `TooltipTableContentProps` | - | Content for a TooltipTable shown above the segment on hover. Use it for the breakdown behind the number; a segment without it has no tooltip. |
|