@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.
Files changed (167) 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/ProteinStructureViewer.md +1044 -0
  44. package/data/component-docs/SegmentedControl.md +396 -0
  45. package/data/component-docs/StackedBarChart.md +475 -0
  46. package/data/component-docs/Table.md +2000 -0
  47. package/data/component-docs/TableHeader.md +53 -0
  48. package/data/component-docs/TableRow.md +74 -0
  49. package/data/component-docs/Tabs.md +455 -0
  50. package/data/component-docs/Tag.md +647 -0
  51. package/data/component-docs/TagFilter.md +131 -0
  52. package/data/component-docs/Tooltip.md +561 -0
  53. package/data/component-docs/TooltipCondensed.md +203 -0
  54. package/data/component-docs/TooltipTable.md +145 -0
  55. package/data/component-docs/index.json +267 -0
  56. package/data/component-list.json +2 -5
  57. package/data/component-props/Accordion.json +31 -11
  58. package/data/component-props/Alert.json +3 -3
  59. package/data/component-props/Autocomplete.json +150 -15
  60. package/data/component-props/Banner.json +31 -17
  61. package/data/component-props/Button.json +45 -20
  62. package/data/component-props/ButtonDropdown.json +36 -17
  63. package/data/component-props/ButtonDropdownLegacy.json +11 -11
  64. package/data/component-props/ButtonGroup.json +32 -11
  65. package/data/component-props/ButtonIcon.json +10 -10
  66. package/data/component-props/ButtonLegacy.json +19 -19
  67. package/data/component-props/ButtonToggle.json +32 -19
  68. package/data/component-props/ButtonToggleLegacy.json +11 -11
  69. package/data/component-props/Callout.json +58 -17
  70. package/data/component-props/CellBasic.json +94 -41
  71. package/data/component-props/CellComponent.json +23 -7
  72. package/data/component-props/CellHeader.json +62 -19
  73. package/data/component-props/ComplexFilter.json +52 -21
  74. package/data/component-props/ContentCard.json +70 -47
  75. package/data/component-props/Dialog.json +47 -14
  76. package/data/component-props/Dropdown.json +68 -31
  77. package/data/component-props/DropdownMenu.json +128 -23
  78. package/data/component-props/HeatmapChart.json +88 -12
  79. package/data/component-props/Hero.json +80 -39
  80. package/data/component-props/Icon.json +20 -9
  81. package/data/component-props/InputCheckbox.json +36 -14
  82. package/data/component-props/InputDropdown.json +75 -27
  83. package/data/component-props/InputRadio.json +31 -13
  84. package/data/component-props/InputSearch.json +41 -18
  85. package/data/component-props/InputSlider.json +46 -19
  86. package/data/component-props/InputText.json +26 -19
  87. package/data/component-props/InputToggle.json +26 -9
  88. package/data/component-props/IntentMessage.json +38 -9
  89. package/data/component-props/Link.json +13 -7
  90. package/data/component-props/LoadingIndicator.json +11 -3
  91. package/data/component-props/MenuItem.json +38 -19
  92. package/data/component-props/MenuSelect.json +8 -8
  93. package/data/component-props/NavigationFooter.json +49 -19
  94. package/data/component-props/NavigationHeader.json +150 -37
  95. package/data/component-props/NavigationJumpTo.json +33 -7
  96. package/data/component-props/Notification.json +39 -21
  97. package/data/component-props/Pagination.json +39 -11
  98. package/data/component-props/Panel.json +62 -9
  99. package/data/component-props/Popover.json +80 -7
  100. package/data/component-props/PreComposedTable.json +99 -27
  101. package/data/component-props/ProteinStructureViewer.json +117 -0
  102. package/data/component-props/SegmentedControl.json +42 -3
  103. package/data/component-props/StackedBarChart.json +116 -42
  104. package/data/component-props/TableRow.json +38 -19
  105. package/data/component-props/Tabs.json +66 -9
  106. package/data/component-props/Tag.json +34 -17
  107. package/data/component-props/TagFilter.json +10 -3
  108. package/data/component-props/Tooltip.json +49 -18
  109. package/data/component-props/TooltipCondensed.json +18 -9
  110. package/data/component-props/TooltipTable.json +17 -7
  111. package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
  112. package/data/resources/figma-rules.md +2 -2
  113. package/data/tailwind.json +20 -14
  114. package/dist/stdio.js +112 -208
  115. package/package.json +9 -5
  116. package/data/component-props/ButtonDropdownV2.json +0 -34
  117. package/data/component-props/ButtonNew.json +0 -47
  118. package/data/component-props/ButtonToggleV2.json +0 -44
  119. package/data/component-props/ButtonV2.json +0 -43
  120. package/data/zeroheight-cache.json +0 -10259
  121. package/data/zeroheight-exports/Accordion.md +0 -774
  122. package/data/zeroheight-exports/Banner.md +0 -691
  123. package/data/zeroheight-exports/Borders.md +0 -117
  124. package/data/zeroheight-exports/Buttons.md +0 -1854
  125. package/data/zeroheight-exports/Callout.md +0 -931
  126. package/data/zeroheight-exports/Colors.md +0 -1755
  127. package/data/zeroheight-exports/Content-Card.md +0 -309
  128. package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
  129. package/data/zeroheight-exports/Control-Inputs.md +0 -2818
  130. package/data/zeroheight-exports/Corners.md +0 -104
  131. package/data/zeroheight-exports/Cover-page.md +0 -3
  132. package/data/zeroheight-exports/Dialog.md +0 -1367
  133. package/data/zeroheight-exports/Downloads.md +0 -236
  134. package/data/zeroheight-exports/Drop-Shadows.md +0 -79
  135. package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
  136. package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
  137. package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
  138. package/data/zeroheight-exports/Field-Inputs.md +0 -1405
  139. package/data/zeroheight-exports/Filters.md +0 -1111
  140. package/data/zeroheight-exports/Getting-Started.md +0 -1157
  141. package/data/zeroheight-exports/Hero.md +0 -414
  142. package/data/zeroheight-exports/Icons.md +0 -2067
  143. package/data/zeroheight-exports/Intent.md +0 -86
  144. package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
  145. package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
  146. package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
  147. package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
  148. package/data/zeroheight-exports/Introduction.md +0 -115
  149. package/data/zeroheight-exports/Link.md +0 -432
  150. package/data/zeroheight-exports/Lists.md +0 -986
  151. package/data/zeroheight-exports/Loading-Indicator.md +0 -41
  152. package/data/zeroheight-exports/Navigation.md +0 -1190
  153. package/data/zeroheight-exports/Notification.md +0 -1038
  154. package/data/zeroheight-exports/Panel.md +0 -1030
  155. package/data/zeroheight-exports/README.md +0 -122
  156. package/data/zeroheight-exports/Responsive-Design.md +0 -330
  157. package/data/zeroheight-exports/Search-Input.md +0 -91
  158. package/data/zeroheight-exports/Search.md +0 -822
  159. package/data/zeroheight-exports/Segmented-Control.md +0 -609
  160. package/data/zeroheight-exports/Spacing.md +0 -297
  161. package/data/zeroheight-exports/Table.md +0 -5345
  162. package/data/zeroheight-exports/Tabs.md +0 -1324
  163. package/data/zeroheight-exports/Tags.md +0 -1545
  164. package/data/zeroheight-exports/Text-Link.md +0 -268
  165. package/data/zeroheight-exports/Theming.md +0 -362
  166. package/data/zeroheight-exports/Tooltips.md +0 -2015
  167. package/data/zeroheight-exports/Typography.md +0 -800
