@czi-sds/mcp 0.2.2 → 0.3.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 (50) hide show
  1. package/data/zeroheight-cache.json +5195 -0
  2. package/data/zeroheight-exports/Accordion.md +675 -0
  3. package/data/zeroheight-exports/Banner.md +576 -0
  4. package/data/zeroheight-exports/Borders.md +118 -0
  5. package/data/zeroheight-exports/Buttons.md +2260 -0
  6. package/data/zeroheight-exports/Callout.md +756 -0
  7. package/data/zeroheight-exports/Colors.md +633 -0
  8. package/data/zeroheight-exports/Content-Card.md +162 -0
  9. package/data/zeroheight-exports/Contributing-to-SDS.md +99 -0
  10. package/data/zeroheight-exports/Control-Inputs.md +2066 -0
  11. package/data/zeroheight-exports/Corners.md +112 -0
  12. package/data/zeroheight-exports/Cover-page.md +4 -0
  13. package/data/zeroheight-exports/Dialog.md +1133 -0
  14. package/data/zeroheight-exports/Downloads.md +121 -0
  15. package/data/zeroheight-exports/Drop-Shadows.md +80 -0
  16. package/data/zeroheight-exports/Dropdown-Input.md +1149 -0
  17. package/data/zeroheight-exports/Dropdown-Menu.md +3787 -0
  18. package/data/zeroheight-exports/Element-Status-Tracker.md +268 -0
  19. package/data/zeroheight-exports/Field-Inputs.md +965 -0
  20. package/data/zeroheight-exports/Filters.md +868 -0
  21. package/data/zeroheight-exports/Getting-Started.md +1296 -0
  22. package/data/zeroheight-exports/Hero.md +256 -0
  23. package/data/zeroheight-exports/Icons.md +432 -0
  24. package/data/zeroheight-exports/Intent.md +47 -0
  25. package/data/zeroheight-exports/Introduction-to-Bases.md +36 -0
  26. package/data/zeroheight-exports/Introduction-to-Cells.md +36 -0
  27. package/data/zeroheight-exports/Introduction-to-DNA.md +36 -0
  28. package/data/zeroheight-exports/Introduction-to-Genes.md +36 -0
  29. package/data/zeroheight-exports/Introduction.md +116 -0
  30. package/data/zeroheight-exports/Link.md +373 -0
  31. package/data/zeroheight-exports/Lists.md +617 -0
  32. package/data/zeroheight-exports/Loading-Indicator.md +38 -0
  33. package/data/zeroheight-exports/Navigation.md +853 -0
  34. package/data/zeroheight-exports/Notification.md +858 -0
  35. package/data/zeroheight-exports/Panel.md +862 -0
  36. package/data/zeroheight-exports/README.md +124 -0
  37. package/data/zeroheight-exports/Responsive-Design.md +275 -0
  38. package/data/zeroheight-exports/Search-Input.md +54 -0
  39. package/data/zeroheight-exports/Search.md +609 -0
  40. package/data/zeroheight-exports/Segmented-Control.md +561 -0
  41. package/data/zeroheight-exports/Spacing.md +298 -0
  42. package/data/zeroheight-exports/Table.md +4832 -0
  43. package/data/zeroheight-exports/Tabs.md +773 -0
  44. package/data/zeroheight-exports/Tags.md +1025 -0
  45. package/data/zeroheight-exports/Text-Link.md +185 -0
  46. package/data/zeroheight-exports/Theming.md +313 -0
  47. package/data/zeroheight-exports/Tooltips.md +1564 -0
  48. package/data/zeroheight-exports/Typography.md +769 -0
  49. package/dist/stdio.js +188 -0
  50. package/package.json +4 -3
