@czi-sds/mcp 0.15.0 → 0.17.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.
Files changed (164) hide show
  1. package/README.md +2 -1
  2. package/data/component-docs/Accordion.md +291 -0
  3. package/data/component-docs/Autocomplete.md +525 -0
  4. package/data/component-docs/Banner.md +231 -0
  5. package/data/component-docs/Button.md +224 -0
  6. package/data/component-docs/ButtonDropdown.md +256 -0
  7. package/data/component-docs/ButtonGroup.md +320 -0
  8. package/data/component-docs/ButtonToggle.md +397 -0
  9. package/data/component-docs/Callout.md +268 -0
  10. package/data/component-docs/CellBasic.md +122 -0
  11. package/data/component-docs/CellComponent.md +63 -0
  12. package/data/component-docs/CellHeader.md +128 -0
  13. package/data/component-docs/ComplexFilter.md +294 -0
  14. package/data/component-docs/ContentCard.md +406 -0
  15. package/data/component-docs/Dialog.md +651 -0
  16. package/data/component-docs/Dropdown.md +155 -0
  17. package/data/component-docs/DropdownMenu.md +949 -0
  18. package/data/component-docs/HeatmapChart.md +782 -0
  19. package/data/component-docs/Hero.md +363 -0
  20. package/data/component-docs/Icon.md +283 -0
  21. package/data/component-docs/InputCheckbox.md +205 -0
  22. package/data/component-docs/InputDropdown.md +482 -0
  23. package/data/component-docs/InputRadio.md +173 -0
  24. package/data/component-docs/InputSearch.md +81 -0
  25. package/data/component-docs/InputSlider.md +189 -0
  26. package/data/component-docs/InputText.md +110 -0
  27. package/data/component-docs/InputToggle.md +141 -0
  28. package/data/component-docs/IntentMessage.md +293 -0
  29. package/data/component-docs/Legend.md +442 -0
  30. package/data/component-docs/Link.md +23 -0
  31. package/data/component-docs/List.md +300 -0
  32. package/data/component-docs/LoadingIndicator.md +198 -0
  33. package/data/component-docs/Menu.md +298 -0
  34. package/data/component-docs/MenuItem.md +100 -0
  35. package/data/component-docs/NavigationFooter.md +165 -0
  36. package/data/component-docs/NavigationHeader.md +500 -0
  37. package/data/component-docs/NavigationJumpTo.md +414 -0
  38. package/data/component-docs/Notification.md +288 -0
  39. package/data/component-docs/Pagination.md +67 -0
  40. package/data/component-docs/Panel.md +636 -0
  41. package/data/component-docs/Popover.md +448 -0
  42. package/data/component-docs/PreComposedTable.md +2947 -0
  43. package/data/component-docs/SegmentedControl.md +396 -0
  44. package/data/component-docs/StackedBarChart.md +475 -0
  45. package/data/component-docs/Table.md +2000 -0
  46. package/data/component-docs/TableHeader.md +53 -0
  47. package/data/component-docs/TableRow.md +74 -0
  48. package/data/component-docs/Tabs.md +455 -0
  49. package/data/component-docs/Tag.md +647 -0
  50. package/data/component-docs/TagFilter.md +131 -0
  51. package/data/component-docs/Tooltip.md +561 -0
  52. package/data/component-docs/TooltipCondensed.md +203 -0
  53. package/data/component-docs/TooltipTable.md +145 -0
  54. package/data/component-docs/index.json +262 -0
  55. package/data/component-list.json +2 -5
  56. package/data/component-props/Accordion.json +31 -11
  57. package/data/component-props/Alert.json +3 -3
  58. package/data/component-props/Autocomplete.json +150 -15
  59. package/data/component-props/Banner.json +31 -17
  60. package/data/component-props/Button.json +45 -20
  61. package/data/component-props/ButtonDropdown.json +36 -17
  62. package/data/component-props/ButtonDropdownLegacy.json +11 -11
  63. package/data/component-props/ButtonGroup.json +32 -11
  64. package/data/component-props/ButtonIcon.json +10 -10
  65. package/data/component-props/ButtonLegacy.json +19 -19
  66. package/data/component-props/ButtonToggle.json +32 -19
  67. package/data/component-props/ButtonToggleLegacy.json +11 -11
  68. package/data/component-props/Callout.json +58 -17
  69. package/data/component-props/CellBasic.json +94 -41
  70. package/data/component-props/CellComponent.json +23 -7
  71. package/data/component-props/CellHeader.json +62 -19
  72. package/data/component-props/ComplexFilter.json +52 -21
  73. package/data/component-props/ContentCard.json +70 -47
  74. package/data/component-props/Dialog.json +47 -14
  75. package/data/component-props/Dropdown.json +67 -31
  76. package/data/component-props/DropdownMenu.json +128 -23
  77. package/data/component-props/HeatmapChart.json +88 -12
  78. package/data/component-props/Hero.json +80 -39
  79. package/data/component-props/Icon.json +20 -9
  80. package/data/component-props/InputCheckbox.json +36 -14
  81. package/data/component-props/InputDropdown.json +75 -27
  82. package/data/component-props/InputRadio.json +31 -13
  83. package/data/component-props/InputSearch.json +41 -18
  84. package/data/component-props/InputSlider.json +46 -19
  85. package/data/component-props/InputText.json +26 -19
  86. package/data/component-props/InputToggle.json +26 -9
  87. package/data/component-props/IntentMessage.json +38 -9
  88. package/data/component-props/Link.json +13 -7
  89. package/data/component-props/LoadingIndicator.json +11 -3
  90. package/data/component-props/MenuItem.json +38 -19
  91. package/data/component-props/MenuSelect.json +8 -8
  92. package/data/component-props/NavigationFooter.json +49 -19
  93. package/data/component-props/NavigationHeader.json +150 -37
  94. package/data/component-props/NavigationJumpTo.json +33 -7
  95. package/data/component-props/Notification.json +39 -21
  96. package/data/component-props/Pagination.json +39 -11
  97. package/data/component-props/Panel.json +62 -9
  98. package/data/component-props/Popover.json +80 -7
  99. package/data/component-props/PreComposedTable.json +99 -27
  100. package/data/component-props/SegmentedControl.json +42 -3
  101. package/data/component-props/StackedBarChart.json +116 -42
  102. package/data/component-props/TableRow.json +38 -19
  103. package/data/component-props/Tabs.json +66 -9
  104. package/data/component-props/Tag.json +34 -17
  105. package/data/component-props/TagFilter.json +10 -3
  106. package/data/component-props/Tooltip.json +49 -18
  107. package/data/component-props/TooltipCondensed.json +18 -9
  108. package/data/component-props/TooltipTable.json +17 -7
  109. package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
  110. package/data/resources/figma-rules.md +2 -2
  111. package/dist/stdio.js +483 -780
  112. package/package.json +12 -8
  113. package/data/component-props/ButtonDropdownV2.json +0 -34
  114. package/data/component-props/ButtonNew.json +0 -47
  115. package/data/component-props/ButtonToggleV2.json +0 -44
  116. package/data/component-props/ButtonV2.json +0 -43
  117. package/data/zeroheight-cache.json +0 -10259
  118. package/data/zeroheight-exports/Accordion.md +0 -774
  119. package/data/zeroheight-exports/Banner.md +0 -691
  120. package/data/zeroheight-exports/Borders.md +0 -117
  121. package/data/zeroheight-exports/Buttons.md +0 -1854
  122. package/data/zeroheight-exports/Callout.md +0 -931
  123. package/data/zeroheight-exports/Colors.md +0 -1755
  124. package/data/zeroheight-exports/Content-Card.md +0 -309
  125. package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
  126. package/data/zeroheight-exports/Control-Inputs.md +0 -2818
  127. package/data/zeroheight-exports/Corners.md +0 -104
  128. package/data/zeroheight-exports/Cover-page.md +0 -3
  129. package/data/zeroheight-exports/Dialog.md +0 -1367
  130. package/data/zeroheight-exports/Downloads.md +0 -236
  131. package/data/zeroheight-exports/Drop-Shadows.md +0 -79
  132. package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
  133. package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
  134. package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
  135. package/data/zeroheight-exports/Field-Inputs.md +0 -1405
  136. package/data/zeroheight-exports/Filters.md +0 -1111
  137. package/data/zeroheight-exports/Getting-Started.md +0 -1157
  138. package/data/zeroheight-exports/Hero.md +0 -414
  139. package/data/zeroheight-exports/Icons.md +0 -2067
  140. package/data/zeroheight-exports/Intent.md +0 -86
  141. package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
  142. package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
  143. package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
  144. package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
  145. package/data/zeroheight-exports/Introduction.md +0 -115
  146. package/data/zeroheight-exports/Link.md +0 -432
  147. package/data/zeroheight-exports/Lists.md +0 -986
  148. package/data/zeroheight-exports/Loading-Indicator.md +0 -41
  149. package/data/zeroheight-exports/Navigation.md +0 -1190
  150. package/data/zeroheight-exports/Notification.md +0 -1038
  151. package/data/zeroheight-exports/Panel.md +0 -1030
  152. package/data/zeroheight-exports/README.md +0 -122
  153. package/data/zeroheight-exports/Responsive-Design.md +0 -330
  154. package/data/zeroheight-exports/Search-Input.md +0 -91
  155. package/data/zeroheight-exports/Search.md +0 -822
  156. package/data/zeroheight-exports/Segmented-Control.md +0 -609
  157. package/data/zeroheight-exports/Spacing.md +0 -297
  158. package/data/zeroheight-exports/Table.md +0 -5345
  159. package/data/zeroheight-exports/Tabs.md +0 -1324
  160. package/data/zeroheight-exports/Tags.md +0 -1545
  161. package/data/zeroheight-exports/Text-Link.md +0 -268
  162. package/data/zeroheight-exports/Theming.md +0 -362
  163. package/data/zeroheight-exports/Tooltips.md +0 -2015
  164. package/data/zeroheight-exports/Typography.md +0 -800