@@ -0,0 +1,525 @@
1
+ # Autocomplete
2
+
3
+ The searchable list of options behind every SDS dropdown: a MUI Autocomplete rendered as SDS MenuItems, in one column or several.
4
+
5
+ > **Most applications want DropdownMenu instead.** Autocomplete is the list itself, with no popper of its own and no trigger. DropdownMenu wraps it in a popper anchored to an element you choose, and Dropdown adds the click target too. Use Autocomplete directly when the list belongs somewhere a popper cannot go: inside a panel, a dialog body, or a layout you are placing yourself. The Dropdowns overview sets out the whole family.
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/components/src/core/Autocomplete/index.tsx).
10
+
11
+ ## Import
12
+
13
+ **React TypeScript**
14
+
15
+ ```tsx
16
+ import { Autocomplete } from "@czi-sds/components";
17
+ ```
18
+
19
+ ## Code examples
20
+
21
+ ### Default
22
+
23
+ A single list with a search field. Click the field to open it; options here carry nothing but a name.
24
+
25
+ **Example: DefaultAutocomplete**
26
+
27
+ ```tsx
28
+ // An option needs nothing but a name: it is the label, the key, and what
29
+ // equality is judged on. Everything else an option can carry is optional.
30
+ //
31
+ // search gives the component its visible text field. Without it the input is
32
+ // hidden and the list has to be opened by a parent, which is how DropdownMenu
33
+ // drives it.
34
+ //
35
+ // The component fills its container, so the width comes from the wrapper.
36
+
37
+ import {
38
+ Autocomplete,
39
+ type DefaultAutocompleteOption,
40
+ } from "@czi-sds/components";
41
+
42
+ const OPTIONS: DefaultAutocompleteOption[] = [
43
+ { name: "Astrocyte" },
44
+ { name: "B cell" },
45
+ { name: "Endothelial cell" },
46
+ { name: "Fibroblast" },
47
+ { name: "Macrophage" },
48
+ { name: "Neuron" },
49
+ { name: "T cell" },
50
+ ];
51
+
52
+ function App() {
53
+ return (
54
+ <div className="app">
55
+ <Autocomplete label="Search cell types" options={OPTIONS} search />
56
+ </div>
57
+ );
58
+ }
59
+
60
+ export default App;
61
+ ```
62
+
63
+ ### Multi-select
64
+
65
+ Selecting several options, with the selection read back out above the list. The component will not show it in the field.
66
+
67
+ **Example: AutocompleteMultiSelect**
68
+
69
+ ```tsx
70
+ // multiple keeps the list open after each pick and marks the chosen options
71
+ // with a checkmark. It does not show them in the input: the field always shows
72
+ // its label, because that is the parent's job in SDS: a DropdownMenu sits
73
+ // under an InputDropdown that reports the selection.
74
+ //
75
+ // Making it controlled is the way to read the selection out. The handler is
76
+ // given the whole value, not the option that changed, so it can be stored as
77
+ // is; details.option holds the one that was just added or removed.
78
+ //
79
+ // keepSearchOnSelect leaves the typed text in place after a pick, so several
80
+ // matches for the same search can be selected without retyping it.
81
+
82
+ import { useState } from "react";
83
+ import {
84
+ Autocomplete,
85
+ type AutocompleteSingleColumnOnChange,
86
+ type DefaultAutocompleteOption,
87
+ } from "@czi-sds/components";
88
+
89
+ const OPTIONS: DefaultAutocompleteOption[] = [
90
+ { name: "Astrocyte" },
91
+ { name: "B cell" },
92
+ { name: "Endothelial cell" },
93
+ { name: "Fibroblast" },
94
+ { name: "Macrophage" },
95
+ { name: "Neuron" },
96
+ { name: "T cell" },
97
+ ];
98
+
99
+ function App() {
100
+ const [selected, setSelected] = useState<DefaultAutocompleteOption[]>([]);
101
+
102
+ // Pinning the generics is what makes the handler's value typed: multiple,
103
+ // not clearable-disabled, not freeSolo, so the value is an array of options.
104
+ const handleChange: AutocompleteSingleColumnOnChange<
105
+ DefaultAutocompleteOption,
106
+ true,
107
+ false,
108
+ false
109
+ > = (_event, value) => setSelected(value);
110
+
111
+ return (
112
+ <div className="app">
113
+ <Autocomplete<DefaultAutocompleteOption, true, false, false>
114
+ keepSearchOnSelect
115
+ label="Search cell types"
116
+ multiple
117
+ options={OPTIONS}
118
+ search
119
+ value={selected}
120
+ onChange={handleChange}
121
+ InputBaseProps={{
122
+ style: {
123
+ width: "280px",
124
+ },
125
+ }}
126
+ />
127
+ </div>
128
+ );
129
+ }
130
+
131
+ export default App;
132
+ ```
133
+
134
+ ### Grouped sections
135
+
136
+ `groupBy` splits the same list into labelled sections.
137
+
138
+ **Example: AutocompleteGroupedSections**
139
+
140
+ ```tsx
141
+ // groupBy sorts the list into labelled sections. It reads whatever the callback
142
+ // returns, so section is only a convention: any field, or a value computed on
143
+ // the spot, will do.
144
+ //
145
+ // The list is not reordered for you: options are grouped in the order they
146
+ // arrive, so an option that repeats a section already passed starts a second
147
+ // section with the same heading. Sort the options the way the sections should
148
+ // read before handing them over.
149
+
150
+ import {
151
+ Autocomplete,
152
+ type DefaultAutocompleteOption,
153
+ } from "@czi-sds/components";
154
+
155
+ const OPTIONS: DefaultAutocompleteOption[] = [
156
+ { name: "Astrocyte", section: "Nervous" },
157
+ { name: "Neuron", section: "Nervous" },
158
+ { name: "Oligodendrocyte", section: "Nervous" },
159
+ { name: "B cell", section: "Immune" },
160
+ { name: "Macrophage", section: "Immune" },
161
+ { name: "T cell", section: "Immune" },
162
+ { name: "Endothelial cell", section: "Structural" },
163
+ { name: "Fibroblast", section: "Structural" },
164
+ ];
165
+
166
+ function App() {
167
+ return (
168
+ <div className="app">
169
+ <Autocomplete
170
+ groupBy={(option) => option.section as string}
171
+ label="Search cell types"
172
+ multiple
173
+ options={OPTIONS}
174
+ search
175
+ InputBaseProps={{
176
+ style: {
177
+ width: "280px",
178
+ },
179
+ }}
180
+ />
181
+ </div>
182
+ );
183
+ }
184
+
185
+ export default App;
186
+ ```
187
+
188
+ ### What an option can show
189
+
190
+ Counts, details, icons, a disabled row, and an option that renders a component of its own instead of a label.
191
+
192
+ **Example: AutocompleteOptionContent**
193
+
194
+ ```tsx
195
+ // Beyond its name, an option can carry a count, a line of details, and an icon:
196
+ // either an SDS icon name, styled through sdsIconProps, or an element of your
197
+ // own. The MenuItem the list renders lays all of that out.
198
+ //
199
+ // An option can instead carry a component, and then it renders that in place of
200
+ // the label. The two are mutually exclusive by type: an option is either a
201
+ // described one or a custom one, never both. It still needs a name, which is
202
+ // what search matches against and what selection is tracked by.
203
+
204
+ import {
205
+ Autocomplete,
206
+ Tag,
207
+ type DefaultAutocompleteOption,
208
+ } from "@czi-sds/components";
209
+
210
+ const OPTIONS: DefaultAutocompleteOption[] = [
211
+ { count: 128, name: "T cell" },
212
+ {
213
+ details: "Includes both classical and non-classical subsets",
214
+ name: "Monocyte",
215
+ },
216
+ {
217
+ icon: "TreeVertical",
218
+ name: "Neuron",
219
+ sdsIconProps: { color: "blue", shade: 400 },
220
+ },
221
+ {
222
+ count: 12,
223
+ details: "Awaiting curation",
224
+ icon: "List",
225
+ name: "Unannotated",
226
+ sdsIconProps: { color: "gray", shade: 500 },
227
+ },
228
+ { disabled: true, name: "Platelet" },
229
+ {
230
+ component: (
231
+ <div>
232
+ Available labels
233
+ <div style={{ display: "flex", gap: 4, marginTop: 4 }}>
234
+ <Tag
235
+ color="positive"
236
+ hover={false}
237
+ label="curated"
238
+ sdsStyle="square"
239
+ sdsType="secondary"
240
+ />
241
+ <Tag
242
+ color="notice"
243
+ hover={false}
244
+ label="draft"
245
+ sdsStyle="square"
246
+ sdsType="secondary"
247
+ />
248
+ </div>
249
+ </div>
250
+ ),
251
+ name: "Available labels",
252
+ },
253
+ ];
254
+
255
+ function App() {
256
+ return (
257
+ <div className="app">
258
+ <Autocomplete
259
+ label="Search cell types"
260
+ multiple
261
+ options={OPTIONS}
262
+ search
263
+ InputBaseProps={{
264
+ style: {
265
+ width: "320px",
266
+ },
267
+ }}
268
+ />
269
+ </div>
270
+ );
271
+ }
272
+
273
+ export default App;
274
+ ```
275
+
276
+ ### Multiple columns
277
+
278
+ Two lists side by side under one search field, and the record-shaped value they report.
279
+
280
+ **Example: AutocompleteMultiColumn**
281
+
282
+ ```tsx
283
+ // Passing columns instead of options switches the component to its multi-column
284
+ // form: each entry is a named list with its own width, rendered side by side and
285
+ // separated by a divider that can carry an icon.
286
+ //
287
+ // The value changes shape to match. Instead of one selection there is one per
288
+ // column, keyed by the column's name, and the handler receives the whole record
289
+ // every time any column changes.
290
+ //
291
+ // One search field sits above all the columns and filters them together.
292
+ // groupBy is ignored here: sections and columns do not combine.
293
+
294
+ import { useState } from "react";
295
+ import {
296
+ Autocomplete,
297
+ Icon,
298
+ fontBodyXs,
299
+ getSemanticColors,
300
+ getSpaces,
301
+ type AutocompleteMultiColumnOnChange,
302
+ type AutocompleteMultiColumnOption,
303
+ type AutocompleteMultiColumnValue,
304
+ type CommonThemeProps,
305
+ type DefaultAutocompleteOption,
306
+ } from "@czi-sds/components";
307
+ import styled from "@emotion/styled";
308
+
309
+ type Option = DefaultAutocompleteOption;
310
+
311
+ const COLUMNS: AutocompleteMultiColumnOption<Option, true, false, false>[] = [
312
+ {
313
+ icon: <Icon sdsIcon="ChevronRight" sdsSize="xs" />,
314
+ name: "Tissue",
315
+ options: [
316
+ { name: "Blood" },
317
+ { name: "Brain" },
318
+ { name: "Kidney" },
319
+ { name: "Lung" },
320
+ ],
321
+ },
322
+ {
323
+ name: "Assay",
324
+ options: [
325
+ { count: 24, name: "10x 3' v3" },
326
+ { name: "Slide-seq" },
327
+ { details: "Sequential FISH", name: "seqFISH" },
328
+ { name: "Smart-seq2" },
329
+ ],
330
+ },
331
+ ];
332
+
333
+ const Readout = styled.p<CommonThemeProps>`
334
+ ${fontBodyXs}
335
+
336
+ ${(props) => {
337
+ const semanticColors = getSemanticColors(props);
338
+ const spaces = getSpaces(props);
339
+
340
+ return `
341
+ color: ${semanticColors?.base?.textSecondary};
342
+ margin: 0 0 ${spaces?.m}px;
343
+ `;
344
+ }}
345
+ `;
346
+
347
+ type Picked = AutocompleteMultiColumnValue<Option, true, false, false>;
348
+
349
+ function App() {
350
+ const [picked, setPicked] = useState<Picked>({});
351
+
352
+ const handleChange: AutocompleteMultiColumnOnChange<
353
+ Option,
354
+ true,
355
+ false,
356
+ false
357
+ > = (_event, value) => setPicked(value);
358
+
359
+ const summary = Object.entries(picked ?? {})
360
+ .filter(([, options]) => options.length)
361
+ .map(([column, options]) => `${column}: ${options.length}`)
362
+ .join(" · ");
363
+
364
+ return (
365
+ <div className="app">
366
+ <Readout>{summary || "Nothing selected."}</Readout>
367
+
368
+ <Autocomplete<Option, true, false, false>
369
+ label="Search tissues and assays"
370
+ multiple
371
+ options={COLUMNS}
372
+ search
373
+ onChange={handleChange}
374
+ InputBaseProps={{
375
+ style: {
376
+ width: "280px",
377
+ },
378
+ }}
379
+ />
380
+ </div>
381
+ );
382
+ }
383
+
384
+ export default App;
385
+ ```
386
+
387
+ ## SDS vs MUI
388
+
389
+ SDS Autocomplete wraps [MUI Autocomplete](https://mui.com/material-ui/react-autocomplete/), so every MUI prop is available and anything not described here behaves as MUI documents it. What SDS adds is a house style and a set of defaults that suit dropdowns rather than form fields:
390
+
391
+ - **Options render as SDS MenuItems** inside a MenuList, which is where the checkmarks, counts, details, icons, and disabled styling come from. Supplying your own `renderOption` replaces all of it.
392
+
393
+ - **The selection is never drawn in the input.** MUI shows chips for a multi-select; SDS returns `null` from `renderValue`, so the field keeps showing its label and the checkmarks in the list are the only indication. Reporting the selection belongs to whatever sits above: an InputDropdown, a set of Tags, your own summary.
394
+
395
+ - **The input is an SDS InputSearch** with a search button and a clear button, built by the component. Reach it through `InputBaseProps`. MUI's `renderInput` still works and replaces that input outright, which also takes the two buttons with it, so it is a last resort rather than the way in.
396
+
397
+ - **Defaults differ.** `clearOnBlur` is `false` rather than MUI's `true`, so typed text survives a blur; `disableCloseOnSelect` follows `multiple`; and `blurOnSelect` is forced off for a multi-select so the list stays put while several options are picked. Backspace is also stopped from reaching MUI, where it would otherwise remove the last selection.
398
+
399
+ - **The popper never portals.** `disablePortal` is set, so the list renders in place, inside whatever container the component is in, and inherits its stacking and overflow.
400
+
401
+ ## Options
402
+
403
+ Every option needs a `name`. It is the label, the search text, the React key, and what selection equality is judged on by default, so names should be unique within a list. Beyond that an option comes in one of two shapes, and the type will not let you mix them:
404
+
405
+ - A **described option** may add `count`, a number shown at the end of the row; `details`, a second line under the label; `icon`, either an SDS icon name or an element of your own; and `sdsIconProps` to colour and size a named icon.
406
+
407
+ - A **custom option** carries a `component` instead, rendered in place of the label inside the same MenuItem. It still needs a name for search and selection to work.
408
+
409
+ Either shape may set `section`, which `groupBy` can read to split the list into labelled groups, and `disabled`, which greys the row out and blocks selection. Options are grouped in the order they arrive rather than being sorted, so a section that reappears later in the array is rendered as a second group under the same heading.
410
+
411
+ ## One column or several
412
+
413
+ The shape of `options` decides which form you get. A flat array is a single list. An array of objects that each have their own `options` is a multi-column dropdown, with one list per entry, a heading from its `name`, a fixed `width`, and an optional `icon` drawn on the divider that follows it:
414
+
415
+ **React TypeScript**
416
+
417
+ ```tsx
418
+ // One column
419
+ options={[{ name: "Brain" }, { name: "Lung" }]}
420
+
421
+ // Two columns
422
+ options={[
423
+ { name: "Tissue", options: [{ name: "Brain" }], width: 180 },
424
+ { name: "Assay", options: [{ name: "Slide-seq" }], width: 220 },
425
+ ]}
426
+ ```
427
+
428
+ The value follows the same split. A single column reports what MUI reports: an option, or an array of them for a multi-select. Multiple columns report a record keyed by column name, with each column's own value inside it, and the handler receives the whole record whenever any column changes.
429
+
430
+ A columns array of length one collapses back to a single list, so a variable number of columns needs no special casing. `groupBy` is ignored once there is more than one column: sections and columns do not combine.
431
+
432
+ ## Search, and who opens the list
433
+
434
+ `search` is what gives the component a visible, focusable text field. It is off by default, and when it is off the input is not merely hidden but clipped and disabled, which also means there is nothing left to click, and the list can only be opened by a parent through `open`. That is the arrangement DropdownMenu uses, and the reason a bare Autocomplete with no props appears to do nothing.
435
+
436
+ So there are two working configurations: give it `search` and let it manage itself, or control `open` from outside and use `onClick` and `onClickAway` to hear when the component would like to open and close. Both are advisory when `open` is controlled. The list stays where your state puts it.
437
+
438
+ Filtering is MUI's, matching against `getOptionLabel`, which defaults to the option's name; pass `filterOptions` to change how matching works. When nothing matches, `noOptionsText` is shown. For a multi-select, `keepSearchOnSelect` leaves the typed text in place after each pick so several matches for one search can be selected without retyping it.
439
+
440
+ ## Behavior and accessibility
441
+
442
+ - A single-select closes and blurs on selection. A multi-select stays open, keeps focus, and marks selected rows with a checkmark, shifting the labels right to make room.
443
+
444
+ - The listbox keeps its scroll position across selections, so picking an option halfway down a long list does not jump back to the top.
445
+
446
+ - Keyboard support is MUI's: arrows move the highlight, Enter selects, Escape closes, and typing filters. The field takes a focus outline only when it was reached by tabbing, not when it was clicked.
447
+
448
+ - With `search` off there is no keyboard path into the component at all: the input is disabled and `aria-hidden` along with its two buttons, and the options carry a `tabindex` of `-1`. Anything built on that mode has to provide its own keyboard route to the list.
449
+
450
+ - The input is labelled by `label`, which is used as both the visible label and the placeholder. It is worth writing as an instruction: "Search cell types" rather than "Search".
451
+
452
+ - Because the input never shows the selection, a selection that is not reported anywhere else is invisible to someone who cannot see the checkmarks. Pair the list with a summary of what is chosen.
453
+
454
+ > **count and icon do nothing at the top level.** Both are accepted on the component itself, left over from an earlier version, and neither is rendered. On an option they work as described above; on a column, `icon` draws the divider glyph.
455
+
456
+ ## Typing onChange
457
+
458
+ The handler is typed as a union of the single-column and multi-column signatures, which is enough to stop TypeScript inferring its parameters. Pin the component's generics and declare the handler with the matching type, and both sides line up:
459
+
460
+ **React TypeScript**
461
+
462
+ ```tsx
463
+ import {
464
+ Autocomplete,
465
+ type AutocompleteSingleColumnOnChange,
466
+ type DefaultAutocompleteOption,
467
+ } from "@czi-sds/components";
468
+
469
+ // <Option, Multiple, DisableClearable, FreeSolo>
470
+ const handleChange: AutocompleteSingleColumnOnChange<
471
+ DefaultAutocompleteOption,
472
+ true,
473
+ false,
474
+ false
475
+ > = (event, value) => setSelected(value);
476
+
477
+ <Autocomplete<DefaultAutocompleteOption, true, false, false>
478
+ multiple
479
+ onChange={handleChange}
480
+ options={OPTIONS}
481
+ value={selected}
482
+ />;
483
+ ```
484
+
485
+ Use `AutocompleteMultiColumnOnChange` and `AutocompleteMultiColumnValue` for the multi-column form.
486
+
487
+ ## MUI Documentation
488
+
489
+ Documentation for the underlying MUI Autocomplete component can be found [here](https://mui.com/material-ui/react-autocomplete/).
490
+
491
+ ## Props
492
+
493
+ The props below are the SDS additions and the MUI props whose behaviour SDS changes. Everything else is passed through to MUI Autocomplete; see the MUI documentation for the rest.
494
+
495
+ | **Name** | **Type** | **Default** | **Description** |
496
+ | ------------------------ | -------------------------------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
497
+ | `options` | `T[]` \| `AutocompleteMultiColumnOption<T>[]` | - (required) | The options, or an array of named columns each with their own options. |
498
+ | `value` | `T \| T[]` \| `Record<string, T \| T[]>` | - | The selection. An array when `multiple`, and a record keyed by column name when there is more than one column. |
499
+ | `onChange` | `function` | - | Fired on every selection change with the whole value. `details.option` is the one that just changed. **Signature:** `function(event: SyntheticEvent, value: T \| T[], reason: AutocompleteChangeReason, details?: AutocompleteChangeDetails<T>) => void` |
500
+ | `onInputChange` | `function` | - | Fired as the search text changes, after the component has updated its own input state. **Signature:** `function(event: SyntheticEvent, value: string, reason: AutocompleteInputChangeReason) => void` |
501
+ | `multiple` | `boolean` | `false` | Allows more than one selection, shows checkmarks, and keeps the list open and focused after each pick. |
502
+ | `search` | `boolean` | `false` | Shows the search field. Without it the input is hidden and disabled, and the list must be opened through `open`. |
503
+ | `label` | `string` | `"Label"` `"Search"` (columns) | The search field's label and placeholder. |
504
+ | `open` | `boolean` | - | Controls whether the list is shown. Once set, the component will not open or close itself. |
505
+ | `onClick` | `(event) => void` | - | Fired when the component would like to open. |
506
+ | `onClickAway` | `(event, reason) => void` | - | Fired when the component would like to close, including on Escape and on a click outside. |
507
+ | `keepSearchOnSelect` | `boolean` | `false` | For a multi-select, leaves the typed text in place after a selection instead of clearing it. |
508
+ | `groupBy` | `(option: T) => string` | - | Splits the list into labelled sections. Ignored when there is more than one column. |
509
+ | `intent` | `"default" \| "negative"` \| `"notice" \| "positive"` | `"default"` | Validation styling on the search field. Single column only; the multi-column field does not take it. |
510
+ | `InputBaseProps` | `Partial<InputSearchProps>` | - | Props for the search field. This is the way in: SDS supplies `renderInput` itself. |
511
+ | `renderInput` | `function` | an SDS InputSearch | `(params: AutocompleteRenderInputParams) => ReactNode`. Replaces the whole input, losing the search and clear buttons SDS puts there, so prefer `InputBaseProps`. On a multi-column Autocomplete it reaches the hidden per-column inputs rather than the trigger. |
512
+ | `PopperBaseProps` | `Partial<PopperProps>` | - | Props for the popper holding the columns. Multi-column only; a single column uses MUI's own popper. |
513
+ | `PopperComponent` | `component` | SDS styled Popper | Replaces that popper outright. Multi-column only. |
514
+ | `PopperPlacement` | `"bottom-start" \| "bottom-end"` \| `"top-start" \| "top-end"` | `"bottom-start"` | Where the columns are placed. Multi-column only. |
515
+ | `ClickAwayListenerProps` | `Partial<ClickAwayListenerProps>` | - | Props for the MUI ClickAwayListener that closes the columns. Multi-column only. |
516
+ | `disableCloseOnSelect` | `boolean` | `multiple` | Keeps the list open after a selection. Follows `multiple` unless you set it. |
517
+ | `blurOnSelect` | `boolean \| "touch" \| "mouse"` | `!multiple` | Blurs the input after a selection. Forced off when `multiple`. |
518
+ | `clearOnBlur` | `boolean` | `false` | Clears the typed text when the field loses focus. MUI defaults this to `true`; SDS does not. |
519
+ | `getOptionLabel` | `(option: T) => string` | `option.name` | The text an option displays and is searched by. |
520
+ | `isOptionEqualToValue` | `(option: T, value) => boolean` | compares names | How an option is matched to the value. Override it when names are not unique or the value is a different object. |
521
+ | `getOptionDisabled` | `(option: T) => boolean` | - | Disables options by rule, as an alternative to the `disabled` flag on an option. |
522
+ | `renderOption` | `(props, option, state) => node` | SDS MenuItem | Replaces the row entirely, including checkmarks, counts, details, and icons. |
523
+ | `noOptionsText` | `ReactNode` | `"No options"` | Shown when nothing matches the search. |
524
+ | `loading` | `boolean` | `false` | Shows `loadingText` in place of the options. Note that `loadingText` defaults to an empty string, so set it. |
525
+ | `disabled` | `boolean` | `false` | Disables the search field. |