@@ -0,0 +1,3787 @@
1
+ # Dropdown Menus
2
+
3
+ Dropdown Menus are coupled with Dropdown Inputs and in some cases Icon Buttons whenever those elements present the user with options to choose from or selections to make.
4
+
5
+ ## Overview
6
+
7
+ >**Warning:** The Dropdown Menu and its affiliated components have undergone a major overhaul in their construction and how they have been coded, rendering the documentation below out of date.
8
+ >
9
+ >While the SDS works on updating the documentation to reflect these changes, see `src/core/Dropdown/index.stories.tsx` within the `sci-components` codebase for example usage of the new Dropdown Menu structure.
10
+
11
+ ## Dropdown Menu Variants
12
+
13
+ | In their basic form, Dropdown Menus are composed of a Menu Container that is filled with Menu Items offering the user options to adjust or select that are contextually relevant to the Dropdown Input they are coupled with. Menu Items can be optionally paired with additional details or with counters that indicate the number of items within that Menu Item's category. Additionally, Dropdown Menus can be configured to include a Search Input, have their Menu Items separated out into different sections or groupings, or a combination of these features. | | **Jump to section:** Dropdown Menu Common Configurations Menu Item |
14
+ | --- | --- | --- |
15
+
16
+ ---
17
+
18
+ ## Dropdown Menu
19
+
20
+ | | In Figma | | | Meets Accessibility | | | In napari hub + .org Codebases |
21
+ | --- | --- | --- | --- | --- | --- | --- | --- |
22
+
23
+ Dropdown Menus can be configured to have their Menu Items all in one large list or separated out into different sections or groupings; these Menu Item formats are established by the `[array]` passed into the `options` prop. Additionally, Dropdown Menus can be configured to include a Search Input. See the Data Format section below for details about configuring the `[array]`.
24
+
25
+ Dropdown Menus are used in combination with Dropdown Inputs on napari hub as part of plugin Filters, accessible on the left side of the plugin page.
26
+
27
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
28
+
29
+  
30
+
31
+ ---
32
+
33
+  
34
+
35
+ ---
36
+
37
+ ### Data Format
38
+
39
+ Use the following configuration for the `[array]` passed into the `options` prop:
40
+
41
+ ```js
42
+ data = [
43
+ {
44
+ // section with a Header and Menu Item Details
45
+ dataRows: [
46
+ {
47
+ name: "Menu Item",
48
+ details: "Details",
49
+ },
50
+ {
51
+ name: "Menu Item",
52
+ details: "Details",
53
+ },
54
+ {
55
+ name: "Menu Item",
56
+ details: "Details",
57
+ },
58
+ ],
59
+ label: "Section One",
60
+ },
61
+ {
62
+ // section with Menu Item Counts
63
+ dataRows: [
64
+ {
65
+ name: "Menu Item",
66
+ count: 20,
67
+ },
68
+ {
69
+ name: "Menu Item",
70
+ count: 40,
71
+ },
72
+ {
73
+ name: "Menu Item",
74
+ count: 60,
75
+ },
76
+ ],
77
+ },
78
+ {
79
+ // section with disabled Menu Items
80
+ dataRows: [
81
+ {
82
+ name: "Menu Item",
83
+ disabled: true,
84
+ },
85
+ {
86
+ name: "Menu Item",
87
+ disabled: true,
88
+ },
89
+ {
90
+ name: "Menu Item",
91
+ disabled: true,
92
+ },
93
+ ],
94
+ },
95
+ ];
96
+ ```
97
+
98
+ ---
99
+
100
+ ### Dropdown Menu Spacing
101
+
102
+ These rules establish how much margin should exist between and around elements.
103
+
104
+ ---
105
+
106
+ ## Common Configurations
107
+
108
+ Dropdown Menus can be thought of as being comprised of different combinations of building blocks all within a Menu Container, ensuring a wide range of flexibility for whatever need a designer has with their designs.
109
+
110
+ Below are a number of common Dropdown Menu configurations, but by no means is this an exhaustive list:
111
+
112
+ | **Search + Count** | **Search + Sections** | **Search + Details** |
113
+ | --- | --- | --- |
114
+
115
+ | **Sections + Details** | **Search + Sections + Details** | **Search + Sections + Count** |
116
+ | --- | --- | --- |
117
+
118
+ ---
119
+
120
+ ## Menu Item
121
+
122
+ | | In Figma | | | Meets Accessibility | | | In napari hub + .org Codebases |
123
+ | --- | --- | --- | --- | --- | --- | --- | --- |
124
+
125
+ Menu Items can be optionally paired with additional clarifying details, with counters that indicate the number of items within that Menu Item's category, or be disabled entirely. These details are established within the `[array]` passed into the `options` prop on the Dropdown Menu component.
126
+
127
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
128
+
129
+ | **Default** | **Hover** | **Selected** | **Selected + Hover** | **Disabled** | **Selected + Disabled** |
130
+ | --- | --- | --- | --- | --- | --- |
131
+
132
+ | **Default + Count** | **Hover** | **Selected** | **Selected + Hover** | **Disabled** | **Selected + Disabled** |
133
+ | --- | --- | --- | --- | --- | --- |
134
+
135
+ | **Default + Details** | **Hover** | **Selected** | **Selected + Hover** | **Disabled** | **Selected + Disabled**  |
136
+ | --- | --- | --- | --- | --- | --- |
137
+
138
+ ## Code
139
+
140
+ ## Dropdown Menu Component
141
+
142
+ | Below you will find interactive Storybook iframes for the Dropdown Menu. Tabs within each iframe provide a different set of tools: The Controls tab allows you to see and adjust all available props. The Actions tab shows you data displayed by event handler arguments. | | **Jump to component:** DropdownMenu |
143
+ | --- | --- | --- |
144
+
145
+ ---
146
+
147
+ >**Warning:** The Dropdown Menu and its affiliated components have undergone a major overhaul in their construction and how they have been coded, rendering the documentation below out of date.
148
+ >
149
+ >While the SDS works on updating the documentation to reflect these changes, see `src/core/Dropdown/index.stories.tsx` within the `sci-components` codebase for example usage of the new Dropdown Menu structure.
150
+
151
+ >This component appears as it is in the default SDS codebase. Once imported into the napari hub or napari.org codebases, use a theme file to enable the napari visual appearance customization.
152
+
153
+ ### DropdownMenu
154
+
155
+ Storybook
156
+
157
+  
158
+
159
+ ---
160
+
161
+ Dropdown Menus are coupled with Dropdown Inputs, Dropdown Buttons, and in some cases Icon Buttons whenever those elements present the user with options to choose from or selections to make.
162
+
163
+ ## Overview
164
+
165
+ >**NOTE:** The Dropdown Menu and its affiliated components have undergone a major overhaul in their construction and how they have been coded, rendering the documentation below out of date.
166
+ >
167
+ >While the SDS works on updating the documentation to reflect these changes, see `src/core/Dropdown/index.stories.tsx` within the `sci-components` codebase for example usage of the new Dropdown Menu structure.
168
+
169
+ ## Dropdown Menu Variants
170
+
171
+ | In their basic form, Dropdown Menus are composed of a Menu Container that is filled with Menu Items offering the user options to adjust or select that are contextually relevant to the Button or Dropdown Input they are coupled with. Menu Items can be optionally paired with additional details or with counters that indicate the number of items within that Menu Item's category. Dropdown Menus can also be multi-select, allowing the user to choose multiple options from the group of Menu Items. Additionally, Dropdown Menus can be configured to include a Search Input, have their Menu Items separated out into different sections or groupings, have a built-in title, or any combination of these features. | | **Jump to section:** Dropdown Menu Common Configurations Menu Item |
172
+ | --- | --- | --- |
173
+
174
+ ---
175
+
176
+ ## Dropdown Menu
177
+
178
+ | | In Figma | | | Meets Accessibility | | | In CZ GEN EPI Codebase |
179
+ | --- | --- | --- | --- | --- | --- | --- | --- |
180
+
181
+ ### Single-select Dropdown Menu
182
+
183
+ Use the Single-select Dropdown Menu when users are only able to select one item from the menu. They can be configured to have their Menu Items separated out into different sections or groupings, or have a built-in title; both of these features are established by the `[array]` passed into the `options` prop. Additionally, they can be configured to include a Search Input. See the Data Format section below for details around configuring the `[array]`.
184
+
185
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
186
+
187
+  
188
+
189
+ ---
190
+
191
+ ---
192
+
193
+ ---
194
+
195
+ ### Multi-select Dropdown Menu
196
+
197
+ Use the Multi-select Dropdown Menu when users are able to select one or more items from the menu. They can be configured to have their Menu Items separated out into different sections or groupings, or have a built-in title; both of these features are established by the `[array]` passed into the `options` prop. Additionally, they can be configured to include a Search Input. See the Data Format section below for details around configuring the `[array]`.
198
+
199
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
200
+
201
+  
202
+
203
+ ---
204
+
205
+ ---
206
+
207
+ ---
208
+
209
+ ### Data Format
210
+
211
+ Use the following configuration for the `[array]` passed into the `options` prop:
212
+
213
+ ```js
214
+ data = [
215
+ {
216
+ // section with a Header and Menu Item Details
217
+ dataRows: [
218
+ {
219
+ name: "Menu Item",
220
+ details: "Details",
221
+ },
222
+ {
223
+ name: "Menu Item",
224
+ details: "Details",
225
+ },
226
+ {
227
+ name: "Menu Item",
228
+ details: "Details",
229
+ },
230
+ ],
231
+ label: "Section One",
232
+ },
233
+ {
234
+ // section with Menu Item Counts
235
+ dataRows: [
236
+ {
237
+ name: "Menu Item",
238
+ count: 20,
239
+ },
240
+ {
241
+ name: "Menu Item",
242
+ count: 40,
243
+ },
244
+ {
245
+ name: "Menu Item",
246
+ count: 60,
247
+ },
248
+ ],
249
+ },
250
+ ];
251
+ ```
252
+
253
+ ---
254
+
255
+ ### Dropdown Menu Spacing
256
+
257
+ These rules establish how much margin should exist between and around elements.
258
+
259
+ ---
260
+
261
+ ## Common Configurations
262
+
263
+ Dropdown Menus can be thought of as being comprised of different combinations of building blocks all within a Menu Container, ensuring a wide range of flexibility for whatever need a designer has with their designs.
264
+
265
+ Below are a number of common Dropdown Menu configurations, but by no means is this an exhaustive list:
266
+
267
+ | **Title + Count** | **Search + Sections + Details** | **Title + Details** |
268
+ | --- | --- | --- |
269
+
270
+ | **Search + Details** | **Title + Search + Count** | **Title + Sections + Details** |
271
+ | --- | --- | --- |
272
+
273
+ ---
274
+
275
+ ## Menu Item
276
+
277
+ | | In Figma | | | Meets Accessibility | | | In CZ GEN EPI Codebase |
278
+ | --- | --- | --- | --- | --- | --- | --- | --- |
279
+
280
+ ### Single-select Menu Item
281
+
282
+ Menu Items can be optionally paired with additional clarifying details, with counters that indicate the number of items within that Menu Item's category, or be disabled entirely. These details are established within the `[array]` passed into the `options` prop on the Dropdown Menu component.
283
+
284
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
285
+
286
+ | **Default** | **Hover** | **Selected** | **Disabled** |
287
+ | --- | --- | --- | --- |
288
+
289
+ | **Default + Count** | **Hover** | **Selected** | **Disabled** |
290
+ | --- | --- | --- | --- |
291
+
292
+ | **Default + Details** | **Hover** | **Selected** | **Disabled** |
293
+ | --- | --- | --- | --- |
294
+
295
+  
296
+
297
+ ### Multi-select Menu Item
298
+
299
+ Menu Items can be optionally paired with additional clarifying details, with counters that indicate the number of items within that Menu Item's category, or be disabled entirely. These details are established within the `[array]` passed into the `options` prop on the Dropdown Menu component.
300
+
301
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
302
+
303
+ | **Default** | **Hover** | **Selected** | **Disabled** |
304
+ | --- | --- | --- | --- |
305
+
306
+ | **Default + Count** | **Hover** | **Selected** | **Disabled** |
307
+ | --- | --- | --- | --- |
308
+
309
+ | **Default + Details** | **Hover** | **Selected** | **Disabled** |
310
+ | --- | --- | --- | --- |
311
+
312
+ ## Code
313
+
314
+ ## Dropdown Menu Component
315
+
316
+ | Below you will find interactive Storybook iframes for the Dropdown Menu. Tabs within each iframe provide a different set of tools: The Controls tab allows you to see and adjust all available props. The Actions tab shows you data displayed by event handler arguments. | | **Jump to component:** DropdownMenu |
317
+ | --- | --- | --- |
318
+
319
+ ---
320
+
321
+ >**NOTE:** The Dropdown Menu and its affiliated components have undergone a major overhaul in their construction and how they have been coded, rendering the documentation below out of date.
322
+ >
323
+ >While the SDS works on updating the documentation to reflect these changes, see `src/core/Dropdown/index.stories.tsx` within the `sci-components` codebase for example usage of the new Dropdown Menu structure.
324
+
325
+ >This component appears as it is in the default SDS codebase. Once imported into the CZ GEN EPI codebase, use a theme file to enable the CZ GEN EPI visual appearance customization.
326
+
327
+ ### DropdownMenu
328
+
329
+ Storybook
330
+
331
+ ---
332
+
333
+ Dropdown Menus are coupled with Dropdown Inputs, Dropdown Buttons, and in some cases Icon Buttons whenever those elements present the user with options to choose from or selections to make.
334
+
335
+ ## Overview
336
+
337
+ >**Warning:** The Dropdown Menu and its affiliated components have undergone a major overhaul in their construction and how they have been coded, rendering the documentation below out of date.
338
+ >
339
+ >While the SDS works on updating the documentation to reflect these changes, see `src/core/Dropdown/index.stories.tsx` within the `sci-components` codebase for example usage of the new Dropdown Menu structure.
340
+
341
+ ## Dropdown Menu Variants
342
+
343
+ | In their basic form, Dropdown Menus are composed of a Menu Container that is filled with Menu Items offering the user options to adjust or select that are contextually relevant to the Button or Dropdown Input they are coupled with. Menu Items can be optionally paired with additional details or with counters that indicate the number of items within that Menu Item's category. Dropdown Menus can also be multi-select, allowing the user to choose multiple options from the group of Menu Items. Additionally, Dropdown Menus can be configured to include a Search Input, have their Menu Items separated out into different sections or groupings, have a built-in title, or any combination of these features. | | **Jump to section:** Dropdown Menu Common Configurations Menu Item |
344
+ | --- | --- | --- |
345
+
346
+ ---
347
+
348
+ ## Dropdown Menu
349
+
350
+ | | In Figma | | | Meets Accessibility | | | In CZ ID Codebase |
351
+ | --- | --- | --- | --- | --- | --- | --- | --- |
352
+
353
+ ### Single-select Dropdown Menu
354
+
355
+ Use the Single-select Dropdown Menu when users are only able to select one item from the menu. They can be configured to have their Menu Items separated out into different sections or groupings, or have a built-in title; both of these features are established by the `[array]` passed into the `options` prop. Additionally, they can be configured to include a Search Input. See the Data Format section below for details around configuring the `[array]`.
356
+
357
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
358
+
359
+  
360
+
361
+ ---
362
+
363
+ ---
364
+
365
+ ---
366
+
367
+  
368
+
369
+ ### Multi-select Dropdown Menu
370
+
371
+ Use the Multi-select Dropdown Menu when users are able to select one or more items from the menu. They can be configured to have their Menu Items separated out into different sections or groupings, or have a built-in title; both of these features are established by the `[array]` passed into the `options` prop. Additionally, they can be configured to include a Search Input. See the Data Format section below for details around configuring the `[array]`.
372
+
373
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
374
+
375
+  
376
+
377
+ ---
378
+
379
+ ---
380
+
381
+ ---
382
+
383
+ ---
384
+
385
+ ---
386
+
387
+ ### Data Format
388
+
389
+ Use the following configuration for the `[array]` passed into the `options` prop:
390
+
391
+ ```js
392
+ data = [
393
+ {
394
+ // section with a Header and Menu Item Details
395
+ dataRows: [
396
+ {
397
+ name: "Menu Item",
398
+ details: "Details",
399
+ },
400
+ {
401
+ name: "Menu Item",
402
+ details: "Details",
403
+ },
404
+ {
405
+ name: "Menu Item",
406
+ details: "Details",
407
+ },
408
+ ],
409
+ label: "Section One",
410
+ },
411
+ {
412
+ // section with Menu Item Counts
413
+ dataRows: [
414
+ {
415
+ name: "Menu Item",
416
+ count: 20,
417
+ },
418
+ {
419
+ name: "Menu Item",
420
+ count: 40,
421
+ },
422
+ {
423
+ name: "Menu Item",
424
+ count: 60,
425
+ },
426
+ ],
427
+ },
428
+ ];
429
+ ```
430
+
431
+ ---
432
+
433
+ ### Dropdown Menu Spacing
434
+
435
+ These rules establish how much margin should exist between and around elements.
436
+
437
+ ---
438
+
439
+ ## Common Configurations
440
+
441
+ Dropdown Menus can be thought of as being comprised of different combinations of building blocks all within a Menu Container, ensuring a wide range of flexibility for whatever need a designer has with their designs.
442
+
443
+ Below are a number of common Dropdown Menu configurations, but by no means is this an exhaustive list:
444
+
445
+ | **Title + Count** | **Search + Sections + Details** | **Title + Details** |
446
+ | --- | --- | --- |
447
+
448
+ | **Search + Details** | **Title + Search + Count** | **Title + Sections + Details** |
449
+ | --- | --- | --- |
450
+
451
+ ---
452
+
453
+ ## Menu Item
454
+
455
+ | | In Figma | | | Meets Accessibility | | | In CZ ID Codebase |
456
+ | --- | --- | --- | --- | --- | --- | --- | --- |
457
+
458
+ ### Single-select Menu Item
459
+
460
+ Menu Items can be optionally paired with additional clarifying details, with counters that indicate the number of items within that Menu Item's category, or be disabled entirely. These details are established within the `[array]` passed into the `options` prop on the Dropdown Menu component.
461
+
462
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
463
+
464
+ | **Default** | **Hover** | **Selected** | **Disabled** |
465
+ | --- | --- | --- | --- |
466
+
467
+ | **Default + Count** | **Hover** | **Selected** | **Disabled** |
468
+ | --- | --- | --- | --- |
469
+
470
+ | **Default + Details** | **Hover** | **Selected** | **Disabled** |
471
+ | --- | --- | --- | --- |
472
+
473
+  
474
+
475
+ ### Multi-select Menu Item
476
+
477
+ Menu Items can be optionally paired with additional clarifying details, with counters that indicate the number of items within that Menu Item's category, or be disabled entirely. These details are established within the `[array]` passed into the `options` prop on the Dropdown Menu component.
478
+
479
+ Click on each state below to enter inspect view and get details on its code attributes and any global styles it uses.
480
+
481
+ | **Default** | **Hover** | **Selected** | **Disabled** |
482
+ | --- | --- | --- | --- |
483
+
484
+ | **Default + Count** | **Hover** | **Selected** | **Disabled** |
485
+ | --- | --- | --- | --- |
486
+
487
+ | **Default + Details** | **Hover** | **Selected** | **Disabled** |
488
+ | --- | --- | --- | --- |
489
+
490
+ ## Code
491
+
492
+ ## Dropdown Menu Component
493
+
494
+ | Below you will find interactive Storybook iframes for the Dropdown Menu. Tabs within each iframe provide a different set of tools: The Controls tab allows you to see and adjust all available props. The Actions tab shows you data displayed by event handler arguments. | | **Jump to component:** DropdownMenu |
495
+ | --- | --- | --- |
496
+
497
+ ---
498
+
499
+ >**NOTE:** The Dropdown Menu and its affiliated components have undergone a major overhaul in their construction and how they have been coded, rendering the documentation below out of date.
500
+ >
501
+ >While the SDS works on updating the documentation to reflect these changes, see `src/core/Dropdown/index.stories.tsx` within the `sci-components` codebase for example usage of the new Dropdown Menu structure.
502
+
503
+ ### DropdownMenu
504
+
505
+ Storybook
506
+
507
+  
508
+
509
+ ---
510
+
511
+ Dropdown Menus are coupled with Dropdown Inputs, Dropdown Buttons, and in some cases Icon Buttons whenever those elements present the user with options to choose from or selections to make.
512
+
513
+ ## Guidelines
514
+
515
+ ## Dropdown Menu Components
516
+
517
+ | Dropdown Menus offer the user options to adjust or select. The Menu Items found within the Dropdown Menu have multiple format options. Dropdown Menus can be configured to include a Search Input, have their Menu Items separated out into different sections or groupings, have a built-in title, or any combination of these features. Dropdown Menus require a trigger element, such as a Dropdown Input, Dropdown Button, or Icon Button, to open the menu. Dropdown is a component created as a combination of Dropdown Menu and a trigger element to help make Dropdown Menu easier for developers to implement. | | **Jump to component:** Dropdown Dropdown Menu Menu Item |
518
+ | --- | --- | --- |
519
+
520
+ ### Visual Preview
521
+
522
+ ---
523
+
524
+ ## Dropdown
525
+
526
+ | | Meets Accessibility | | | In Code |
527
+ | --- | --- | --- | --- | --- |
528
+
529
+ ### Overview
530
+
531
+ Dropdown is a component that exists only in the codebase (not in Figma) that is a combination of Dropdown Menu and a trigger element to help make Dropdown Menu easier for developers to implement. Trigger elements can be Dropdown Inputs, Dropdown Buttons, or Icon Buttons.
532
+
533
+ Dropdown bundles Dropdown Input with Dropdown Menu into a single component with all of the necessary React handlers set up to speed up the implementation process. Since there is no equivalent component in Figma, designers must manually combine Dropdown Menu with their chosen trigger element.
534
+
535
+ ---
536
+
537
+ ### Live Instance
538
+
539
+ Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
540
+
541
+ Default
542
+
543
+ ---
544
+
545
+ ## Dropdown Menu
546
+
547
+ | | In Figma | | | Meets Accessibility | | | In Code |
548
+ | --- | --- | --- | --- | --- | --- | --- | --- |
549
+
550
+ ### Dropdown Menu Types
551
+
552
+ ---
553
+
554
+ ### Live Instance
555
+
556
+ Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
557
+
558
+ Default
559
+
560
+ ---
561
+
562
+ ### Dropdown Menu – Single-select
563
+
564
+ Use Single-select when users are only able to select one item from the menu. They can be configured to have their Menu Items separated out into different sections or groupings, have a built-in title at the top of the menu, include a Search Input allowing users to easily find specific Menu Items, or a combination of all three.
565
+
566
+ | Default | Default + Title | Default + Search | Default + Sections |
567
+ | --- | --- | --- | --- |
568
+
569
+ ---
570
+
571
+ ### Dropdown Menu – Multi-select
572
+
573
+ Use Multi-select when users are able to select one or more items from the menu. They can be configured to have their Menu Items separated out into different sections or groupings, have a built-in title at the top of the menu, include a Search Input allowing users to easily find specific Menu Items, include confirmation and cancel Buttons at the bottom of the menu, or a combination of all four.
574
+
575
+ | Default | Default + Title | Default + Search | Default + Sections | Default + Buttons |
576
+ | --- | --- | --- | --- | --- |
577
+
578
+ ---
579
+
580
+ ### Dropdown Menu – Multi-column
581
+
582
+ Use Multi-column when there is a need to separate Menu Items across multiple columns. Columns can be dependent or independent of each other. With dependent columns, Menu Item selections made in one column will affect the Menu Items in the others, such as reducing the number of Menu Items visible in neighboring columns; use Indeterminate Menu Items when some, but not all sub-options have been made in a subsequent column. With independent columns, Menu Item selections made in one column will have no effect on the Menu Items in other columns.
583
+
584
+ The same configurations are available in the Multi-column variant as the Single-column including a built-in title at the top of the menu, a Search Input, section names at the top of each column, confirmation and cancel Buttons at the bottom of the menu, or a combination of all four. There can only be one section per column. Additionally, a Chevron icon can be placed between columns to help communicate the relationship between them.
585
+
586
+ Multi-column Dropdown Menus can be Single- or Multi-select.
587
+
588
+ | Default | Default + Title | Default + Search | Default + Buttons |
589
+ | --- | --- | --- | --- |
590
+
591
+ ---
592
+
593
+ ### Dropdown Menu Spacing
594
+
595
+ These rules establish how much margin should exist between and around elements.
596
+
597
+ ---
598
+
599
+ ## Menu Item
600
+
601
+ | | In Figma | | | Meets Accessibility | | | In Code |
602
+ | --- | --- | --- | --- | --- | --- | --- | --- |
603
+
604
+ ### Menu Item Types
605
+
606
+ ---
607
+
608
+ ### Live Instance
609
+
610
+ Interact with the live instance below and adjust the available props to better conceptualize how it will appear in product:
611
+
612
+ Default
613
+
614
+ ---
615
+
616
+ ### Menu Item – Single-select
617
+
618
+ Single-select Menu Items are used in Dropdown Menus where only one item can be selected at a time. They can optionally be paired with additional clarifying details or counters that indicate the number of items within that Menu Item's category.
619
+
620
+ | Default | Hover | Selected | Disabled |
621
+ | --- | --- | --- | --- |
622
+
623
+ ---
624
+
625
+ ### Menu Item – Multi-select
626
+
627
+ #### Determinate
628
+
629
+ Multi-select Menu Items are used in Dropdown Menus where multiple items can be selected at a time. The determinate variant should be used in Single-column Dropdown Menus or in Multi-column Dropdown Menus to communicate that all possible sub-options have been selected. Optionally They can optionally be paired with additional clarifying details or with counters that indicate the number of items within that Menu Item's category.
630
+
631
+ | Default | Hover | Selected | Disabled |
632
+ | --- | --- | --- | --- |
633
+
634
+ #### Indeterminate
635
+
636
+ Indeterminate Multi-select Menu Items are used **only** in Multi-column Dropdown Menus when there is a need to show when some, but not all sub-options have been selected. They can optionally be paired with additional clarifying details or with counters that indicate the number of items within that Menu Item's category.
637
+
638
+ | Default | Hover | Selected | Disabled |
639
+ | --- | --- | --- | --- |
640
+
641
+ ---
642
+
643
+ ### Menu Item with Icon
644
+
645
+ All Menu Item variants can be optionally displayed with an icon.
646
+
647
+ | Default + Icon | Default + Count + Icon | Default + Details + Icon |
648
+ | --- | --- | --- |
649
+
650
+ ## Code
651
+
652
+ ## Dropdown Menu Components
653
+
654
+ | `DropdownMenu` offers the user options to adjust or select. The `MenuItems` found within `DropdownMenu` have multiple format options. `DropdownMenu` can be configured to include `InputSearch`, have their `MenuItems` separated out into different sections or groupings, have a built-in title, or any combination of these features. `DropdownMenu` requires a trigger element, such as `InputDropdown`, `ButtonDropdown`, or `ButtonIcon`, to open the menu. `Dropdown` is a component created as a combination of `DropdownMenu` and a trigger element to help make `DropdownMenu` easier for developers to implement. | | **Jump to component:** DropdownMenu MenuItem Dropdown |
655
+ | --- | --- | --- |
656
+
657
+ ### Visual Preview
658
+
659
+ >**Note:** The code examples below must install dependencies before displaying and may take extra time to load
660
+
661
+ ---
662
+
663
+ ## Dropdown
664
+
665
+ ### SDS Source Code
666
+
667
+ The component's source code in the SDS codebase can be found here.
668
+
669
+ ### SDS vs MUI
670
+
671
+ SDS's `Dropdown` component is a combination of two other SDS components: `DropdownMenu` and `InputDropdown`, since these would often be used together to create a complete dropdown element in the UI. `DropdownMenu` is built off of MUI's `Autocomplete` component, but `InputDropdown` is built from scratch.
672
+
673
+ These are a few of the most relevant ways in which SDS's `DropdownMenu` (which is used within `Dropdown`) integrates with`Autocomplete` and other MUI components:
674
+
675
+ * SDS `Dropdown` uses MUI `Autocomplete` under the hood, so all props passed to `DropdownMenu` are ultimately passed to the Autocomplete component as well. This means MUI's `Autocomplete` props can be used on SDS's `DropdownMenu` if needed.
676
+ * SDS `Dropdown` uses MUI `Popper`. A custom `Popper` component can be passed via `props.PopperComponent` and/or `props.PopperBaseProps` to control the popper component.
677
+ * Similarly, SDS `Dropdown` uses MUI `Paper`. A custom `Paper` component can be passed via `props.PaperComponent` to enable custom styling.
678
+
679
+ ### Props
680
+
681
+ Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
682
+
683
+ | Name | Type | Default | Description |
684
+ | --- | --- | --- | --- |
685
+ | `buttonPosition` | `"left"` `|"right"` | `left` | Specifies the position of the `Apply`/`Cancel` buttons at the Dropdown footer. |
686
+ | `buttons` | `bool` | `false` | When set to `true`, a set of `Apply`/`Cancel` buttons will be displayed at the Dropdown footer. |
687
+ | `closeOnBlur` | `bool` | `true` | When set to `true`, the Dropdown will automatically close when a click occurs outside of it. |
688
+ | `disabled` | `bool` | `false` | When `true`, the Dropdown is disabled and user interaction is not allowed. |
689
+ | `isTriggerChangeOnOptionClick` | `bool` | `false` | When set to `true`, changes in Dropdown selections are applied in real-time to the react state. |
690
+ | `label` | `string` | `-` | The label of the Dropdown. |
691
+ | `multiple` | `bool` | `true` | Determines whether the Dropdown allows for multiple item selection. |
692
+ | `onChange` | `(options: Value<DefaultDropdownMenuOption, Multiple>) => void;` | `-` | Callback fired when the value changes. |
693
+ | `onClose` | `func` | `-` | Callback fired when the Dropdown requests to be closed. |
694
+ | `search` | `bool` | `true` | When set to `true`, a search input is displayed at the top of the Dropdown for filtering options. |
695
+ | `dropdownMenuProps` | `Partial<DropdownMenuProps>` | `-` | These props are directly passed to the underlying DropdownMenu component, customizing its behavior. |
696
+ | `options` | `Array<DefaultDropdownMenuOption>` | `-` | An array of options to be displayed as MenuItems within the Dropdown. |
697
+ | `InputDropdownProps` | `Partial<InputDropdownPropsType>` | `-` | These props are directly passed to the underlying InputDropdown component, customizing its behavior. |
698
+ | `value` | `string | Array<string>` | `-` | The current value of the Dropdown, representing the selected item(s) based on the multi-select status. |
699
+ | `PopperComponent` | `element` | `Paper` | The component used to render the wrapper of the inner DropdownMenu. |
700
+ | `InputDropdownComponent` | `element` | `InputDropdown` | The component used to render the Dropdown trigger button. |
701
+
702
+ ### Code Examples
703
+
704
+ #### **Default Dropdown**
705
+
706
+ This example illustrates the minimum set of props required for rendering a `Dropdown` component.
707
+
708
+ **/styles.css**
709
+
710
+ ```css
711
+ body {
712
+ font-size: 13px;
713
+ font-weight: 400;
714
+ letter-spacing: 0.08px;
715
+ line-height: 20px;
716
+ text-transform: none;
717
+ font-family: "Inter", sans-serif;
718
+ }
719
+
720
+ h1 {
721
+ font-size: 1.5rem;
722
+ }
723
+
724
+ .app {
725
+ padding: 20px;
726
+ }
727
+ ```
728
+
729
+ **tsconfig.json**
730
+
731
+ ```json
732
+ {
733
+ "include": [
734
+ "./**/*"
735
+ ],
736
+ "compilerOptions": {
737
+ "strict": true,
738
+ "esModuleInterop": true,
739
+ "lib": [ "dom", "es2015" ],
740
+ "jsx": "react-jsx"
741
+ }
742
+ }
743
+ ```
744
+
745
+ **/App.tsx**
746
+
747
+ ```tsx
748
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
749
+ import { Dropdown, DefaultDropdownMenuOption } from "@czi-sds/components";
750
+ import "./styles.css";
751
+
752
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
753
+ {
754
+ name: "Menu item 1",
755
+ },
756
+ {
757
+ name: "Menu item 2",
758
+ },
759
+ {
760
+ name: "Menu item 3",
761
+ },
762
+ {
763
+ name: "Menu item 4",
764
+ },
765
+ ];
766
+
767
+ function App() {
768
+ return (
769
+ <div className="app">
770
+ <Dropdown
771
+ label="Click Target"
772
+ onChange={() => {}}
773
+ options={MENU_ITEMS}
774
+ />
775
+ </div>
776
+ );
777
+ }
778
+
779
+ export default App;
780
+ ```
781
+
782
+ **/index.tsx**
783
+
784
+ ```tsx
785
+ import React from "react";
786
+ import { createRoot } from "react-dom/client";
787
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
788
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
789
+ import { Theme } from "@czi-sds/components";
790
+ import CssBaseline from "@mui/material/CssBaseline";
791
+ import useMediaQuery from "@mui/material/useMediaQuery";
792
+
793
+ import App from "./App";
794
+
795
+ const container = document.getElementById("root");
796
+ const root = createRoot(container!);
797
+
798
+ const RootApp = () => {
799
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
800
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
801
+
802
+ return (
803
+ <StyledEngineProvider injectFirst>
804
+ <ThemeProvider theme={theme}>
805
+ <EmotionThemeProvider theme={theme}>
806
+ <CssBaseline />
807
+ <App />
808
+ </EmotionThemeProvider>
809
+ </ThemeProvider>
810
+ </StyledEngineProvider>
811
+ );
812
+ };
813
+
814
+ root.render(
815
+ <React.StrictMode>
816
+ <RootApp />
817
+ </React.StrictMode>
818
+ );
819
+
820
+ ```
821
+
822
+ **/public/index.html**
823
+
824
+ ```html
825
+ <!DOCTYPE html>
826
+ <html lang="en">
827
+ <head>
828
+ <meta charset="UTF-8" />
829
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
830
+ <title>Dropdown</title>
831
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
832
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
833
+ <link
834
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
835
+ rel="stylesheet"
836
+ />
837
+ <link
838
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
839
+ rel="stylesheet"
840
+ />
841
+ </head>
842
+ <body>
843
+ <div id="root"></div>
844
+ </body>
845
+ </html>
846
+
847
+ ```
848
+
849
+ **/package.json**
850
+
851
+ ```json
852
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
853
+ ```
854
+
855
+ ---
856
+
857
+ #### **Multi-select Dropdown with Search**
858
+
859
+ A search feature can be easily incorporated into the `Dropdown` component by including the `search` prop.
860
+
861
+ The search functionality operates by dynamically filtering out non-matching options as the user types, displaying only the menu items whose text corresponds to that input. If no matching items are found, the `Dropdown` will automatically present "No options" below the search bar.
862
+
863
+ To ensure that the search input retains its text even after a user selects a menu item (when multiple selections are allowed), set the `keepSearchOnSelect` prop to `true` within the `DropdownMenuProps` prop. This allows users to conveniently choose multiple menu items that match the searched term without the need to re-enter it for each selection.
864
+
865
+ **/styles.css**
866
+
867
+ ```css
868
+ body {
869
+ font-size: 13px;
870
+ font-weight: 400;
871
+ letter-spacing: 0.08px;
872
+ line-height: 20px;
873
+ text-transform: none;
874
+ font-family: "Inter", sans-serif;
875
+ }
876
+
877
+ h1 {
878
+ font-size: 1.5rem;
879
+ }
880
+
881
+ .app {
882
+ padding: 20px;
883
+ }
884
+ ```
885
+
886
+ **tsconfig.json**
887
+
888
+ ```json
889
+ {
890
+ "include": [
891
+ "./**/*"
892
+ ],
893
+ "compilerOptions": {
894
+ "strict": true,
895
+ "esModuleInterop": true,
896
+ "lib": [ "dom", "es2015" ],
897
+ "jsx": "react-jsx"
898
+ }
899
+ }
900
+ ```
901
+
902
+ **/App.tsx**
903
+
904
+ ```tsx
905
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
906
+ import { Dropdown, DefaultDropdownMenuOption } from "@czi-sds/components";
907
+ import "./styles.css";
908
+
909
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
910
+ {
911
+ name: "Fruit: Apple",
912
+ section: "Fruit",
913
+ count: 10
914
+ },
915
+ {
916
+ name: "Fruit: Cherry",
917
+ section: "Fruit",
918
+ count: 150
919
+ },
920
+ {
921
+ name: "Fruit: Orange",
922
+ section: "Fruit",
923
+ count: 15
924
+ },
925
+ {
926
+ name: "Vegetable: Carrot",
927
+ section: "Vegetable",
928
+ count: 34
929
+ },
930
+ {
931
+ name: "Vegetable: Kale",
932
+ section: "Vegetable"
933
+ },
934
+ {
935
+ name: "Vegetable: Lettuce",
936
+ section: "Vegetable"
937
+ },
938
+ ];
939
+
940
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
941
+
942
+ function handleClickAway(){}
943
+
944
+ function App() {
945
+ const ref = useRef(null);
946
+ const [open, setOpen] = useState(false);
947
+
948
+ useEffect(() => {
949
+ if (ref.current) setOpen(true);
950
+ }, [ref.current])
951
+
952
+ return (
953
+ <div className="app">
954
+ <Dropdown
955
+ label="Click Target"
956
+ onChange={() => {}}
957
+ options={MENU_ITEMS}
958
+ search
959
+ multiple
960
+ DropdownMenuProps={{
961
+ keepSearchOnSelect: true,
962
+ groupBy: (option: DefaultDropdownMenuOption) =>
963
+ option.section as string,
964
+ }}
965
+ />
966
+ </div>
967
+ );
968
+ }
969
+
970
+ export default App;
971
+ ```
972
+
973
+ **/index.tsx**
974
+
975
+ ```tsx
976
+ import React from "react";
977
+ import { createRoot } from "react-dom/client";
978
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
979
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
980
+ import { Theme } from "@czi-sds/components";
981
+ import CssBaseline from "@mui/material/CssBaseline";
982
+ import useMediaQuery from "@mui/material/useMediaQuery";
983
+
984
+ import App from "./App";
985
+
986
+ const container = document.getElementById("root");
987
+ const root = createRoot(container!);
988
+
989
+ const RootApp = () => {
990
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
991
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
992
+
993
+ return (
994
+ <StyledEngineProvider injectFirst>
995
+ <ThemeProvider theme={theme}>
996
+ <EmotionThemeProvider theme={theme}>
997
+ <CssBaseline />
998
+ <App />
999
+ </EmotionThemeProvider>
1000
+ </ThemeProvider>
1001
+ </StyledEngineProvider>
1002
+ );
1003
+ };
1004
+
1005
+ root.render(
1006
+ <React.StrictMode>
1007
+ <RootApp />
1008
+ </React.StrictMode>
1009
+ );
1010
+
1011
+ ```
1012
+
1013
+ **/public/index.html**
1014
+
1015
+ ```html
1016
+ <!DOCTYPE html>
1017
+ <html lang="en">
1018
+ <head>
1019
+ <meta charset="UTF-8" />
1020
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1021
+ <title>Dropdown</title>
1022
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1023
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1024
+ <link
1025
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1026
+ rel="stylesheet"
1027
+ />
1028
+ <link
1029
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1030
+ rel="stylesheet"
1031
+ />
1032
+ </head>
1033
+ <body>
1034
+ <div id="root"></div>
1035
+ </body>
1036
+ </html>
1037
+
1038
+ ```
1039
+
1040
+ **/package.json**
1041
+
1042
+ ```json
1043
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
1044
+ ```
1045
+
1046
+ ---
1047
+
1048
+ ---
1049
+
1050
+ ## DropdownMenu
1051
+
1052
+ ### SDS Source Code
1053
+
1054
+ The component's source code in the SDS codebase can be found here.
1055
+
1056
+ ### SDS vs MUI
1057
+
1058
+ Both SDS's `DropdownMenu` and MUI's `Autocomplete` have huge API surfaces, so it is not practical to list every way in which they differ (in part because the list would likely become obsolete quickly!). In summary, these are a few of the most relevant ways in which `DropdownMenu` integrates with `Autocomplete` and other MUI components:
1059
+
1060
+ * SDS `DropdownMenu` uses MUI `Autocomplete` under the hood, so all props passed to `DropdownMenu` are ultimately passed to the `Autocomplete` component as well. This means MUI's `Autocomplete` props can be used on SDS's `DropdownMenu` if needed.
1061
+ * SDS `DropdownMenu` uses MUI `Popper`. A custom `Popper` component can be passed via `props.PopperComponent` and/or `props.PopperBaseProps` to control the popper component.
1062
+ * Similarly, SDS `DropdownMenu` uses MUI `Paper`. A custom `Paper` component can be passed via `props.PaperComponent` to enable custom styling.
1063
+
1064
+ ### MUI Documentation
1065
+
1066
+ Documentation for the underlying MUI `Autocomplete` component can be found here.
1067
+
1068
+ ### Props
1069
+
1070
+ Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
1071
+
1072
+ | **Name** | **Type** | **Default** | **Description** |
1073
+ | --- | --- | --- | --- |
1074
+ | `anchorEl` | `HTML element` `| object` `| func` | `null` | Tells the DropdownMenu which element to use as an anchor; see more in the MUI `Popper` component's API |
1075
+ | `clickAwayListenerProps` | `func` | `-` | Passes props to the MUI `ClickAwayListener` |
1076
+ | `groupBy` | `func` | `-` | Groups menu items according to their assigned `section` |
1077
+ | `isOptionEqualToValue` | `func` | `(option, val): boolean {` `return option.name === val.name;` `}` | Changes how a selected value is compared against the available options; see more in the MUI Autocomplete API or in the DropdownMenu Component Recipes section |
1078
+ | `keepSearchOnSelect` | `bool` | `true` | Maintains a search input when a user selects a menu item (only if multiple selections are allowed via the `multiple` prop; otherwise the dropdown closes upon selection) |
1079
+ | `multiple` | `bool` | `false` | Allows user to select multiple menu items; see more in the MUI Autocomplete API |
1080
+ | `noOptionsText` | `string` | `"No options"` | Sets the text that displays if there are no matching search results; see more in the MUI Autocomplete API |
1081
+ | `onClickAway` | `func` | `-` | Sets what happens when a user clicks outside of the DropdownMenu |
1082
+ | `onInputChange` | `func` | `noop` | Specifies what happens when a new selection is made; see more in the MUI Autocomplete API or in the DropdownMenu Component Recipes section |
1083
+ | `PopperBaseProps` | | `-` | Passes custom props to the MUI`Popper` component, which is included as part of the SDS`DropdownMenu` component. Learn more in the Popper documentation, and see all the props available to pass to it on the Popper API page. See an example of `PopperBaseProps` in use in the DropdownMenu Component Recipes section |
1084
+ | `PopperComponent` | `func` | SDS's `StyledPopper`; see sci-components/packages/components/src/core/DropdownMenu/style.ts, ~ line 202 | The MUI `Popper` component is included as part of the SDS `DropdownMenu` component, and its styles can be changed or added to |
1085
+ | `search` | `bool` | `f``alse` | Provides a search bar at the top of the menu from which users can narrow the list of menu items |
1086
+ | `title` | `string` | `-` | Sets a heading for the menu |
1087
+
1088
+ There are many more props that can be used with the `DropdownMenu` component, via those available to MUI's `Autocomplete` component.
1089
+
1090
+ >**Note for use with SDS** **`Table`** **and other components:**
1091
+ >
1092
+ >Each time a table column is shown/hidden a new table object is created. This causes components within the table to remount. *This can be a problem with* *`DropdownMenu`* *if the menu is supposed to stay open while the columns are hidden/shown because the anchor element also gets recreated.* To resolve this, move the `DropdownMenu` anchor element outside of the table or use the `pendingState` and only show/hide the columns when closing the dropdown.
1093
+
1094
+ ### Code Examples
1095
+
1096
+ #### Default DropdownMenu
1097
+
1098
+ This example has the minimum props and menu item metadata needed for the `DropdownMenu`.
1099
+
1100
+ Menu item metadata is stored as an array of objects (one per menu item) passed to the `DropdownMenu` component’s `options` prop. At minimum each `MenuItem` needs a `name` property, which is the text it displays. `MenuItems` can display more metadata, though; see the “DropdownMenu with grouped sections” and “DropdownMenu with menu items that have count, icon, and / or details” examples below. Alternatively, a custom component can be displayed instead of a `MenuItem` component; see the “DropdownMenu with components in place of menu items” example below.
1101
+
1102
+ The minimum width for the `DropdownMenu` is 225px, but longer text will automatically widen the `DropdownMenu` up to the length of the text provided.
1103
+
1104
+ Although examples shows the minimum requirements for the `DropdownMenu`, there are many more options available to include; see subsequent code examples further below.
1105
+
1106
+ **/styles.css**
1107
+
1108
+ ```css
1109
+ body {
1110
+ font-size: 13px;
1111
+ font-weight: 400;
1112
+ letter-spacing: 0.08px;
1113
+ line-height: 20px;
1114
+ text-transform: none;
1115
+ font-family: "Inter", sans-serif;
1116
+ }
1117
+
1118
+ h1 {
1119
+ font-size: 1.5rem;
1120
+ }
1121
+
1122
+ .app {
1123
+ padding: 20px;
1124
+ }
1125
+ ```
1126
+
1127
+ **tsconfig.json**
1128
+
1129
+ ```json
1130
+ {
1131
+ "include": [
1132
+ "./**/*"
1133
+ ],
1134
+ "compilerOptions": {
1135
+ "strict": true,
1136
+ "esModuleInterop": true,
1137
+ "lib": [ "dom", "es2015" ],
1138
+ "jsx": "react-jsx"
1139
+ }
1140
+ }
1141
+ ```
1142
+
1143
+ **/App.tsx**
1144
+
1145
+ ```tsx
1146
+ // Most minimal DropdownMenu (just has the basic requirements)
1147
+
1148
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1149
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1150
+ import "./styles.css";
1151
+
1152
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1153
+ {
1154
+ name: "Menu item 1",
1155
+ },
1156
+ {
1157
+ name: "Menu item 2",
1158
+ },
1159
+ {
1160
+ name: "Menu item 3",
1161
+ },
1162
+ {
1163
+ name: "Longer menu item than the others",
1164
+ },
1165
+ ];
1166
+
1167
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
1168
+
1169
+ function handleClickAway(){}
1170
+
1171
+ function App() {
1172
+ const ref = useRef(null);
1173
+ const [open, setOpen] = useState(false);
1174
+
1175
+ useEffect(() => {
1176
+ if (ref.current) setOpen(true);
1177
+ }, [ref.current])
1178
+
1179
+ return (
1180
+ <div className="app" style={{paddingLeft: "10px"}}>
1181
+ <div ref={ref} />
1182
+ {open &&
1183
+ (
1184
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1185
+ <DropdownMenu
1186
+ PopperBaseProps={POPPER_BASE_PROPS}
1187
+ anchorEl={ref.current}
1188
+ onClickAway={handleClickAway}
1189
+ open={open}
1190
+ options={MENU_ITEMS}
1191
+ />
1192
+ )
1193
+ }
1194
+ </div>
1195
+ );
1196
+ }
1197
+
1198
+ export default App;
1199
+ ```
1200
+
1201
+ **/index.tsx**
1202
+
1203
+ ```tsx
1204
+ import React from "react";
1205
+ import { createRoot } from "react-dom/client";
1206
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1207
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1208
+ import { Theme } from "@czi-sds/components";
1209
+ import CssBaseline from "@mui/material/CssBaseline";
1210
+ import useMediaQuery from "@mui/material/useMediaQuery";
1211
+
1212
+ import App from "./App";
1213
+
1214
+ const container = document.getElementById("root");
1215
+ const root = createRoot(container!);
1216
+
1217
+ const RootApp = () => {
1218
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1219
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1220
+
1221
+ return (
1222
+ <StyledEngineProvider injectFirst>
1223
+ <ThemeProvider theme={theme}>
1224
+ <EmotionThemeProvider theme={theme}>
1225
+ <CssBaseline />
1226
+ <App />
1227
+ </EmotionThemeProvider>
1228
+ </ThemeProvider>
1229
+ </StyledEngineProvider>
1230
+ );
1231
+ };
1232
+
1233
+ root.render(
1234
+ <React.StrictMode>
1235
+ <RootApp />
1236
+ </React.StrictMode>
1237
+ );
1238
+
1239
+ ```
1240
+
1241
+ **/public/index.html**
1242
+
1243
+ ```html
1244
+ <!DOCTYPE html>
1245
+ <html lang="en">
1246
+ <head>
1247
+ <meta charset="UTF-8" />
1248
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1249
+ <title>Dropdown</title>
1250
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1251
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1252
+ <link
1253
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1254
+ rel="stylesheet"
1255
+ />
1256
+ <link
1257
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1258
+ rel="stylesheet"
1259
+ />
1260
+ </head>
1261
+ <body>
1262
+ <div id="root"></div>
1263
+ </body>
1264
+ </html>
1265
+
1266
+ ```
1267
+
1268
+ **/package.json**
1269
+
1270
+ ```json
1271
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
1272
+ ```
1273
+
1274
+ ---
1275
+
1276
+ #### DropdownMenu with custom click target
1277
+
1278
+ The example below shows how `DropdownMenu` can be used in conjunction with a click target component (in this case, the `InputDropdown` component). When the click target is clicked, the `DropdownMenu` opens, and when a `MenuItem` is clicked, the `DropdownMenu` closes.
1279
+
1280
+ **/styles.css**
1281
+
1282
+ ```css
1283
+ body {
1284
+ font-size: 13px;
1285
+ font-weight: 400;
1286
+ letter-spacing: 0.08px;
1287
+ line-height: 20px;
1288
+ text-transform: none;
1289
+ font-family: "Inter", sans-serif;
1290
+ }
1291
+
1292
+ h1 {
1293
+ font-size: 1.5rem;
1294
+ }
1295
+
1296
+ .app {
1297
+ padding: 20px;
1298
+ }
1299
+ ```
1300
+
1301
+ **tsconfig.json**
1302
+
1303
+ ```json
1304
+ {
1305
+ "include": [
1306
+ "./**/*"
1307
+ ],
1308
+ "compilerOptions": {
1309
+ "strict": true,
1310
+ "esModuleInterop": true,
1311
+ "lib": [ "dom", "es2015" ],
1312
+ "jsx": "react-jsx"
1313
+ }
1314
+ }
1315
+ ```
1316
+
1317
+ **/App.tsx**
1318
+
1319
+ ```tsx
1320
+ // DropdownMenu with custom click target
1321
+
1322
+ import React, { SyntheticEvent, useState, useRef } from "react";
1323
+ import { DropdownMenu, DefaultDropdownMenuOption, InputDropdown } from "@czi-sds/components";
1324
+ import "./styles.css";
1325
+
1326
+ function App() {
1327
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1328
+ {
1329
+ name: "Menu item 1",
1330
+ },
1331
+ {
1332
+ name: "Menu item 2",
1333
+ },
1334
+ {
1335
+ name: "Menu item 3",
1336
+ },
1337
+ ];
1338
+
1339
+ const [anchorEl, setAnchorEl] = useState(null);
1340
+ const [open, setOpen] = useState(false);
1341
+ const [value, setValue] = useState(null);
1342
+
1343
+ function handleClick(event) {
1344
+ if (open) {
1345
+ setOpen(false);
1346
+ if (anchorEl) {
1347
+ anchorEl.focus();
1348
+ }
1349
+ setAnchorEl(null);
1350
+ } else {
1351
+ setAnchorEl(event.currentTarget);
1352
+ setOpen(true);
1353
+ }
1354
+ }
1355
+
1356
+ function handleChange(_, newValue) {
1357
+ setOpen(false);
1358
+ setValue(newValue);
1359
+ }
1360
+
1361
+ function handleClickAway() {
1362
+ return open && setOpen(false);
1363
+ }
1364
+
1365
+ return (
1366
+ <div className="app">
1367
+ <InputDropdown
1368
+ onClick={handleClick}
1369
+ label="Click Target"
1370
+ sdsType="label"
1371
+ sdsStyle="minimal"
1372
+ />
1373
+ <DropdownMenu
1374
+ options={MENU_ITEMS}
1375
+ anchorEl={anchorEl}
1376
+ open={!!open}
1377
+ onChange={handleChange}
1378
+ value={value}
1379
+ onClickAway={handleClickAway}
1380
+ />
1381
+ </div>
1382
+ );
1383
+ }
1384
+
1385
+ export default App;
1386
+ ```
1387
+
1388
+ **/index.tsx**
1389
+
1390
+ ```tsx
1391
+ import React from "react";
1392
+ import { createRoot } from "react-dom/client";
1393
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1394
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1395
+ import { Theme } from "@czi-sds/components";
1396
+ import CssBaseline from "@mui/material/CssBaseline";
1397
+ import useMediaQuery from "@mui/material/useMediaQuery";
1398
+
1399
+ import App from "./App";
1400
+
1401
+ const container = document.getElementById("root");
1402
+ const root = createRoot(container!);
1403
+
1404
+ const RootApp = () => {
1405
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1406
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1407
+
1408
+ return (
1409
+ <StyledEngineProvider injectFirst>
1410
+ <ThemeProvider theme={theme}>
1411
+ <EmotionThemeProvider theme={theme}>
1412
+ <CssBaseline />
1413
+ <App />
1414
+ </EmotionThemeProvider>
1415
+ </ThemeProvider>
1416
+ </StyledEngineProvider>
1417
+ );
1418
+ };
1419
+
1420
+ root.render(
1421
+ <React.StrictMode>
1422
+ <RootApp />
1423
+ </React.StrictMode>
1424
+ );
1425
+
1426
+ ```
1427
+
1428
+ **/public/index.html**
1429
+
1430
+ ```html
1431
+ <!DOCTYPE html>
1432
+ <html lang="en">
1433
+ <head>
1434
+ <meta charset="UTF-8" />
1435
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1436
+ <title>Dropdown</title>
1437
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1438
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1439
+ <link
1440
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1441
+ rel="stylesheet"
1442
+ />
1443
+ <link
1444
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1445
+ rel="stylesheet"
1446
+ />
1447
+ </head>
1448
+ <body>
1449
+ <div id="root"></div>
1450
+ </body>
1451
+ </html>
1452
+
1453
+ ```
1454
+
1455
+ **/package.json**
1456
+
1457
+ ```json
1458
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
1459
+ ```
1460
+
1461
+ ---
1462
+
1463
+ #### DropdownMenu with search
1464
+
1465
+ An `InputSearch` component can be incorporated into the `DropdownMenu` component by passing it the `search` prop.
1466
+
1467
+ The search functions by filtering out non-matches as the user types, leaving only menu items with text that match the input. If no matches are found, the `DropdownMenu` displays “No options” below the `InputSearch` by default. This text can be changed via the `noOptionsText` prop as shown below.
1468
+
1469
+ By default, the search’s input text is maintained even when a user selects a `MenuItem` (if multiple selections are allowed; otherwise the dropdown closes upon selection). This way users can select multiple `MenuItems` that match the searched term without needing to re-enter it each time. However, this can be changed so that the search input clears upon making a selection; see the “Dropdown with search and multi-select; does *not* maintain searched query upon selection of a menu item” example below.
1470
+
1471
+ 1. In the search box, type in "fruit"
1472
+ 2. `DropdownMenu` will display "Fruit: Apple" and "Fruit: Orange"
1473
+ 3. Select "Fruit: Apple"
1474
+ 4. Note that the search text is maintained along with the limited list of items
1475
+
1476
+ **/styles.css**
1477
+
1478
+ ```css
1479
+ body {
1480
+ font-size: 13px;
1481
+ font-weight: 400;
1482
+ letter-spacing: 0.08px;
1483
+ line-height: 20px;
1484
+ text-transform: none;
1485
+ font-family: "Inter", sans-serif;
1486
+ }
1487
+
1488
+ h1 {
1489
+ font-size: 1.5rem;
1490
+ }
1491
+
1492
+ .app {
1493
+ padding: 20px;
1494
+ }
1495
+ ```
1496
+
1497
+ **tsconfig.json**
1498
+
1499
+ ```json
1500
+ {
1501
+ "include": [
1502
+ "./**/*"
1503
+ ],
1504
+ "compilerOptions": {
1505
+ "strict": true,
1506
+ "esModuleInterop": true,
1507
+ "lib": [ "dom", "es2015" ],
1508
+ "jsx": "react-jsx"
1509
+ }
1510
+ }
1511
+ ```
1512
+
1513
+ **/App.tsx**
1514
+
1515
+ ```tsx
1516
+ // DropdownMenu with search
1517
+
1518
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1519
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1520
+ import "./styles.css";
1521
+
1522
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1523
+ {
1524
+ name: "Fruit: Apple",
1525
+ },
1526
+ {
1527
+ name: "Fruit: Orange",
1528
+ },
1529
+ {
1530
+ name: "Vegetable: Carrot",
1531
+ },
1532
+ {
1533
+ name: "Vegetable: Kale",
1534
+ },
1535
+ ];
1536
+
1537
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
1538
+
1539
+ function handleClickAway(){}
1540
+
1541
+ function App() {
1542
+ const ref = useRef(null);
1543
+ const [open, setOpen] = useState(false);
1544
+
1545
+ useEffect(() => {
1546
+ if (ref.current) setOpen(true);
1547
+ }, [ref.current])
1548
+
1549
+ return (
1550
+ <div className="app" style={{paddingLeft: "10px"}}>
1551
+ <div ref={ref} />
1552
+ {open &&
1553
+ (
1554
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1555
+ <DropdownMenu
1556
+ PopperBaseProps={POPPER_BASE_PROPS}
1557
+ anchorEl={ref.current}
1558
+ noOptionsText="No matches found, try again!"
1559
+ onClickAway={handleClickAway}
1560
+ open
1561
+ options={MENU_ITEMS}
1562
+ search
1563
+ />
1564
+ )
1565
+ }
1566
+ </div>
1567
+ );
1568
+ }
1569
+
1570
+ export default App;
1571
+ ```
1572
+
1573
+ **/index.tsx**
1574
+
1575
+ ```tsx
1576
+ import React from "react";
1577
+ import { createRoot } from "react-dom/client";
1578
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1579
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1580
+ import { Theme } from "@czi-sds/components";
1581
+ import CssBaseline from "@mui/material/CssBaseline";
1582
+ import useMediaQuery from "@mui/material/useMediaQuery";
1583
+
1584
+ import App from "./App";
1585
+
1586
+ const container = document.getElementById("root");
1587
+ const root = createRoot(container!);
1588
+
1589
+ const RootApp = () => {
1590
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1591
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1592
+
1593
+ return (
1594
+ <StyledEngineProvider injectFirst>
1595
+ <ThemeProvider theme={theme}>
1596
+ <EmotionThemeProvider theme={theme}>
1597
+ <CssBaseline />
1598
+ <App />
1599
+ </EmotionThemeProvider>
1600
+ </ThemeProvider>
1601
+ </StyledEngineProvider>
1602
+ );
1603
+ };
1604
+
1605
+ root.render(
1606
+ <React.StrictMode>
1607
+ <RootApp />
1608
+ </React.StrictMode>
1609
+ );
1610
+
1611
+ ```
1612
+
1613
+ **/public/index.html**
1614
+
1615
+ ```html
1616
+ <!DOCTYPE html>
1617
+ <html lang="en">
1618
+ <head>
1619
+ <meta charset="UTF-8" />
1620
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1621
+ <title>Dropdown</title>
1622
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1623
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1624
+ <link
1625
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1626
+ rel="stylesheet"
1627
+ />
1628
+ <link
1629
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1630
+ rel="stylesheet"
1631
+ />
1632
+ </head>
1633
+ <body>
1634
+ <div id="root"></div>
1635
+ </body>
1636
+ </html>
1637
+
1638
+ ```
1639
+
1640
+ **/package.json**
1641
+
1642
+ ```json
1643
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
1644
+ ```
1645
+
1646
+ ---
1647
+
1648
+ #### DropdownMenu with multi-select
1649
+
1650
+ You can allow users to select multiple menu items by passing the `DropdownMenu` component the `multiple` prop. `MenuItems` will be shifted further to the right to make room for checkmarks displayed upon selection.
1651
+
1652
+ If the `multiple` prop is not `true`, the `DropdownMenu` allows just one selection. It closes and applies the selection once  a menu item is selected.
1653
+
1654
+ **/styles.css**
1655
+
1656
+ ```css
1657
+ body {
1658
+ font-size: 13px;
1659
+ font-weight: 400;
1660
+ letter-spacing: 0.08px;
1661
+ line-height: 20px;
1662
+ text-transform: none;
1663
+ font-family: "Inter", sans-serif;
1664
+ }
1665
+
1666
+ h1 {
1667
+ font-size: 1.5rem;
1668
+ }
1669
+
1670
+ .app {
1671
+ padding: 20px;
1672
+ }
1673
+ ```
1674
+
1675
+ **tsconfig.json**
1676
+
1677
+ ```json
1678
+ {
1679
+ "include": [
1680
+ "./**/*"
1681
+ ],
1682
+ "compilerOptions": {
1683
+ "strict": true,
1684
+ "esModuleInterop": true,
1685
+ "lib": [ "dom", "es2015" ],
1686
+ "jsx": "react-jsx"
1687
+ }
1688
+ }
1689
+ ```
1690
+
1691
+ **/App.tsx**
1692
+
1693
+ ```tsx
1694
+ // DropdownMenu with multi-select
1695
+
1696
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1697
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1698
+ import "./styles.css";
1699
+
1700
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1701
+ {
1702
+ name: "Menu item 1",
1703
+ },
1704
+ {
1705
+ name: "Menu item 2",
1706
+ },
1707
+ {
1708
+ name: "Menu item 3",
1709
+ },
1710
+ ];
1711
+
1712
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
1713
+
1714
+ function handleClickAway(){}
1715
+
1716
+ function App() {
1717
+ const ref = useRef(null);
1718
+ const [open, setOpen] = useState(false);
1719
+
1720
+ useEffect(() => {
1721
+ if (ref.current) setOpen(true);
1722
+ }, [ref.current])
1723
+
1724
+ return (
1725
+ <div className="app" style={{paddingLeft: "10px"}}>
1726
+ <div ref={ref} />
1727
+ {open &&
1728
+ (
1729
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1730
+ <DropdownMenu
1731
+ PopperBaseProps={POPPER_BASE_PROPS}
1732
+ anchorEl={ref.current}
1733
+ multiple
1734
+ onClickAway={handleClickAway}
1735
+ open
1736
+ options={MENU_ITEMS}
1737
+ />
1738
+ )
1739
+ }
1740
+ </div>
1741
+ );
1742
+ }
1743
+
1744
+ export default App;
1745
+ ```
1746
+
1747
+ **/index.tsx**
1748
+
1749
+ ```tsx
1750
+ import React from "react";
1751
+ import { createRoot } from "react-dom/client";
1752
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1753
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1754
+ import { Theme } from "@czi-sds/components";
1755
+ import CssBaseline from "@mui/material/CssBaseline";
1756
+ import useMediaQuery from "@mui/material/useMediaQuery";
1757
+
1758
+ import App from "./App";
1759
+
1760
+ const container = document.getElementById("root");
1761
+ const root = createRoot(container!);
1762
+
1763
+ const RootApp = () => {
1764
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1765
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1766
+
1767
+ return (
1768
+ <StyledEngineProvider injectFirst>
1769
+ <ThemeProvider theme={theme}>
1770
+ <EmotionThemeProvider theme={theme}>
1771
+ <CssBaseline />
1772
+ <App />
1773
+ </EmotionThemeProvider>
1774
+ </ThemeProvider>
1775
+ </StyledEngineProvider>
1776
+ );
1777
+ };
1778
+
1779
+ root.render(
1780
+ <React.StrictMode>
1781
+ <RootApp />
1782
+ </React.StrictMode>
1783
+ );
1784
+
1785
+ ```
1786
+
1787
+ **/public/index.html**
1788
+
1789
+ ```html
1790
+ <!DOCTYPE html>
1791
+ <html lang="en">
1792
+ <head>
1793
+ <meta charset="UTF-8" />
1794
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1795
+ <title>Dropdown</title>
1796
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1797
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1798
+ <link
1799
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1800
+ rel="stylesheet"
1801
+ />
1802
+ <link
1803
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1804
+ rel="stylesheet"
1805
+ />
1806
+ </head>
1807
+ <body>
1808
+ <div id="root"></div>
1809
+ </body>
1810
+ </html>
1811
+
1812
+ ```
1813
+
1814
+ **/package.json**
1815
+
1816
+ ```json
1817
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
1818
+ ```
1819
+
1820
+ ---
1821
+
1822
+ #### Dropdown with search and multi-select; does *not* maintain searched query upon selection of a menu item
1823
+
1824
+ If search is included in the `DropdownMenu`, by default the search input will be maintained when a user selects a `MenuItem` (if multiple selections are allowed; otherwise the dropdown menu closes upon selection). However, the search input can be set to close upon selection by setting the optional `keepSearchOnSelect` prop to `false`.
1825
+
1826
+ This setting is intended to be used only when `search` and `multiple` are both set to true (otherwise it will have no noticeable affect since the dropdown menu closes upon selection if multi-select is not allowed, hiding the search bar).
1827
+
1828
+ To see how this works:
1829
+
1830
+ 1. In the search box, type in "fruit"
1831
+ 2. `DropdownMenu` will display "Fruit: Apple" and "Fruit: Orange"
1832
+ 3. Select "Fruit: Apple"
1833
+ 4. Note that the search text is now cleared and all dropdown items reappear
1834
+
1835
+ Compare this to the default behavior in the "DropdownMenu with search" example above, or change the `keepSearchOnSelect` prop in the example below to `true`.
1836
+
1837
+ **/styles.css**
1838
+
1839
+ ```css
1840
+ body {
1841
+ font-size: 13px;
1842
+ font-weight: 400;
1843
+ letter-spacing: 0.08px;
1844
+ line-height: 20px;
1845
+ text-transform: none;
1846
+ font-family: "Inter", sans-serif;
1847
+ }
1848
+
1849
+ h1 {
1850
+ font-size: 1.5rem;
1851
+ }
1852
+
1853
+ .app {
1854
+ padding: 20px;
1855
+ }
1856
+ ```
1857
+
1858
+ **tsconfig.json**
1859
+
1860
+ ```json
1861
+ {
1862
+ "include": [
1863
+ "./**/*"
1864
+ ],
1865
+ "compilerOptions": {
1866
+ "strict": true,
1867
+ "esModuleInterop": true,
1868
+ "lib": [ "dom", "es2015" ],
1869
+ "jsx": "react-jsx"
1870
+ }
1871
+ }
1872
+ ```
1873
+
1874
+ **/App.tsx**
1875
+
1876
+ ```tsx
1877
+ // Dropdown with multi-select that does not maintain searched query upon selection of a menu item
1878
+
1879
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1880
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1881
+ import "./styles.css";
1882
+
1883
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1884
+ {
1885
+ name: "Fruit: Apple",
1886
+ },
1887
+ {
1888
+ name: "Fruit: Orange",
1889
+ },
1890
+ {
1891
+ name: "Vegetable: Carrot",
1892
+ },
1893
+ {
1894
+ name: "Vegetable: Kale",
1895
+ },
1896
+ ];
1897
+
1898
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
1899
+
1900
+ function handleClickAway(){}
1901
+
1902
+ function App() {
1903
+ const ref = useRef(null);
1904
+ const [open, setOpen] = useState(false);
1905
+
1906
+ useEffect(() => {
1907
+ if (ref.current) setOpen(true);
1908
+ }, [ref.current])
1909
+
1910
+ return (
1911
+ <div className="app" style={{paddingLeft: "10px"}}>
1912
+ <div ref={ref} />
1913
+ {open &&
1914
+ (
1915
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1916
+ <DropdownMenu
1917
+ PopperBaseProps={POPPER_BASE_PROPS}
1918
+ anchorEl={ref.current}
1919
+ keepSearchOnSelect={false}
1920
+ multiple
1921
+ onClickAway={function handleClickAway(){}}
1922
+ open
1923
+ options={MENU_ITEMS}
1924
+ search
1925
+ />
1926
+ )
1927
+ }
1928
+ </div>
1929
+ );
1930
+ }
1931
+
1932
+ export default App;
1933
+ ```
1934
+
1935
+ **/index.tsx**
1936
+
1937
+ ```tsx
1938
+ import React from "react";
1939
+ import { createRoot } from "react-dom/client";
1940
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1941
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1942
+ import { Theme } from "@czi-sds/components";
1943
+ import CssBaseline from "@mui/material/CssBaseline";
1944
+ import useMediaQuery from "@mui/material/useMediaQuery";
1945
+
1946
+ import App from "./App";
1947
+
1948
+ const container = document.getElementById("root");
1949
+ const root = createRoot(container!);
1950
+
1951
+ const RootApp = () => {
1952
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1953
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1954
+
1955
+ return (
1956
+ <StyledEngineProvider injectFirst>
1957
+ <ThemeProvider theme={theme}>
1958
+ <EmotionThemeProvider theme={theme}>
1959
+ <CssBaseline />
1960
+ <App />
1961
+ </EmotionThemeProvider>
1962
+ </ThemeProvider>
1963
+ </StyledEngineProvider>
1964
+ );
1965
+ };
1966
+
1967
+ root.render(
1968
+ <React.StrictMode>
1969
+ <RootApp />
1970
+ </React.StrictMode>
1971
+ );
1972
+
1973
+ ```
1974
+
1975
+ **/public/index.html**
1976
+
1977
+ ```html
1978
+ <!DOCTYPE html>
1979
+ <html lang="en">
1980
+ <head>
1981
+ <meta charset="UTF-8" />
1982
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1983
+ <title>Dropdown</title>
1984
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1985
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1986
+ <link
1987
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1988
+ rel="stylesheet"
1989
+ />
1990
+ <link
1991
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1992
+ rel="stylesheet"
1993
+ />
1994
+ </head>
1995
+ <body>
1996
+ <div id="root"></div>
1997
+ </body>
1998
+ </html>
1999
+
2000
+ ```
2001
+
2002
+ **/package.json**
2003
+
2004
+ ```json
2005
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
2006
+ ```
2007
+
2008
+ ---
2009
+
2010
+ #### DropdownMenu with grouped sections
2011
+
2012
+ `MenuItems` can be displayed in groups with a label.
2013
+
2014
+ This requires passing `DropdownMenu` the `groupBy` prop to group options under the returned string. The `groupBy` value is also used as the text for group headings when `MUI Autocomplete renderGroup` is not provided. For example:
2015
+
2016
+ `groupBy={(option) => {`
2017
+
2018
+ `return option.section; // this will group items by section`
2019
+
2020
+ `// Another example`
2021
+
2022
+ `// this will group items by the last character of their names`
2023
+
2024
+ `return option.name.at(-1);`
2025
+
2026
+ `}}`
2027
+
2028
+ This `groupBy` prop must be provided or the groups will not render, even if all menu items have `section` properties (see below).
2029
+
2030
+ >**Warning:** To avoid duplicate headers, please make sure that the options are also sorted with the same dimension that they are grouped by. (See: Issue and example below)
2031
+
2032
+ **/styles.css**
2033
+
2034
+ ```css
2035
+ body {
2036
+ font-size: 13px;
2037
+ font-weight: 400;
2038
+ letter-spacing: 0.08px;
2039
+ line-height: 20px;
2040
+ text-transform: none;
2041
+ font-family: "Inter", sans-serif;
2042
+ }
2043
+
2044
+ h1 {
2045
+ font-size: 1.5rem;
2046
+ }
2047
+
2048
+ .app {
2049
+ padding: 20px;
2050
+ }
2051
+ ```
2052
+
2053
+ **tsconfig.json**
2054
+
2055
+ ```json
2056
+ {
2057
+ "include": [
2058
+ "./**/*"
2059
+ ],
2060
+ "compilerOptions": {
2061
+ "strict": true,
2062
+ "esModuleInterop": true,
2063
+ "lib": [ "dom", "es2015" ],
2064
+ "jsx": "react-jsx"
2065
+ }
2066
+ }
2067
+ ```
2068
+
2069
+ **/App.tsx**
2070
+
2071
+ ```tsx
2072
+ // Dropdown with grouped sections
2073
+
2074
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
2075
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
2076
+ import "./styles.css";
2077
+
2078
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
2079
+ {
2080
+ name: "Menu item A.1",
2081
+ section: "Section A"
2082
+ },
2083
+ {
2084
+ name: "Menu item A.2",
2085
+ section: "Section A"
2086
+ },
2087
+ {
2088
+ name: "Menu item without a specified section",
2089
+ },
2090
+ {
2091
+ name: "Menu item B.1",
2092
+ section: "Section B"
2093
+ },
2094
+ {
2095
+ name: "Menu item C.1",
2096
+ section: "Section C"
2097
+ },
2098
+ {
2099
+ name: "Menu item C.2",
2100
+ section: "Section C"
2101
+ },
2102
+ {
2103
+ name: "Menu item C.3",
2104
+ section: "Section C"
2105
+ },
2106
+ {
2107
+ name: "Menu item B.2",
2108
+ section: "Section B"
2109
+ },
2110
+ ];
2111
+
2112
+ /**
2113
+ * Items need to be sorted by the same groupBy dimension
2114
+ * In this example, we're grouping by the last character of the item name
2115
+ */
2116
+ const SORTED_ITEMS = [...MENU_ITEMS].sort((a, b) => {
2117
+ const aLastLetter = a.name.at(-1);
2118
+ const bLastLetter = b.name.at(-1);
2119
+
2120
+ if (aLastLetter < bLastLetter) {
2121
+ return -1;
2122
+ }
2123
+ if (aLastLetter > bLastLetter) {
2124
+ return 1;
2125
+ }
2126
+ return 0;
2127
+ });
2128
+
2129
+ function groupBy(option) {
2130
+ return option.name.at(-1)
2131
+ }
2132
+
2133
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
2134
+
2135
+ function handleClickAway(){}
2136
+
2137
+ function App() {
2138
+ const ref = useRef(null);
2139
+ const [open, setOpen] = useState(false);
2140
+
2141
+ useEffect(() => {
2142
+ if (ref.current) setOpen(true);
2143
+ }, [ref.current])
2144
+
2145
+ return (
2146
+ <div className="app" style={{paddingLeft: "10px"}}>
2147
+ <div ref={ref} />
2148
+ {open &&
2149
+ (
2150
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
2151
+ <DropdownMenu
2152
+ PopperBaseProps={POPPER_BASE_PROPS}
2153
+ anchorEl={ref.current}
2154
+ groupBy={groupBy}
2155
+ onClickAway={handleClickAway}
2156
+ open
2157
+ options={SORTED_ITEMS}
2158
+ />
2159
+ )
2160
+ }
2161
+ </div>
2162
+ );
2163
+ }
2164
+
2165
+ export default App;
2166
+ ```
2167
+
2168
+ **/index.tsx**
2169
+
2170
+ ```tsx
2171
+ import React from "react";
2172
+ import { createRoot } from "react-dom/client";
2173
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2174
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2175
+ import { Theme } from "@czi-sds/components";
2176
+ import CssBaseline from "@mui/material/CssBaseline";
2177
+ import useMediaQuery from "@mui/material/useMediaQuery";
2178
+
2179
+ import App from "./App";
2180
+
2181
+ const container = document.getElementById("root");
2182
+ const root = createRoot(container!);
2183
+
2184
+ const RootApp = () => {
2185
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2186
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2187
+
2188
+ return (
2189
+ <StyledEngineProvider injectFirst>
2190
+ <ThemeProvider theme={theme}>
2191
+ <EmotionThemeProvider theme={theme}>
2192
+ <CssBaseline />
2193
+ <App />
2194
+ </EmotionThemeProvider>
2195
+ </ThemeProvider>
2196
+ </StyledEngineProvider>
2197
+ );
2198
+ };
2199
+
2200
+ root.render(
2201
+ <React.StrictMode>
2202
+ <RootApp />
2203
+ </React.StrictMode>
2204
+ );
2205
+
2206
+ ```
2207
+
2208
+ **/public/index.html**
2209
+
2210
+ ```html
2211
+ <!DOCTYPE html>
2212
+ <html lang="en">
2213
+ <head>
2214
+ <meta charset="UTF-8" />
2215
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2216
+ <title>Dropdown</title>
2217
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2218
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2219
+ <link
2220
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2221
+ rel="stylesheet"
2222
+ />
2223
+ <link
2224
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2225
+ rel="stylesheet"
2226
+ />
2227
+ </head>
2228
+ <body>
2229
+ <div id="root"></div>
2230
+ </body>
2231
+ </html>
2232
+
2233
+ ```
2234
+
2235
+ **/package.json**
2236
+
2237
+ ```json
2238
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
2239
+ ```
2240
+
2241
+ ---
2242
+
2243
+ #### DropdownMenu with menu items that have count, icon, and / or details
2244
+
2245
+ Each `MenuItem` provided to `DropdownMenu` can optionally include `count`, `details` and `sdsIcon` properties. 
2246
+
2247
+ The `count` property takes a number which is displayed to the right of the menu item text, with built-in spacing to its left (to prevent the menu item's text from running into it).
2248
+
2249
+ Add a text string to the `details` property to display text below the menu item’s main text.
2250
+
2251
+ `sdsIcon` can be passed the name of any icon in the SDS codebase and it will automatically render the icon to the left of the menu item’s text; if an invalid name is passed, the menu item will render the same as if no icon were passed to it. The `sdsIconProps` prop can be added to a menu item with an icon to provide more specifics like a `className`or `color`.
2252
+
2253
+ **/styles.css**
2254
+
2255
+ ```css
2256
+ body {
2257
+ font-size: 13px;
2258
+ font-weight: 400;
2259
+ letter-spacing: 0.08px;
2260
+ line-height: 20px;
2261
+ text-transform: none;
2262
+ font-family: "Inter", sans-serif;
2263
+ }
2264
+
2265
+ h1 {
2266
+ font-size: 1.5rem;
2267
+ }
2268
+
2269
+ .app {
2270
+ padding: 20px;
2271
+ }
2272
+ ```
2273
+
2274
+ **tsconfig.json**
2275
+
2276
+ ```json
2277
+ {
2278
+ "include": [
2279
+ "./**/*"
2280
+ ],
2281
+ "compilerOptions": {
2282
+ "strict": true,
2283
+ "esModuleInterop": true,
2284
+ "lib": [ "dom", "es2015" ],
2285
+ "jsx": "react-jsx"
2286
+ }
2287
+ }
2288
+ ```
2289
+
2290
+ **/App.tsx**
2291
+
2292
+ ```tsx
2293
+ // Dropdown with grouped sections
2294
+
2295
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
2296
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
2297
+ import "./styles.css";
2298
+
2299
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
2300
+ {
2301
+ name: "Menu item with count",
2302
+ count: 3
2303
+ },
2304
+ {
2305
+ name: "Menu item with details",
2306
+ details: "Details"
2307
+ },
2308
+ {
2309
+ name: "Menu item with icon",
2310
+ sdsIcon: "puzzlePiece",
2311
+ },
2312
+ {
2313
+ name: "Menu item with icon that has its own props",
2314
+ sdsIcon: "copy",
2315
+ sdsIconProps: {
2316
+ className: "custom-class-name",
2317
+ color: "error",
2318
+ },
2319
+ },
2320
+ {
2321
+ name: "Menu item with count, details, & icon",
2322
+ count: 908,
2323
+ details: "Details",
2324
+ sdsIcon: "lightBulb",
2325
+ },
2326
+ ];
2327
+
2328
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
2329
+
2330
+ function handleClickAway(){}
2331
+
2332
+ function App() {
2333
+ const ref = useRef(null);
2334
+ const [open, setOpen] = useState(false);
2335
+
2336
+ useEffect(() => {
2337
+ if (ref.current) setOpen(true);
2338
+ }, [ref.current])
2339
+
2340
+ return (
2341
+ <div className="app" style={{paddingLeft: "10px"}}>
2342
+ <div ref={ref} />
2343
+ {open &&
2344
+ (
2345
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
2346
+ <DropdownMenu
2347
+ PopperBaseProps={POPPER_BASE_PROPS}
2348
+ anchorEl={ref.current}
2349
+ groupBy={(option: DefaultDropdownMenuOption) => {
2350
+ return option.section as string;
2351
+ }}
2352
+ onClickAway={handleClickAway}
2353
+ open
2354
+ options={MENU_ITEMS}
2355
+ />
2356
+ )
2357
+ }
2358
+ </div>
2359
+ );
2360
+ }
2361
+
2362
+ export default App;
2363
+ ```
2364
+
2365
+ **/index.tsx**
2366
+
2367
+ ```tsx
2368
+ import React from "react";
2369
+ import { createRoot } from "react-dom/client";
2370
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2371
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2372
+ import { Theme } from "@czi-sds/components";
2373
+ import CssBaseline from "@mui/material/CssBaseline";
2374
+ import useMediaQuery from "@mui/material/useMediaQuery";
2375
+
2376
+ import App from "./App";
2377
+
2378
+ const container = document.getElementById("root");
2379
+ const root = createRoot(container!);
2380
+
2381
+ const RootApp = () => {
2382
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2383
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2384
+
2385
+ return (
2386
+ <StyledEngineProvider injectFirst>
2387
+ <ThemeProvider theme={theme}>
2388
+ <EmotionThemeProvider theme={theme}>
2389
+ <CssBaseline />
2390
+ <App />
2391
+ </EmotionThemeProvider>
2392
+ </ThemeProvider>
2393
+ </StyledEngineProvider>
2394
+ );
2395
+ };
2396
+
2397
+ root.render(
2398
+ <React.StrictMode>
2399
+ <RootApp />
2400
+ </React.StrictMode>
2401
+ );
2402
+
2403
+ ```
2404
+
2405
+ **/public/index.html**
2406
+
2407
+ ```html
2408
+ <!DOCTYPE html>
2409
+ <html lang="en">
2410
+ <head>
2411
+ <meta charset="UTF-8" />
2412
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2413
+ <title>Dropdown</title>
2414
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2415
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2416
+ <link
2417
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2418
+ rel="stylesheet"
2419
+ />
2420
+ <link
2421
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2422
+ rel="stylesheet"
2423
+ />
2424
+ </head>
2425
+ <body>
2426
+ <div id="root"></div>
2427
+ </body>
2428
+ </html>
2429
+
2430
+ ```
2431
+
2432
+ **/package.json**
2433
+
2434
+ ```json
2435
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
2436
+ ```
2437
+
2438
+ ---
2439
+
2440
+ #### DropdownMenu with components in place of menu items
2441
+
2442
+ In place of the text normally shown for a menu item, a custom component can be to displayed instead.
2443
+
2444
+ Any existing SDS component (must be imported) can be provided to the menu item's object (within the array passed to the `options` prop), via the `component` property. HTML elements (like the wrapper `<div>` surrounding the three `Tag` components in the example below) can also be used.
2445
+
2446
+ These custom menu items can optionally be assigned metadata just like regular menu items, including `section`, `count`, and `sdsIcon` and `sdsIconProps`.
2447
+
2448
+ >**Warning:** Currently, there is a bug preventing the `details` property from rendering when used in combination with a custom component in place of a menu item; see issue #522 for *details* (pun intended!).
2449
+
2450
+ **/styles.css**
2451
+
2452
+ ```css
2453
+ body {
2454
+ font-size: 13px;
2455
+ font-weight: 400;
2456
+ letter-spacing: 0.08px;
2457
+ line-height: 20px;
2458
+ text-transform: none;
2459
+ font-family: "Inter", sans-serif;
2460
+ }
2461
+
2462
+ h1 {
2463
+ font-size: 1.5rem;
2464
+ }
2465
+
2466
+ .app {
2467
+ padding: 20px;
2468
+ }
2469
+ ```
2470
+
2471
+ **tsconfig.json**
2472
+
2473
+ ```json
2474
+ {
2475
+ "include": [
2476
+ "./**/*"
2477
+ ],
2478
+ "compilerOptions": {
2479
+ "strict": true,
2480
+ "esModuleInterop": true,
2481
+ "lib": [ "dom", "es2015" ],
2482
+ "jsx": "react-jsx"
2483
+ }
2484
+ }
2485
+ ```
2486
+
2487
+ **/App.tsx**
2488
+
2489
+ ```tsx
2490
+ // DropdownMenu with components in place of menu items
2491
+
2492
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
2493
+ import { DropdownMenu, DefaultDropdownMenuOption, Tag, Button, ButtonIcon } from "@czi-sds/components";
2494
+ import "./styles.css";
2495
+
2496
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
2497
+ {
2498
+ component: (
2499
+ <Button
2500
+ sdsStyle="square"
2501
+ sdsType="primary"
2502
+ >
2503
+ Button
2504
+ </Button>
2505
+ ),
2506
+ name: "Button component",
2507
+ section: "Button component"
2508
+ },
2509
+ {
2510
+ component: (
2511
+ <ButtonIcon
2512
+ sdsIcon="Copy"
2513
+ sdsSize="large"
2514
+ sdsType="primary"
2515
+ />
2516
+ ),
2517
+ name: "ButtonIcon component",
2518
+ section: "ButtonIcon component",
2519
+ },
2520
+ {
2521
+ component: (
2522
+ <div style={{ marginTop: 10 }}>
2523
+ <Tag
2524
+ label="Tag 1"
2525
+ sdsStyle="rounded"
2526
+ sdsType="secondary"
2527
+ color="negative"
2528
+ />
2529
+ <Tag
2530
+ label="Tag 2"
2531
+ sdsStyle="rounded"
2532
+ sdsType="secondary"
2533
+ color="notice"
2534
+ />
2535
+ <Tag
2536
+ label="Tag 3"
2537
+ sdsStyle="rounded"
2538
+ sdsType="secondary"
2539
+ color="neutral"
2540
+ />
2541
+ </div>
2542
+ ),
2543
+ name: "tag components",
2544
+ section: "Tag components",
2545
+ },
2546
+ {
2547
+ component: (
2548
+ <Tag
2549
+ label="Tag"
2550
+ sdsStyle="rounded"
2551
+ sdsType="secondary"
2552
+ color="neutral"
2553
+ />
2554
+ ),
2555
+ name: "Button component",
2556
+ section: "Component with count and icon",
2557
+ count: 37,
2558
+ sdsIcon: "PuzzlePiece",
2559
+ },
2560
+ ];
2561
+
2562
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
2563
+
2564
+ function handleClickAway(){}
2565
+
2566
+ function App() {
2567
+ const ref = useRef(null);
2568
+ const [open, setOpen] = useState(false);
2569
+
2570
+ useEffect(() => {
2571
+ if (ref.current) setOpen(true);
2572
+ }, [ref.current])
2573
+
2574
+ return (
2575
+ <div className="app" style={{paddingLeft: "10px"}}>
2576
+ <div ref={ref} />
2577
+ {open &&
2578
+ (
2579
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
2580
+ <DropdownMenu
2581
+ PopperBaseProps={POPPER_BASE_PROPS}
2582
+ anchorEl={ref.current}
2583
+ groupBy={(option: DefaultDropdownMenuOption) => {
2584
+ return option.section;
2585
+ }}
2586
+ onClickAway={handleClickAway}
2587
+ open
2588
+ options={MENU_ITEMS}
2589
+ />
2590
+ )
2591
+ }
2592
+ </div>
2593
+ );
2594
+ }
2595
+
2596
+ export default App;
2597
+ ```
2598
+
2599
+ **/index.tsx**
2600
+
2601
+ ```tsx
2602
+ import React from "react";
2603
+ import { createRoot } from "react-dom/client";
2604
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2605
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2606
+ import { Theme } from "@czi-sds/components";
2607
+ import CssBaseline from "@mui/material/CssBaseline";
2608
+ import useMediaQuery from "@mui/material/useMediaQuery";
2609
+
2610
+ import App from "./App";
2611
+
2612
+ const container = document.getElementById("root");
2613
+ const root = createRoot(container!);
2614
+
2615
+ const RootApp = () => {
2616
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2617
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2618
+
2619
+ return (
2620
+ <StyledEngineProvider injectFirst>
2621
+ <ThemeProvider theme={theme}>
2622
+ <EmotionThemeProvider theme={theme}>
2623
+ <CssBaseline />
2624
+ <App />
2625
+ </EmotionThemeProvider>
2626
+ </ThemeProvider>
2627
+ </StyledEngineProvider>
2628
+ );
2629
+ };
2630
+
2631
+ root.render(
2632
+ <React.StrictMode>
2633
+ <RootApp />
2634
+ </React.StrictMode>
2635
+ );
2636
+
2637
+ ```
2638
+
2639
+ **/public/index.html**
2640
+
2641
+ ```html
2642
+ <!DOCTYPE html>
2643
+ <html lang="en">
2644
+ <head>
2645
+ <meta charset="UTF-8" />
2646
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2647
+ <title>Dropdown</title>
2648
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2649
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2650
+ <link
2651
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2652
+ rel="stylesheet"
2653
+ />
2654
+ <link
2655
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2656
+ rel="stylesheet"
2657
+ />
2658
+ </head>
2659
+ <body>
2660
+ <div id="root"></div>
2661
+ </body>
2662
+ </html>
2663
+
2664
+ ```
2665
+
2666
+ **/package.json**
2667
+
2668
+ ```json
2669
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
2670
+ ```
2671
+
2672
+ ---
2673
+
2674
+ ---
2675
+
2676
+ ## MenuItem
2677
+
2678
+ ### SDS Source Code
2679
+
2680
+ The component's source code in the SDS codebase can be found here.
2681
+
2682
+ ### Props
2683
+
2684
+ Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
2685
+
2686
+ | Name | Type | Default | Description |
2687
+ | --- | --- | --- | --- |
2688
+ | `column` | `string` | `-` | The `column` prop determines the content displayed in the right column of the menu item. This content can be customized to provide additional information or context for the menu item. |
2689
+ | `children` | `node` | `-` | Main content of the MenuItem. |
2690
+ | `disabled` | `bool` | `false` | When set to `true`, the disabled prop indicates that the menu item is disabled and not interactable. |
2691
+ | `isMultiSelect` | `bool` | `false` | When `true`, designates that the menu item supports multi-select functionality. |
2692
+ | `sdsIcon` | `func` | `-` | Allows you to provide a custom SDS icon component to be displayed within the menu item. |
2693
+ | `sdsIconProps` | `func` | `-` | Enables you to pass additional props to customize the SDS icon within the menu item. |
2694
+ | `sdsStyle` | `"determinate"` `| "indeterminate"` | `"determinate"` | Determines the style of the menu item, whether it is `determinate` or `indeterminate`. |
2695
+ | `selected` | `bool` | `false` | When set to `true`, the `selected` prop signifies that the menu item is currently selected. |
2696
+
2697
+ ### Code Examples
2698
+
2699
+ #### **Default MenuItem**
2700
+
2701
+ This example shows the minimum props needed for rendering a `MenuItem`.
2702
+
2703
+ **/styles.css**
2704
+
2705
+ ```css
2706
+ body {
2707
+ font-size: 13px;
2708
+ font-weight: 400;
2709
+ letter-spacing: 0.08px;
2710
+ line-height: 20px;
2711
+ text-transform: none;
2712
+ font-family: "Inter", sans-serif;
2713
+ }
2714
+
2715
+ h1 {
2716
+ font-size: 1.5rem;
2717
+ }
2718
+
2719
+ .app {
2720
+ padding: 20px;
2721
+ }
2722
+ ```
2723
+
2724
+ **tsconfig.json**
2725
+
2726
+ ```json
2727
+ {
2728
+ "include": [
2729
+ "./**/*"
2730
+ ],
2731
+ "compilerOptions": {
2732
+ "strict": true,
2733
+ "esModuleInterop": true,
2734
+ "lib": [ "dom", "es2015" ],
2735
+ "jsx": "react-jsx"
2736
+ }
2737
+ }
2738
+ ```
2739
+
2740
+ **/App.tsx**
2741
+
2742
+ ```tsx
2743
+ import { MenuItem } from "@czi-sds/components";
2744
+ import "./styles.css";
2745
+
2746
+ function App() {
2747
+ return (
2748
+ <div className="app">
2749
+ <MenuItem>
2750
+ MenuItem 1
2751
+ </MenuItem>
2752
+ </div>
2753
+ );
2754
+ }
2755
+
2756
+ export default App;
2757
+ ```
2758
+
2759
+ **/index.tsx**
2760
+
2761
+ ```tsx
2762
+ import React from "react";
2763
+ import { createRoot } from "react-dom/client";
2764
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2765
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2766
+ import { Theme } from "@czi-sds/components";
2767
+ import CssBaseline from "@mui/material/CssBaseline";
2768
+ import useMediaQuery from "@mui/material/useMediaQuery";
2769
+
2770
+ import App from "./App";
2771
+
2772
+ const container = document.getElementById("root");
2773
+ const root = createRoot(container!);
2774
+
2775
+ const RootApp = () => {
2776
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2777
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2778
+
2779
+ return (
2780
+ <StyledEngineProvider injectFirst>
2781
+ <ThemeProvider theme={theme}>
2782
+ <EmotionThemeProvider theme={theme}>
2783
+ <CssBaseline />
2784
+ <App />
2785
+ </EmotionThemeProvider>
2786
+ </ThemeProvider>
2787
+ </StyledEngineProvider>
2788
+ );
2789
+ };
2790
+
2791
+ root.render(
2792
+ <React.StrictMode>
2793
+ <RootApp />
2794
+ </React.StrictMode>
2795
+ );
2796
+
2797
+ ```
2798
+
2799
+ **/public/index.html**
2800
+
2801
+ ```html
2802
+ <!DOCTYPE html>
2803
+ <html lang="en">
2804
+ <head>
2805
+ <meta charset="UTF-8" />
2806
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2807
+ <title>Dropdown</title>
2808
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2809
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2810
+ <link
2811
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2812
+ rel="stylesheet"
2813
+ />
2814
+ <link
2815
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2816
+ rel="stylesheet"
2817
+ />
2818
+ </head>
2819
+ <body>
2820
+ <div id="root"></div>
2821
+ </body>
2822
+ </html>
2823
+
2824
+ ```
2825
+
2826
+ **/package.json**
2827
+
2828
+ ```json
2829
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
2830
+ ```
2831
+
2832
+ ---
2833
+
2834
+ #### **Multi-Select MenuItem with Icon**
2835
+
2836
+ This example showcases a multi-select `MenuItem` component featuring an SDS `Icon` and a distinct column value.
2837
+
2838
+ >**SDS Tip:** MenuItem exclusively accepts SDS Icons in `xs` and `s` sizes. To explore a comprehensive list of SDS icons and their available sizes, refer to the IconBank page within Storybook.
2839
+
2840
+ **/styles.css**
2841
+
2842
+ ```css
2843
+ body {
2844
+ font-size: 13px;
2845
+ font-weight: 400;
2846
+ letter-spacing: 0.08px;
2847
+ line-height: 20px;
2848
+ text-transform: none;
2849
+ font-family: "Inter", sans-serif;
2850
+ }
2851
+
2852
+ h1 {
2853
+ font-size: 1.5rem;
2854
+ }
2855
+
2856
+ .app {
2857
+ padding: 20px;
2858
+ }
2859
+ ```
2860
+
2861
+ **tsconfig.json**
2862
+
2863
+ ```json
2864
+ {
2865
+ "include": [
2866
+ "./**/*"
2867
+ ],
2868
+ "compilerOptions": {
2869
+ "strict": true,
2870
+ "esModuleInterop": true,
2871
+ "lib": [ "dom", "es2015" ],
2872
+ "jsx": "react-jsx"
2873
+ }
2874
+ }
2875
+ ```
2876
+
2877
+ **/App.tsx**
2878
+
2879
+ ```tsx
2880
+ import { useState } from "react";
2881
+ import { MenuItem } from "@czi-sds/components";
2882
+ import "./styles.css";
2883
+
2884
+ function App() {
2885
+ const [selected, setSelected] = useState(false);
2886
+
2887
+ const handleClick = () => {
2888
+ setSelected(prev => !prev);
2889
+ };
2890
+
2891
+ return (
2892
+ <div className="app">
2893
+ <MenuItem
2894
+ isMultiSelect
2895
+ column="Column Value"
2896
+ sdsIcon="gear"
2897
+ count={3}
2898
+ sdsIconProps={{
2899
+ color: "beta"
2900
+ }}
2901
+ selected={selected}
2902
+ onClick={handleClick}
2903
+ >
2904
+ MenuItem 1
2905
+ </MenuItem>
2906
+ </div>
2907
+ );
2908
+ }
2909
+
2910
+ export default App;
2911
+ ```
2912
+
2913
+ **/index.tsx**
2914
+
2915
+ ```tsx
2916
+ import React from "react";
2917
+ import { createRoot } from "react-dom/client";
2918
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2919
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2920
+ import { Theme } from "@czi-sds/components";
2921
+ import CssBaseline from "@mui/material/CssBaseline";
2922
+ import useMediaQuery from "@mui/material/useMediaQuery";
2923
+
2924
+ import App from "./App";
2925
+
2926
+ const container = document.getElementById("root");
2927
+ const root = createRoot(container!);
2928
+
2929
+ const RootApp = () => {
2930
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2931
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2932
+
2933
+ return (
2934
+ <StyledEngineProvider injectFirst>
2935
+ <ThemeProvider theme={theme}>
2936
+ <EmotionThemeProvider theme={theme}>
2937
+ <CssBaseline />
2938
+ <App />
2939
+ </EmotionThemeProvider>
2940
+ </ThemeProvider>
2941
+ </StyledEngineProvider>
2942
+ );
2943
+ };
2944
+
2945
+ root.render(
2946
+ <React.StrictMode>
2947
+ <RootApp />
2948
+ </React.StrictMode>
2949
+ );
2950
+
2951
+ ```
2952
+
2953
+ **/public/index.html**
2954
+
2955
+ ```html
2956
+ <!DOCTYPE html>
2957
+ <html lang="en">
2958
+ <head>
2959
+ <meta charset="UTF-8" />
2960
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2961
+ <title>Dropdown</title>
2962
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2963
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2964
+ <link
2965
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2966
+ rel="stylesheet"
2967
+ />
2968
+ <link
2969
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2970
+ rel="stylesheet"
2971
+ />
2972
+ </head>
2973
+ <body>
2974
+ <div id="root"></div>
2975
+ </body>
2976
+ </html>
2977
+
2978
+ ```
2979
+
2980
+ **/package.json**
2981
+
2982
+ ```json
2983
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
2984
+ ```
2985
+
2986
+ ---
2987
+
2988
+ ## Component Recipes
2989
+
2990
+ ## DropdownMenu Recipes
2991
+
2992
+ | These component recipes show a few of the more complex approaches available for customizing Dropdown Menus. | | **Jump to recipe:** DropdownMenu with customized `isOptionEqualToValue` DropdownMenu with customized `onChange` DropdownMenu with custom `PopperBaseProps` DropdownMenu with custom `Popper` component |
2993
+ | --- | --- | --- |
2994
+
2995
+ #### **DropdownMenu with customized** **`isOptionEqualToValue`**
2996
+
2997
+ The `isOptionEqualToValue` prop changes how a selected value is compared against the available options. By default `option.name === val.name` is used. In the custom example below, the `id` property is used instead of the `name` property.
2998
+
2999
+ This is a MUI prop; learn more in the Autocomplete API.
3000
+
3001
+ **/styles.css**
3002
+
3003
+ ```css
3004
+ body {
3005
+ font-size: 13px;
3006
+ font-weight: 400;
3007
+ letter-spacing: 0.08px;
3008
+ line-height: 20px;
3009
+ text-transform: none;
3010
+ font-family: "Inter", sans-serif;
3011
+ }
3012
+
3013
+ h1 {
3014
+ font-size: 1.5rem;
3015
+ }
3016
+
3017
+ .app {
3018
+ padding: 20px;
3019
+ }
3020
+ ```
3021
+
3022
+ **tsconfig.json**
3023
+
3024
+ ```json
3025
+ {
3026
+ "include": [
3027
+ "./**/*"
3028
+ ],
3029
+ "compilerOptions": {
3030
+ "strict": true,
3031
+ "esModuleInterop": true,
3032
+ "lib": [ "dom", "es2015" ],
3033
+ "jsx": "react-jsx"
3034
+ }
3035
+ }
3036
+ ```
3037
+
3038
+ **/App.tsx**
3039
+
3040
+ ```tsx
3041
+ // DropdownMenu with customized `isOptionEqualToValue`
3042
+
3043
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3044
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3045
+ import "./styles.css";
3046
+
3047
+ function optionIdEqualToValueId(option, value) {
3048
+ return option.id === value.id;
3049
+ }
3050
+
3051
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3052
+ {
3053
+ name: "Menu item 1",
3054
+ id: "one",
3055
+ },
3056
+ {
3057
+ name: "Menu item 2",
3058
+ id: "two",
3059
+ },
3060
+ {
3061
+ name: "Menu item 3",
3062
+ id: "three",
3063
+ },
3064
+ {
3065
+ name: "Longer menu item than the others",
3066
+ id: "four",
3067
+ },
3068
+ ];
3069
+
3070
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
3071
+
3072
+ function handleClickAway() {}
3073
+
3074
+ function App() {
3075
+ const ref = useRef(null);
3076
+ const [open, setOpen] = useState(false);
3077
+
3078
+ useEffect(() => {
3079
+ if (ref.current) setOpen(true);
3080
+ }, [ref.current])
3081
+
3082
+ return (
3083
+ <div className="app" style={{paddingLeft: "10px"}}>
3084
+ <div ref={ref} />
3085
+ {open &&
3086
+ (
3087
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3088
+ <DropdownMenu
3089
+ PopperBaseProps={POPPER_BASE_PROPS}
3090
+ anchorEl={ref.current}
3091
+ isOptionEqualToValue={optionIdEqualToValueId}
3092
+ onClickAway={handleClickAway}
3093
+ open
3094
+ options={MENU_ITEMS}
3095
+ />
3096
+ )
3097
+ }
3098
+ </div>
3099
+ );
3100
+ }
3101
+
3102
+ export default App;
3103
+ ```
3104
+
3105
+ **/index.tsx**
3106
+
3107
+ ```tsx
3108
+ import React from "react";
3109
+ import { createRoot } from "react-dom/client";
3110
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3111
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3112
+ import { Theme } from "@czi-sds/components";
3113
+ import CssBaseline from "@mui/material/CssBaseline";
3114
+ import useMediaQuery from "@mui/material/useMediaQuery";
3115
+
3116
+ import App from "./App";
3117
+
3118
+ const container = document.getElementById("root");
3119
+ const root = createRoot(container!);
3120
+
3121
+ const RootApp = () => {
3122
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3123
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3124
+
3125
+ return (
3126
+ <StyledEngineProvider injectFirst>
3127
+ <ThemeProvider theme={theme}>
3128
+ <EmotionThemeProvider theme={theme}>
3129
+ <CssBaseline />
3130
+ <App />
3131
+ </EmotionThemeProvider>
3132
+ </ThemeProvider>
3133
+ </StyledEngineProvider>
3134
+ );
3135
+ };
3136
+
3137
+ root.render(
3138
+ <React.StrictMode>
3139
+ <RootApp />
3140
+ </React.StrictMode>
3141
+ );
3142
+
3143
+ ```
3144
+
3145
+ **/public/index.html**
3146
+
3147
+ ```html
3148
+ <!DOCTYPE html>
3149
+ <html lang="en">
3150
+ <head>
3151
+ <meta charset="UTF-8" />
3152
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3153
+ <title>DropdownMenu</title>
3154
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3155
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3156
+ <link
3157
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3158
+ rel="stylesheet"
3159
+ />
3160
+ <link
3161
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3162
+ rel="stylesheet"
3163
+ />
3164
+ </head>
3165
+ <body>
3166
+ <div id="root"></div>
3167
+ </body>
3168
+ </html>
3169
+
3170
+ ```
3171
+
3172
+ **/package.json**
3173
+
3174
+ ```json
3175
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
3176
+ ```
3177
+
3178
+ ---
3179
+
3180
+ #### **DropdownMenu with customized** **`onChange`** **prop**
3181
+
3182
+ The optional `onChange` prop specifies what happens when a new selection is made.
3183
+
3184
+ In the example below, `onChange` has been modified such that making a new selection logs the `value` and `reason` to the console.
3185
+
3186
+ This is a MUI prop; learn more in the Autocomplete API.
3187
+
3188
+ **/styles.css**
3189
+
3190
+ ```css
3191
+ body {
3192
+ font-size: 13px;
3193
+ font-weight: 400;
3194
+ letter-spacing: 0.08px;
3195
+ line-height: 20px;
3196
+ text-transform: none;
3197
+ font-family: "Inter", sans-serif;
3198
+ }
3199
+
3200
+ h1 {
3201
+ font-size: 1.5rem;
3202
+ }
3203
+
3204
+ .app {
3205
+ padding: 20px;
3206
+ }
3207
+ ```
3208
+
3209
+ **tsconfig.json**
3210
+
3211
+ ```json
3212
+ {
3213
+ "include": [
3214
+ "./**/*"
3215
+ ],
3216
+ "compilerOptions": {
3217
+ "strict": true,
3218
+ "esModuleInterop": true,
3219
+ "lib": [ "dom", "es2015" ],
3220
+ "jsx": "react-jsx"
3221
+ }
3222
+ }
3223
+ ```
3224
+
3225
+ **/App.tsx**
3226
+
3227
+ ```tsx
3228
+ // DropdownMenu with customized `onInputChange`
3229
+
3230
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3231
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3232
+ import "./styles.css";
3233
+
3234
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3235
+ {
3236
+ name: "Menu item 1",
3237
+ },
3238
+ {
3239
+ name: "Menu item 2",
3240
+ },
3241
+ {
3242
+ name: "Menu item 3",
3243
+ },
3244
+ {
3245
+ name: "Longer menu item than the others",
3246
+ },
3247
+ ];
3248
+
3249
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
3250
+
3251
+ function handleClickAway() {};
3252
+
3253
+ function Message({message}) {
3254
+ return (
3255
+ <div style={{ position: "absolute", top: "170px", fontFamily: "sans-serif" }}>
3256
+ <p>{JSON.stringify(message)}</p>
3257
+ </div>
3258
+ );
3259
+ }
3260
+
3261
+ function App() {
3262
+
3263
+ const [message, setMessage] = useState();
3264
+
3265
+ const ref = useRef(null);
3266
+ const [open, setOpen] = useState(false);
3267
+
3268
+ useEffect(() => {
3269
+ if (ref.current) setOpen(true);
3270
+ }, [ref.current])
3271
+
3272
+ return (
3273
+ <div className="app" style={{paddingLeft: "10px"}}>
3274
+ <div ref={ref} />
3275
+ {open &&
3276
+ (
3277
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3278
+ <>
3279
+ <DropdownMenu
3280
+ PopperBaseProps={POPPER_BASE_PROPS}
3281
+ anchorEl={ref.current}
3282
+ keepSearchOnSelect
3283
+ onClickAway={handleClickAway}
3284
+ onChange={(_, value, reason) => setMessage({value, reason})}
3285
+ open
3286
+ options={MENU_ITEMS}
3287
+ />
3288
+ <Message message={message} />
3289
+ </>
3290
+ )
3291
+ }
3292
+ </div>
3293
+ );
3294
+ }
3295
+
3296
+ export default App;
3297
+ ```
3298
+
3299
+ **/index.tsx**
3300
+
3301
+ ```tsx
3302
+ import React from "react";
3303
+ import { createRoot } from "react-dom/client";
3304
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3305
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3306
+ import { Theme } from "@czi-sds/components";
3307
+ import CssBaseline from "@mui/material/CssBaseline";
3308
+ import useMediaQuery from "@mui/material/useMediaQuery";
3309
+
3310
+ import App from "./App";
3311
+
3312
+ const container = document.getElementById("root");
3313
+ const root = createRoot(container!);
3314
+
3315
+ const RootApp = () => {
3316
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3317
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3318
+
3319
+ return (
3320
+ <StyledEngineProvider injectFirst>
3321
+ <ThemeProvider theme={theme}>
3322
+ <EmotionThemeProvider theme={theme}>
3323
+ <CssBaseline />
3324
+ <App />
3325
+ </EmotionThemeProvider>
3326
+ </ThemeProvider>
3327
+ </StyledEngineProvider>
3328
+ );
3329
+ };
3330
+
3331
+ root.render(
3332
+ <React.StrictMode>
3333
+ <RootApp />
3334
+ </React.StrictMode>
3335
+ );
3336
+
3337
+ ```
3338
+
3339
+ **/public/index.html**
3340
+
3341
+ ```html
3342
+ <!DOCTYPE html>
3343
+ <html lang="en">
3344
+ <head>
3345
+ <meta charset="UTF-8" />
3346
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3347
+ <title>DropdownMenu</title>
3348
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3349
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3350
+ <link
3351
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3352
+ rel="stylesheet"
3353
+ />
3354
+ <link
3355
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3356
+ rel="stylesheet"
3357
+ />
3358
+ </head>
3359
+ <body>
3360
+ <div id="root"></div>
3361
+ </body>
3362
+ </html>
3363
+
3364
+ ```
3365
+
3366
+ **/package.json**
3367
+
3368
+ ```json
3369
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
3370
+ ```
3371
+
3372
+ ---
3373
+
3374
+ #### **DropdownMenu with custom** **`PopperBaseProps`**
3375
+
3376
+ `PopperBaseProps` is an SDS-specific prop made for passing custom props to the `Popper` component, which is included as part of the `DropdownMenu` component.
3377
+
3378
+ The `Popper` component is part of MUI; learn more in the Popper documentation, and see all the props available to pass to it on the Popper API page.
3379
+
3380
+ The example below passes the Popper a `className` prop to make some style changes (see the styles.css tab in the code window below) and additionally uses the `sx` prop to make more style changes (learn more on the MUI `sx` documentation page).
3381
+
3382
+ **/styles.css**
3383
+
3384
+ ```css
3385
+ body {
3386
+ font-size: 13px;
3387
+ font-weight: 400;
3388
+ letter-spacing: 0.08px;
3389
+ line-height: 20px;
3390
+ text-transform: none;
3391
+ font-family: "Inter", sans-serif;
3392
+ }
3393
+
3394
+ h1 {
3395
+ font-size: 1.5rem;
3396
+ }
3397
+
3398
+ .app {
3399
+ padding: 20px;
3400
+ }
3401
+ ```
3402
+
3403
+ **tsconfig.json**
3404
+
3405
+ ```json
3406
+ {
3407
+ "include": [
3408
+ "./**/*"
3409
+ ],
3410
+ "compilerOptions": {
3411
+ "strict": true,
3412
+ "esModuleInterop": true,
3413
+ "lib": [ "dom", "es2015" ],
3414
+ "jsx": "react-jsx"
3415
+ }
3416
+ }
3417
+ ```
3418
+
3419
+ **/App.tsx**
3420
+
3421
+ ```tsx
3422
+ // DropdownMenu with custom `PopperBaseProps`
3423
+
3424
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3425
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3426
+ import "./styles.css";
3427
+
3428
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3429
+ {
3430
+ name: "Menu item 1",
3431
+ },
3432
+ {
3433
+ name: "Menu item 2",
3434
+ },
3435
+ {
3436
+ name: "Menu item 3",
3437
+ },
3438
+ {
3439
+ name: "Longer menu item than the others",
3440
+ },
3441
+ ];
3442
+
3443
+ const POPPER_BASE_PROPS = {
3444
+ className: "popper",
3445
+ sx: {
3446
+ width: 500,
3447
+ borderColor: "salmon",
3448
+ borderWidth: 10,
3449
+ },
3450
+ popperOptions: { strategy: "absolute"},
3451
+ }
3452
+
3453
+ function App() {
3454
+ const ref = useRef(null);
3455
+ const [open, setOpen] = useState(false);
3456
+
3457
+ useEffect(() => {
3458
+ if (ref.current) setOpen(true);
3459
+ }, [ref.current])
3460
+
3461
+ return (
3462
+ <div className="app" style={{paddingLeft: "10px"}}>
3463
+ <div ref={ref} />
3464
+ {open &&
3465
+ (
3466
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3467
+ <DropdownMenu
3468
+ PopperBaseProps={POPPER_BASE_PROPS}
3469
+ anchorEl={ref.current}
3470
+ onClickAway={function handleClickAway(){}}
3471
+ open
3472
+ options={MENU_ITEMS}
3473
+ PopperBaseProps={POPPER_BASE_PROPS}
3474
+ />
3475
+ )
3476
+ }
3477
+ </div>
3478
+ );
3479
+ }
3480
+
3481
+ export default App;
3482
+ ```
3483
+
3484
+ **/index.tsx**
3485
+
3486
+ ```tsx
3487
+ import React from "react";
3488
+ import { createRoot } from "react-dom/client";
3489
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3490
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3491
+ import { Theme } from "@czi-sds/components";
3492
+ import CssBaseline from "@mui/material/CssBaseline";
3493
+ import useMediaQuery from "@mui/material/useMediaQuery";
3494
+
3495
+ import App from "./App";
3496
+
3497
+ const container = document.getElementById("root");
3498
+ const root = createRoot(container!);
3499
+
3500
+ const RootApp = () => {
3501
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3502
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3503
+
3504
+ return (
3505
+ <StyledEngineProvider injectFirst>
3506
+ <ThemeProvider theme={theme}>
3507
+ <EmotionThemeProvider theme={theme}>
3508
+ <CssBaseline />
3509
+ <App />
3510
+ </EmotionThemeProvider>
3511
+ </ThemeProvider>
3512
+ </StyledEngineProvider>
3513
+ );
3514
+ };
3515
+
3516
+ root.render(
3517
+ <React.StrictMode>
3518
+ <RootApp />
3519
+ </React.StrictMode>
3520
+ );
3521
+
3522
+ ```
3523
+
3524
+ **/public/index.html**
3525
+
3526
+ ```html
3527
+ <!DOCTYPE html>
3528
+ <html lang="en">
3529
+ <head>
3530
+ <meta charset="UTF-8" />
3531
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3532
+ <title>DropdownMenu</title>
3533
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3534
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3535
+ <link
3536
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3537
+ rel="stylesheet"
3538
+ />
3539
+ <link
3540
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3541
+ rel="stylesheet"
3542
+ />
3543
+ </head>
3544
+ <body>
3545
+ <div id="root"></div>
3546
+ </body>
3547
+ </html>
3548
+
3549
+ ```
3550
+
3551
+ **/package.json**
3552
+
3553
+ ```json
3554
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
3555
+ ```
3556
+
3557
+ ---
3558
+
3559
+ #### **DropdownMenu with custom Popper component**
3560
+
3561
+ In addition to modifying the `Popper`'s styles via `PopperBaseProps`, the styles for the `Popper` component can be overwritten more directly with `styled()`.
3562
+
3563
+ To get all the default SDS styles used for `Popper`, first copy them into a custom-styled `Popper` (from within sci-components/packages/components/src/core/DropdownMenu/style.ts file). Then replace and / or follow up those styles with custom styles. The example below includes comments for each of these steps within `CustomPopper`.
3564
+
3565
+ **/styles.css**
3566
+
3567
+ ```css
3568
+ body {
3569
+ font-size: 13px;
3570
+ font-weight: 400;
3571
+ letter-spacing: 0.08px;
3572
+ line-height: 20px;
3573
+ text-transform: none;
3574
+ font-family: "Inter", sans-serif;
3575
+ }
3576
+
3577
+ h1 {
3578
+ font-size: 1.5rem;
3579
+ }
3580
+
3581
+ .app {
3582
+ padding: 20px;
3583
+ }
3584
+ ```
3585
+
3586
+ **tsconfig.json**
3587
+
3588
+ ```json
3589
+ {
3590
+ "include": [
3591
+ "./**/*"
3592
+ ],
3593
+ "compilerOptions": {
3594
+ "strict": true,
3595
+ "esModuleInterop": true,
3596
+ "lib": [ "dom", "es2015" ],
3597
+ "jsx": "react-jsx"
3598
+ }
3599
+ }
3600
+ ```
3601
+
3602
+ **/App.tsx**
3603
+
3604
+ ```tsx
3605
+ // DropdownMenu with custom Popper component
3606
+
3607
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3608
+ import { Popper, styled } from "@mui/material";
3609
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3610
+ import "./styles.css";
3611
+
3612
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3613
+ {
3614
+ name: "Menu item 1",
3615
+ },
3616
+ {
3617
+ name: "Menu item 2",
3618
+ },
3619
+ {
3620
+ name: "Menu item 3",
3621
+ },
3622
+ {
3623
+ name: "Longer menu item than the others",
3624
+ },
3625
+ ];
3626
+
3627
+ // Step 1/3: Copy existing base styles for the Popper component from
3628
+ // sci-components/packages/components/src/core/DropdownMenu/style.ts, ~lines 206-229:
3629
+ const getBorders = ({ theme }: CommonThemeProps): Borders | null => {
3630
+ return theme?.app?.borders || null;
3631
+ };
3632
+
3633
+ const getCorners = ({ theme }: CommonThemeProps): Corners | null => {
3634
+ return theme?.app?.corners || null;
3635
+ };
3636
+
3637
+ const getShadows = ({ theme }: CommonThemeProps): Shadows | null => {
3638
+ return theme?.app?.shadows || null;
3639
+ };
3640
+
3641
+ const getSpaces = ({ theme }: CommonThemeProps): Spaces | null => {
3642
+ return theme?.app?.spacing || null;
3643
+ };
3644
+
3645
+ const CustomPopper = styled(Popper)`
3646
+ .MuiAutocomplete-popperDisablePortal {
3647
+ position: relative;
3648
+ width: 100% !important;
3649
+ box-shadow: none;
3650
+ padding: 0;
3651
+ border: none;
3652
+ }
3653
+
3654
+ ${(props) => {
3655
+ const borders = getBorders(props);
3656
+ const corners = getCorners(props);
3657
+ const shadows = getShadows(props);
3658
+ const spacings = getSpaces(props);
3659
+
3660
+ return `
3661
+ background-color: white;
3662
+ border: ${borders?.base?.divider};
3663
+ border-radius: ${corners?.m}px;
3664
+ box-shadow: ${shadows?.m};
3665
+ padding: ${spacings?.xs}px;
3666
+ box-sizing: border-box;
3667
+ z-index: 1400;
3668
+ `;
3669
+ }}
3670
+
3671
+ // Step 2/3: Modify or add to the Popper component's base styles (copied in above)
3672
+ // by putting your custom styles at the end:
3673
+
3674
+ border-color: rebeccapurple;
3675
+ border-style: dotted;
3676
+ border-width: 15px !important;
3677
+ `;
3678
+
3679
+ const POPPER_BASE_PROPS = {popperOptions: { strategy: "absolute"}};
3680
+
3681
+ function App() {
3682
+ const ref = useRef(null);
3683
+ const [open, setOpen] = useState(false);
3684
+
3685
+ useEffect(() => {
3686
+ if (ref.current) setOpen(true);
3687
+ }, [ref.current])
3688
+
3689
+ return (
3690
+ <div className="app" style={{paddingLeft: "10px"}}>
3691
+ <div ref={ref} />
3692
+ {open &&
3693
+ (
3694
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3695
+ <DropdownMenu
3696
+ PopperBaseProps={POPPER_BASE_PROPS}
3697
+ anchorEl={ref.current}
3698
+ onClickAway={function handleClickAway(){}}
3699
+ open
3700
+ options={MENU_ITEMS}
3701
+ // Step 3/3: Pass the new CustomPopper to PopperComponent prop
3702
+ PopperComponent={CustomPopper}
3703
+ />
3704
+ )
3705
+ }
3706
+ </div>
3707
+ );
3708
+ }
3709
+
3710
+ export default App;
3711
+ ```
3712
+
3713
+ **/index.tsx**
3714
+
3715
+ ```tsx
3716
+ import React from "react";
3717
+ import { createRoot } from "react-dom/client";
3718
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3719
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3720
+ import { Theme } from "@czi-sds/components";
3721
+ import CssBaseline from "@mui/material/CssBaseline";
3722
+ import useMediaQuery from "@mui/material/useMediaQuery";
3723
+
3724
+ import App from "./App";
3725
+
3726
+ const container = document.getElementById("root");
3727
+ const root = createRoot(container!);
3728
+
3729
+ const RootApp = () => {
3730
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3731
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3732
+
3733
+ return (
3734
+ <StyledEngineProvider injectFirst>
3735
+ <ThemeProvider theme={theme}>
3736
+ <EmotionThemeProvider theme={theme}>
3737
+ <CssBaseline />
3738
+ <App />
3739
+ </EmotionThemeProvider>
3740
+ </ThemeProvider>
3741
+ </StyledEngineProvider>
3742
+ );
3743
+ };
3744
+
3745
+ root.render(
3746
+ <React.StrictMode>
3747
+ <RootApp />
3748
+ </React.StrictMode>
3749
+ );
3750
+
3751
+ ```
3752
+
3753
+ **/public/index.html**
3754
+
3755
+ ```html
3756
+ <!DOCTYPE html>
3757
+ <html lang="en">
3758
+ <head>
3759
+ <meta charset="UTF-8" />
3760
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3761
+ <title>DropdownMenu</title>
3762
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3763
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3764
+ <link
3765
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3766
+ rel="stylesheet"
3767
+ />
3768
+ <link
3769
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3770
+ rel="stylesheet"
3771
+ />
3772
+ </head>
3773
+ <body>
3774
+ <div id="root"></div>
3775
+ </body>
3776
+ </html>
3777
+
3778
+ ```
3779
+
3780
+ **/package.json**
3781
+
3782
+ ```json
3783
+ {"dependencies":{"react":"^18.0.0","react-dom":"^18.0.0","react-scripts":"^4.0.0"},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","typescript":"^4.0.0"},"main":"/index.tsx"}
3784
+ ```
3785
+
3786
+ ---
3787
+