@@ -0,0 +1,442 @@
1
+ # Legend
2
+
3
+ A wrapping row of colored swatches and labels, for naming the parts of a chart and, optionally, filtering it.
4
+
5
+ ## Source Code
6
+
7
+ The component's source code in the SDS codebase can be found [here](https://github.com/chanzuckerberg/sci-components/tree/main/packages/components/src/core/Legend).
8
+
9
+ ## Import
10
+
11
+ **React TypeScript**
12
+
13
+ ```tsx
14
+ import { Legend } from "@czi-sds/components";
15
+ ```
16
+
17
+ ## Code examples
18
+
19
+ ### **Default Legend**
20
+
21
+ The minimum: a list of names, each with a color. Nothing here is interactive, which is how the legend reads when it is only a key.
22
+
23
+ **Example: DefaultLegend**
24
+
25
+ ```tsx
26
+ // A legend on its own is a key: a swatch and a name for each part of whatever it
27
+ // sits next to. Items are drawn in the order they are given, and the row wraps
28
+ // to as many lines as the container needs, so the container is what decides how
29
+ // wide the legend is.
30
+ //
31
+ // Nothing here is interactive. Items are still buttons for the sake of the
32
+ // legends that are, so a click lands on one and does nothing.
33
+
34
+ import { Legend } from "@czi-sds/components";
35
+
36
+ const ITEMS = [
37
+ { color: "#0B6CCC", name: "H. sapiens" },
38
+ { color: "#3E8F3E", name: "M. musculus" },
39
+ { color: "#9A54C1", name: "D. rerio" },
40
+ { color: "#C9721A", name: "M. mulatta" },
41
+ ];
42
+
43
+ function App() {
44
+ return (
45
+ <div className="app">
46
+ <div style={{ maxWidth: "400px" }}>
47
+ <Legend items={ITEMS} />
48
+ </div>
49
+ </div>
50
+ );
51
+ }
52
+
53
+ export default App;
54
+ ```
55
+
56
+ ### Values and colors
57
+
58
+ Values turned on, and the two ways to color the swatches: a palette passed as `colors`, and a color on each item. The last item is disabled, which is how a catch-all category sits in the legend without behaving like the rest.
59
+
60
+ **Example: LegendValuesAndColors**
61
+
62
+ ```tsx
63
+ // Two ways to color a legend, and what showValues does to it.
64
+ //
65
+ // The colors prop is a palette for the set as a whole, applied by index. It is
66
+ // what generateDiscreteColors produces, and it has to be regenerated when the
67
+ // theme changes, since the generator reverses its ramp for dark mode. A color on
68
+ // the item itself is the opposite case: a category that has to look the same
69
+ // everywhere it appears.
70
+ //
71
+ // Values are formatted for you when they are numbers, and printed as given when
72
+ // they are strings, which is how a percentage or a unit gets in. The last item
73
+ // on the right is disabled: it is drawn like the others but takes no hover, no
74
+ // click, and does not dim when its neighbours are selected, which is what a
75
+ // catch-all category usually wants.
76
+
77
+ import {
78
+ Legend,
79
+ fontBodyXs,
80
+ generateDiscreteColors,
81
+ getMode,
82
+ getSemanticColors,
83
+ getSpaces,
84
+ type CommonThemeProps,
85
+ type SDSTheme,
86
+ } from "@czi-sds/components";
87
+ import styled from "@emotion/styled";
88
+ import { useTheme } from "@mui/material";
89
+
90
+ const COUNTS = [
91
+ { name: "H. sapiens", value: 3212 },
92
+ { name: "M. musculus", value: 130 },
93
+ { name: "C. jacchus", value: 89 },
94
+ { name: "D. rerio", value: 65 },
95
+ { name: "M. mulatta", value: 45 },
96
+ ];
97
+
98
+ const SHARES = [
99
+ { color: "#0B6CCC", name: "Passed", value: "68%" },
100
+ { color: "#F5C700", name: "Flagged", value: "19%" },
101
+ { color: "#C41E3A", name: "Failed", value: "8%" },
102
+ { disabled: true, name: "Not run", value: "5%" },
103
+ ];
104
+
105
+ const Row = styled.div<CommonThemeProps>`
106
+ ${(props) => {
107
+ const spaces = getSpaces(props);
108
+
109
+ return `
110
+ display: flex;
111
+ flex-wrap: wrap;
112
+ gap: ${spaces?.xxl}px;
113
+ `;
114
+ }}
115
+ `;
116
+
117
+ const Caption = styled.p<CommonThemeProps>`
118
+ ${fontBodyXs}
119
+
120
+ ${(props) => {
121
+ const semanticColors = getSemanticColors(props);
122
+ const spaces = getSpaces(props);
123
+
124
+ return `
125
+ color: ${semanticColors?.base?.textSecondary};
126
+ margin: 0 0 ${spaces?.s}px;
127
+ `;
128
+ }}
129
+ `;
130
+
131
+ function App() {
132
+ const theme = useTheme() as SDSTheme;
133
+ const colors = generateDiscreteColors(COUNTS.length, {
134
+ isDarkMode: getMode({ theme }) === "dark",
135
+ });
136
+
137
+ return (
138
+ <div className="app">
139
+ <Row>
140
+ <div style={{ maxWidth: "320px" }}>
141
+ <Caption>A generated palette, with counts</Caption>
142
+ <Legend colors={colors} items={COUNTS} showValues />
143
+ </div>
144
+
145
+ <div style={{ maxWidth: "320px" }}>
146
+ <Caption>A color on each item, with shares</Caption>
147
+ <Legend items={SHARES} showValues />
148
+ </div>
149
+ </Row>
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export default App;
155
+ ```
156
+
157
+ ### Selection
158
+
159
+ Selection lives in the parent, so the same state can filter a list beside the legend. The buttons are not just a convenience: clicking an item is pointer-only, so they are how a keyboard reaches the selection at all.
160
+
161
+ **Example: SelectableLegend**
162
+
163
+ ```tsx
164
+ // Selection is controlled: the legend draws whatever selectedIndices holds and
165
+ // asks the parent to change it through onSelectionChange, which is called with
166
+ // the clicked index toggled in or out. Because the state lives in the parent,
167
+ // the same selection can drive something else on the page. Here, that is the
168
+ // list of datasets beside it.
169
+ //
170
+ // Selected items keep a filled background and their swatch at full opacity,
171
+ // while everything unselected fades. Hovering wins over that for as long as the
172
+ // pointer is on the row.
173
+ //
174
+ // Clicking an item is pointer-only: items are buttons and take focus, but they
175
+ // do not respond to Enter or Space. The buttons below are what let a keyboard
176
+ // change the selection at all.
177
+
178
+ import { useState } from "react";
179
+ import {
180
+ Button,
181
+ Legend,
182
+ fontBodyXs,
183
+ getSemanticColors,
184
+ getSpaces,
185
+ type CommonThemeProps,
186
+ } from "@czi-sds/components";
187
+ import styled from "@emotion/styled";
188
+
189
+ const ITEMS = [
190
+ { color: "#0B6CCC", name: "Transcriptomic", value: 117 },
191
+ { color: "#3E8F3E", name: "Prosthetics", value: 130 },
192
+ { color: "#9A54C1", name: "Epigenomics", value: 100 },
193
+ { color: "#C9721A", name: "Imaging", value: 61 },
194
+ ];
195
+
196
+ const Stack = styled.div<CommonThemeProps>`
197
+ ${(props) => {
198
+ const spaces = getSpaces(props);
199
+
200
+ return `
201
+ display: flex;
202
+ flex-direction: column;
203
+ gap: ${spaces?.l}px;
204
+ max-width: 420px;
205
+ `;
206
+ }}
207
+ `;
208
+
209
+ const Controls = styled.div<CommonThemeProps>`
210
+ ${(props) => {
211
+ const spaces = getSpaces(props);
212
+
213
+ return `
214
+ display: flex;
215
+ gap: ${spaces?.s}px;
216
+ `;
217
+ }}
218
+ `;
219
+
220
+ const Caption = styled.p<CommonThemeProps>`
221
+ ${fontBodyXs}
222
+
223
+ ${(props) => {
224
+ const semanticColors = getSemanticColors(props);
225
+
226
+ return `
227
+ color: ${semanticColors?.base?.textSecondary};
228
+ margin: 0;
229
+ `;
230
+ }}
231
+ `;
232
+
233
+ function App() {
234
+ const [selectedIndices, setSelectedIndices] = useState<number[]>([1]);
235
+
236
+ const summary = selectedIndices.length
237
+ ? selectedIndices.map((index) => ITEMS[index].name).join(", ")
238
+ : "Nothing selected: showing all modalities.";
239
+
240
+ return (
241
+ <div className="app">
242
+ <Stack>
243
+ <Legend
244
+ items={ITEMS}
245
+ onSelectionChange={setSelectedIndices}
246
+ selectedIndices={selectedIndices}
247
+ showValues
248
+ />
249
+
250
+ <Controls>
251
+ <Button
252
+ onClick={() => setSelectedIndices(ITEMS.map((_, index) => index))}
253
+ sdsStyle="outline"
254
+ sdsType="primary"
255
+ >
256
+ Select all
257
+ </Button>
258
+ <Button
259
+ disabled={selectedIndices.length === 0}
260
+ onClick={() => setSelectedIndices([])}
261
+ sdsStyle="minimal"
262
+ sdsType="secondary"
263
+ >
264
+ Clear
265
+ </Button>
266
+ </Controls>
267
+
268
+ <Caption>{summary}</Caption>
269
+ </Stack>
270
+ </div>
271
+ );
272
+ }
273
+
274
+ export default App;
275
+ ```
276
+
277
+ ### Hover synced with a chart
278
+
279
+ `hoveredIndex` and the hover callbacks wired in both directions, so pointing at a bar highlights its legend item and pointing at a legend item highlights the bar.
280
+
281
+ **Example: LegendWithChartHover**
282
+
283
+ ```tsx
284
+ // The legend describes a chart it knows nothing about, so keeping the two in
285
+ // step is the parent's job, and it takes both halves of the hover API.
286
+ //
287
+ // Pointing at a bar sets hoveredIndex, which highlights the matching legend
288
+ // item. Pointing at a legend item fires onItemMouseEnter, which the parent turns
289
+ // into a highlight on the bar. hoveredIndex only ever adds a highlight (the
290
+ // legend still tracks its own hover, and null means "nothing from outside"
291
+ // rather than "clear it"), so the two never fight over the same item.
292
+ //
293
+ // The bar here is four divs sized by flex-grow, deliberately plain: it stands in
294
+ // for whatever chart the legend is a key to. For a real stacked bar, reach for
295
+ // StackedBarChart from @czi-sds/data-viz, which renders this legend itself.
296
+
297
+ import { useState } from "react";
298
+ import {
299
+ Legend,
300
+ getCorners,
301
+ getSpaces,
302
+ type CommonThemeProps,
303
+ } from "@czi-sds/components";
304
+ import styled from "@emotion/styled";
305
+
306
+ const ITEMS = [
307
+ { color: "#0B6CCC", name: "Transcriptomic", value: 117 },
308
+ { color: "#3E8F3E", name: "Prosthetics", value: 130 },
309
+ { color: "#9A54C1", name: "Epigenomics", value: 100 },
310
+ { color: "#C9721A", name: "Imaging", value: 61 },
311
+ ];
312
+
313
+ const TOTAL = ITEMS.reduce((sum, item) => sum + item.value, 0);
314
+
315
+ const Stack = styled.div<CommonThemeProps>`
316
+ ${(props) => {
317
+ const spaces = getSpaces(props);
318
+
319
+ return `
320
+ display: flex;
321
+ flex-direction: column;
322
+ gap: ${spaces?.m}px;
323
+ max-width: 420px;
324
+ `;
325
+ }}
326
+ `;
327
+
328
+ const Bar = styled.div<CommonThemeProps>`
329
+ ${(props) => {
330
+ const corners = getCorners(props);
331
+ const spaces = getSpaces(props);
332
+
333
+ return `
334
+ display: flex;
335
+ gap: ${spaces?.xxxs}px;
336
+ height: 16px;
337
+ overflow: hidden;
338
+ border-radius: ${corners?.s}px;
339
+ `;
340
+ }}
341
+ `;
342
+
343
+ const Segment = styled.div`
344
+ cursor: pointer;
345
+ transition: opacity 0.2s ease-in-out;
346
+ `;
347
+
348
+ function App() {
349
+ const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
350
+
351
+ return (
352
+ <div className="app">
353
+ <Stack>
354
+ <Bar>
355
+ {ITEMS.map((item, index) => (
356
+ <Segment
357
+ key={item.name}
358
+ onMouseEnter={() => setHoveredIndex(index)}
359
+ onMouseLeave={() => setHoveredIndex(null)}
360
+ style={{
361
+ backgroundColor: item.color,
362
+ flexGrow: item.value / TOTAL,
363
+ opacity:
364
+ hoveredIndex === null || hoveredIndex === index ? 1 : 0.2,
365
+ }}
366
+ />
367
+ ))}
368
+ </Bar>
369
+
370
+ <Legend
371
+ hoveredIndex={hoveredIndex}
372
+ items={ITEMS}
373
+ onItemMouseEnter={(_, index) => setHoveredIndex(index)}
374
+ onItemMouseLeave={() => setHoveredIndex(null)}
375
+ showValues
376
+ />
377
+ </Stack>
378
+ </div>
379
+ );
380
+ }
381
+
382
+ export default App;
383
+ ```
384
+
385
+ ## How it is built
386
+
387
+ Legend has no MUI base. It is a flex row of divs that wraps, where each item is a swatch, a label, and an optional value, styled from SDS spacing and semantic colors. It draws nothing but itself: it holds no chart, and it is up to the page to keep the legend and whatever it describes in the same order and the same colors.
388
+
389
+ It is also what StackedBarChart renders below its bar. Reach for Legend directly when the chart is your own (an ECharts plot, a map, a set of swatches in a table) and you want its key to match the rest of the system.
390
+
391
+ ## Items and colors
392
+
393
+ `items` is the whole content of the legend, drawn in the order given. Each item needs a `name` and may carry a `value`, a `color`, and `disabled`. An item's index is its identity everywhere else in the API: selection, hover, and every callback are indices into this array. Items are therefore positional and names need not be unique.
394
+
395
+ A swatch takes the first color it finds: the entry at its index in the `colors` prop, then the item's own `color`, then a grey fallback from the theme. The `colors` prop is for a palette computed for the whole set at once, which is what `generateDiscreteColors` produces and what needs regenerating when the theme changes; a color on the item is for a category that must always look the same. A short colors array is fine, since each index falls through on its own.
396
+
397
+ Values are only drawn when `showValues` is on and the item has one. Numbers go through `toLocaleString`, so 3212 reads as "3,212"; strings are printed as given, which is the way to show a percentage, a unit, or a range.
398
+
399
+ ## Selection and hover
400
+
401
+ Selection is controlled. The legend draws whatever `selectedIndices` holds and never changes it: clicking an item calls `onSelectionChange` with that index toggled in or out, and the parent decides what to do with it. Without the callback the legend is inert: clicks still reach `onItemClick`, but nothing is ever selected.
402
+
403
+ Once anything is selected, the swatches of unselected items drop to 20% opacity. Hovering overrides that while the pointer is down the row: the hovered item and the selected ones stay solid, everything else dims. Labels never dim, so the legend stays readable as a list whatever is highlighted.
404
+
405
+ `hoveredIndex` lets a chart highlight a legend item as the pointer moves over the matching mark, and `onItemMouseEnter` / `onItemMouseLeave` drive the same thing in the other direction. It only ever adds a highlight: the legend keeps its own hover state, and passing `null` is the same as not passing the prop at all rather than a way to clear one.
406
+
407
+ ## Behavior and accessibility
408
+
409
+ - Each item 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`. It does not respond to Enter or Space, though, so a keyboard reaches the legend but cannot select from it. Where selection matters, give the page a keyboard path of its own, as the selection example below does with buttons.
410
+
411
+ - Every item is a button whether or not anything listens, so a legend used purely as a key still invites a click that does nothing. That is worth weighing against the alternative of drawing the key by hand.
412
+
413
+ - Color is the only thing tying an item to the mark it names. Where the marks are close in hue, or the chart is read by someone who cannot separate them, the values in the legend are what make it interpretable, which is a reason to turn `showValues` on rather than leave it to the chart.
414
+
415
+ - A disabled item takes no pointer events at all: no hover, no click, no dimming when its neighbours are selected. It is drawn exactly like the others, so anything a reader should know about it has to be in its name: "Other (17)", "Remaining". It also keeps its tab stop out of the tab order.
416
+
417
+ - The legend wraps to as many rows as it needs and has no width of its own, so it takes the width of its container. Constrain that container to keep it the same width as the chart it belongs to.
418
+
419
+ ## Props
420
+
421
+ The legend spreads any remaining props onto its root div, so standard HTML attributes such as `className`, `id`, and `data-testid` work as usual.
422
+
423
+ | Name | Type | Default | Description |
424
+ | ------------------- | ------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
425
+ | `items` | `LegendItemData[]` | - (required) | The items, drawn in order. Their indices are what selection, hover, and the callbacks refer to. See the table below for the shape of an item. |
426
+ | `colors` | `string[]` | - | A palette applied by index, taking priority over the color on an item. Entries past the end of the array fall back to the item's color, then to grey. |
427
+ | `showValues` | `boolean` | `false` | Draws each item's value beside its name. Items without a value are unaffected. |
428
+ | `selectedIndices` | `number[]` | `[]` | Indices of the selected items. The legend is controlled: it draws this and never changes it. |
429
+ | `onSelectionChange` | `function` | - | `(selectedIndices: number[]) => void`. Called with the next selection when an item is clicked, with that index toggled. Without it, clicking selects nothing. |
430
+ | `hoveredIndex` | `number \| null` | - | Highlights an item from outside, for syncing with a chart. It adds to the legend's own hover rather than replacing it, and `null` reads as no external highlight. |
431
+ | `onItemClick` | `function` | - | `(item: LegendItemData, index: number) => void`. Fires on a click, alongside any selection change rather than instead of it. |
432
+ | `onItemMouseEnter` | `function` | - | `(item: LegendItemData, index: number) => void`. Called when the pointer enters an item. The legend already dims its own swatches, so this is for driving something outside it, such as a highlight on the chart. |
433
+ | `onItemMouseLeave` | `function` | - | `(item: LegendItemData, index: number) => void`. Called when the pointer leaves an item. |
434
+
435
+ ### LegendItemData
436
+
437
+ | Name | Type | Default | Description |
438
+ | ---------- | ------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
439
+ | `name` | `string` | - (required) | The item's label, and the start of its accessible name. Names need not be unique, since items are addressed by index. |
440
+ | `value` | `number \| string` | - | Shown after the name when `showValues` is on. Numbers are grouped by locale; a string is printed as given, for a unit or a percentage. |
441
+ | `color` | `string` | theme grey | Any CSS color for the swatch. The `colors` prop overrides it, and without either the swatch is drawn in the theme's secondary ornament grey. |
442
+ | `disabled` | `boolean` | `false` | Takes the item out of every interaction and out of the tab order, and keeps its swatch at full opacity while others dim. |
@@ -0,0 +1,23 @@
1
+ # Deprecated: the Link component
2
+
3
+ > **Do not use this in new code.** The **Link** component still ships, and its story sits under **Deprecated** in this Storybook. It is documented here only so that existing usages can be migrated.
4
+
5
+ It wrapped MUI's Link, offered two sizes and a bold weight, and underlined only on hover in its default style. Its source is [here](https://github.com/chanzuckerberg/sci-components/tree/main/packages/components/src/core/Link) and the underlying [MUI documentation](https://mui.com/material-ui/react-link/) still applies to the props it passed through.
6
+
7
+ ## Migrating away from it
8
+
9
+ | Old prop | Replace with |
10
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `sdsSize="s"` | `fontLinkS` |
12
+ | `sdsSize="xs"` | `fontLinkXs` |
13
+ | `fontWeight="bold"` | The semibold mixin for the size, such as `fontLinkSemiboldS`. The component's `"bold"` was `600`, which SDS calls semibold. |
14
+ | `sdsStyle="default"` | The link type style plus the accent text action colors. Note the difference: the component only underlined on hover, whereas the type style is underlined in every state. |
15
+ | `sdsStyle="dashed"` | `color: inherit` with `text-decoration: underline dashed`, going solid on hover, as in the last example above. |
16
+
17
+ ## Props
18
+
19
+ | Name | Type | Default | Description |
20
+ | ------------ | ------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- |
21
+ | `sdsStyle` | `"default"` \| `"dashed"` | `"default"` | `"default"` underlines on hover, focus, and press only; `"dashed"` is underlined dashed at rest and solid on interaction. |
22
+ | `sdsSize` | `"xs" \| "s"` | `"s"` | The font size, taken from the body scale rather than the link one. |
23
+ | `fontWeight` | `"normal"` \| `"bold"` | `"normal"` | Maps to `font-weight` `400` and `600`. |