@czi-sds/mcp 0.2.2 → 0.4.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 (147) hide show
  1. package/data/component-props/Accordion.json +14 -15
  2. package/data/component-props/Alert.json +2 -44
  3. package/data/component-props/Autocomplete.json +14 -32
  4. package/data/component-props/Banner.json +11 -12
  5. package/data/component-props/Button.json +17 -82
  6. package/data/component-props/ButtonDropdown.json +26 -0
  7. package/data/component-props/ButtonIcon.json +4 -60
  8. package/data/component-props/ButtonToggle.json +2 -66
  9. package/data/component-props/Callout.json +11 -65
  10. package/data/component-props/CellBasic.json +28 -56
  11. package/data/component-props/CellComponent.json +4 -32
  12. package/data/component-props/CellHeader.json +13 -49
  13. package/data/component-props/ComplexFilter.json +24 -4
  14. package/data/component-props/ContentCard.json +66 -2
  15. package/data/component-props/Dialog.json +26 -0
  16. package/data/component-props/Dropdown.json +29 -67
  17. package/data/component-props/DropdownMenu.json +16 -56
  18. package/data/component-props/HeatmapChart.json +6 -87
  19. package/data/component-props/Icon.json +3 -8
  20. package/data/component-props/InputCheckbox.json +5 -53
  21. package/data/component-props/InputDropdown.json +11 -33
  22. package/data/component-props/InputRadio.json +5 -53
  23. package/data/component-props/InputSearch.json +6 -70
  24. package/data/component-props/InputSlider.json +21 -35
  25. package/data/component-props/InputText.json +7 -63
  26. package/data/component-props/InputToggle.json +5 -54
  27. package/data/component-props/Link.json +18 -0
  28. package/data/component-props/LoadingIndicator.json +1 -2
  29. package/data/component-props/MenuItem.json +11 -41
  30. package/data/component-props/MenuSelect.json +4 -67
  31. package/data/component-props/NavigationFooter.json +0 -8
  32. package/data/component-props/NavigationHeader.json +14 -65
  33. package/data/component-props/NavigationJumpTo.json +0 -40
  34. package/data/component-props/Notification.json +14 -58
  35. package/data/component-props/Pagination.json +8 -33
  36. package/data/component-props/Panel.json +4 -54
  37. package/data/component-props/SegmentedControl.json +0 -55
  38. package/data/component-props/TableRow.json +10 -38
  39. package/data/component-props/Tabs.json +18 -0
  40. package/data/component-props/Tag.json +10 -56
  41. package/data/component-props/TagFilter.json +0 -47
  42. package/data/component-props/Tooltip.json +12 -61
  43. package/data/component-props/TooltipCondensed.json +18 -0
  44. package/data/component-props/TooltipTable.json +0 -4
  45. package/data/zeroheight-cache.json +5195 -0
  46. package/data/zeroheight-exports/Accordion.md +717 -0
  47. package/data/zeroheight-exports/Banner.md +599 -0
  48. package/data/zeroheight-exports/Borders.md +117 -0
  49. package/data/zeroheight-exports/Buttons.md +2305 -0
  50. package/data/zeroheight-exports/Callout.md +778 -0
  51. package/data/zeroheight-exports/Colors.md +632 -0
  52. package/data/zeroheight-exports/Content-Card.md +161 -0
  53. package/data/zeroheight-exports/Contributing-to-SDS.md +98 -0
  54. package/data/zeroheight-exports/Control-Inputs.md +2121 -0
  55. package/data/zeroheight-exports/Corners.md +111 -0
  56. package/data/zeroheight-exports/Cover-page.md +3 -0
  57. package/data/zeroheight-exports/Dialog.md +1191 -0
  58. package/data/zeroheight-exports/Downloads.md +120 -0
  59. package/data/zeroheight-exports/Drop-Shadows.md +79 -0
  60. package/data/zeroheight-exports/Dropdown-Input.md +1180 -0
  61. package/data/zeroheight-exports/Dropdown-Menu.md +3883 -0
  62. package/data/zeroheight-exports/Element-Status-Tracker.md +267 -0
  63. package/data/zeroheight-exports/Field-Inputs.md +984 -0
  64. package/data/zeroheight-exports/Filters.md +888 -0
  65. package/data/zeroheight-exports/Getting-Started.md +1159 -0
  66. package/data/zeroheight-exports/Hero.md +262 -0
  67. package/data/zeroheight-exports/Icons.md +431 -0
  68. package/data/zeroheight-exports/Intent.md +46 -0
  69. package/data/zeroheight-exports/Introduction-to-Bases.md +35 -0
  70. package/data/zeroheight-exports/Introduction-to-Cells.md +35 -0
  71. package/data/zeroheight-exports/Introduction-to-DNA.md +35 -0
  72. package/data/zeroheight-exports/Introduction-to-Genes.md +35 -0
  73. package/data/zeroheight-exports/Introduction.md +115 -0
  74. package/data/zeroheight-exports/Link.md +386 -0
  75. package/data/zeroheight-exports/Lists.md +632 -0
  76. package/data/zeroheight-exports/Loading-Indicator.md +37 -0
  77. package/data/zeroheight-exports/Navigation.md +874 -0
  78. package/data/zeroheight-exports/Notification.md +888 -0
  79. package/data/zeroheight-exports/Panel.md +978 -0
  80. package/data/zeroheight-exports/README.md +124 -0
  81. package/data/zeroheight-exports/Responsive-Design.md +278 -0
  82. package/data/zeroheight-exports/Search-Input.md +53 -0
  83. package/data/zeroheight-exports/Search.md +592 -0
  84. package/data/zeroheight-exports/Segmented-Control.md +584 -0
  85. package/data/zeroheight-exports/Spacing.md +297 -0
  86. package/data/zeroheight-exports/Table.md +4944 -0
  87. package/data/zeroheight-exports/Tabs.md +801 -0
  88. package/data/zeroheight-exports/Tags.md +1055 -0
  89. package/data/zeroheight-exports/Text-Link.md +184 -0
  90. package/data/zeroheight-exports/Theming.md +332 -0
  91. package/data/zeroheight-exports/Tooltips.md +1580 -0
  92. package/data/zeroheight-exports/Typography.md +768 -0
  93. package/dist/stdio.js +193 -93
  94. package/package.json +4 -4
  95. package/data/component-props/Chip.json +0 -73
  96. package/data/component-props/List.json +0 -43
  97. package/data/component-props/Menu.json +0 -48
  98. package/data/component-props/Table.json +0 -26
  99. package/data/component-props-storybook/Accordion-storybook.json +0 -45
  100. package/data/component-props-storybook/Alert-storybook.json +0 -30
  101. package/data/component-props-storybook/Autocomplete-storybook.json +0 -67
  102. package/data/component-props-storybook/Banner-storybook.json +0 -78
  103. package/data/component-props-storybook/Button-storybook.json +0 -115
  104. package/data/component-props-storybook/ButtonDropdown-storybook.json +0 -73
  105. package/data/component-props-storybook/ButtonIcon-storybook.json +0 -60
  106. package/data/component-props-storybook/ButtonToggle-storybook.json +0 -68
  107. package/data/component-props-storybook/Callout-storybook.json +0 -90
  108. package/data/component-props-storybook/CellBasic-storybook.json +0 -138
  109. package/data/component-props-storybook/CellComponent-storybook.json +0 -48
  110. package/data/component-props-storybook/CellHeader-storybook.json +0 -71
  111. package/data/component-props-storybook/Chip-storybook.json +0 -26
  112. package/data/component-props-storybook/ComplexFilter-storybook.json +0 -60
  113. package/data/component-props-storybook/ContentCard-storybook.json +0 -175
  114. package/data/component-props-storybook/Dialog-storybook.json +0 -56
  115. package/data/component-props-storybook/Dropdown-storybook.json +0 -93
  116. package/data/component-props-storybook/DropdownMenu-storybook.json +0 -73
  117. package/data/component-props-storybook/HeatmapChart-storybook.json +0 -55
  118. package/data/component-props-storybook/Icon-storybook.json +0 -75
  119. package/data/component-props-storybook/InputCheckbox-storybook.json +0 -57
  120. package/data/component-props-storybook/InputDropdown-storybook.json +0 -80
  121. package/data/component-props-storybook/InputRadio-storybook.json +0 -56
  122. package/data/component-props-storybook/InputSearch-storybook.json +0 -55
  123. package/data/component-props-storybook/InputSlider-storybook.json +0 -55
  124. package/data/component-props-storybook/InputText-storybook.json +0 -59
  125. package/data/component-props-storybook/InputToggle-storybook.json +0 -42
  126. package/data/component-props-storybook/Link-storybook.json +0 -50
  127. package/data/component-props-storybook/List-storybook.json +0 -26
  128. package/data/component-props-storybook/LoadingIndicator-storybook.json +0 -34
  129. package/data/component-props-storybook/Menu-storybook.json +0 -26
  130. package/data/component-props-storybook/MenuItem-storybook.json +0 -74
  131. package/data/component-props-storybook/MenuSelect-storybook.json +0 -38
  132. package/data/component-props-storybook/NavigationFooter-storybook.json +0 -67
  133. package/data/component-props-storybook/NavigationHeader-storybook.json +0 -115
  134. package/data/component-props-storybook/NavigationJumpTo-storybook.json +0 -41
  135. package/data/component-props-storybook/Notification-storybook.json +0 -89
  136. package/data/component-props-storybook/Pagination-storybook.json +0 -50
  137. package/data/component-props-storybook/Panel-storybook.json +0 -52
  138. package/data/component-props-storybook/SegmentedControl-storybook.json +0 -30
  139. package/data/component-props-storybook/Table-storybook.json +0 -26
  140. package/data/component-props-storybook/TableHeader-storybook.json +0 -26
  141. package/data/component-props-storybook/TableRow-storybook.json +0 -62
  142. package/data/component-props-storybook/Tabs-storybook.json +0 -45
  143. package/data/component-props-storybook/Tag-storybook.json +0 -78
  144. package/data/component-props-storybook/TagFilter-storybook.json +0 -30
  145. package/data/component-props-storybook/Tooltip-storybook.json +0 -85
  146. package/data/component-props-storybook/TooltipCondensed-storybook.json +0 -38
  147. package/data/component-props-storybook/TooltipTable-storybook.json +0 -47
@@ -0,0 +1,3883 @@
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
+ "compilerOptions": {
735
+ "strict": true,
736
+ "esModuleInterop": true,
737
+ "lib": ["dom", "es2015"],
738
+ "jsx": "react-jsx"
739
+ }
740
+ }
741
+ ```
742
+
743
+ **/App.tsx**
744
+
745
+ ```tsx
746
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
747
+ import { Dropdown, DefaultDropdownMenuOption } from "@czi-sds/components";
748
+ import "./styles.css";
749
+
750
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
751
+ {
752
+ name: "Menu item 1",
753
+ },
754
+ {
755
+ name: "Menu item 2",
756
+ },
757
+ {
758
+ name: "Menu item 3",
759
+ },
760
+ {
761
+ name: "Menu item 4",
762
+ },
763
+ ];
764
+
765
+ function App() {
766
+ return (
767
+ <div className="app">
768
+ <Dropdown label="Click Target" onChange={() => {}} options={MENU_ITEMS} />
769
+ </div>
770
+ );
771
+ }
772
+
773
+ export default App;
774
+ ```
775
+
776
+ **/index.tsx**
777
+
778
+ ```tsx
779
+ import React from "react";
780
+ import { createRoot } from "react-dom/client";
781
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
782
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
783
+ import { Theme } from "@czi-sds/components";
784
+ import CssBaseline from "@mui/material/CssBaseline";
785
+ import useMediaQuery from "@mui/material/useMediaQuery";
786
+
787
+ import App from "./App";
788
+
789
+ const container = document.getElementById("root");
790
+ const root = createRoot(container!);
791
+
792
+ const RootApp = () => {
793
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
794
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
795
+
796
+ return (
797
+ <StyledEngineProvider injectFirst>
798
+ <ThemeProvider theme={theme}>
799
+ <EmotionThemeProvider theme={theme}>
800
+ <CssBaseline />
801
+ <App />
802
+ </EmotionThemeProvider>
803
+ </ThemeProvider>
804
+ </StyledEngineProvider>
805
+ );
806
+ };
807
+
808
+ root.render(
809
+ <React.StrictMode>
810
+ <RootApp />
811
+ </React.StrictMode>
812
+ );
813
+ ```
814
+
815
+ **/public/index.html**
816
+
817
+ ```html
818
+ <!doctype html>
819
+ <html lang="en">
820
+ <head>
821
+ <meta charset="UTF-8" />
822
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
823
+ <title>Dropdown</title>
824
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
825
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
826
+ <link
827
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
828
+ rel="stylesheet"
829
+ />
830
+ <link
831
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
832
+ rel="stylesheet"
833
+ />
834
+ </head>
835
+ <body>
836
+ <div id="root"></div>
837
+ </body>
838
+ </html>
839
+ ```
840
+
841
+ **/package.json**
842
+
843
+ ```json
844
+ {
845
+ "dependencies": {
846
+ "react": "^18.0.0",
847
+ "react-dom": "^18.0.0",
848
+ "react-scripts": "^4.0.0"
849
+ },
850
+ "devDependencies": {
851
+ "@types/react": "^18.0.0",
852
+ "@types/react-dom": "^18.0.0",
853
+ "typescript": "^4.0.0"
854
+ },
855
+ "main": "/index.tsx"
856
+ }
857
+ ```
858
+
859
+ ---
860
+
861
+ #### **Multi-select Dropdown with Search**
862
+
863
+ A search feature can be easily incorporated into the `Dropdown` component by including the `search` prop.
864
+
865
+ 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.
866
+
867
+ 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.
868
+
869
+ **/styles.css**
870
+
871
+ ```css
872
+ body {
873
+ font-size: 13px;
874
+ font-weight: 400;
875
+ letter-spacing: 0.08px;
876
+ line-height: 20px;
877
+ text-transform: none;
878
+ font-family: "Inter", sans-serif;
879
+ }
880
+
881
+ h1 {
882
+ font-size: 1.5rem;
883
+ }
884
+
885
+ .app {
886
+ padding: 20px;
887
+ }
888
+ ```
889
+
890
+ **tsconfig.json**
891
+
892
+ ```json
893
+ {
894
+ "include": ["./**/*"],
895
+ "compilerOptions": {
896
+ "strict": true,
897
+ "esModuleInterop": true,
898
+ "lib": ["dom", "es2015"],
899
+ "jsx": "react-jsx"
900
+ }
901
+ }
902
+ ```
903
+
904
+ **/App.tsx**
905
+
906
+ ```tsx
907
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
908
+ import { Dropdown, DefaultDropdownMenuOption } from "@czi-sds/components";
909
+ import "./styles.css";
910
+
911
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
912
+ {
913
+ name: "Fruit: Apple",
914
+ section: "Fruit",
915
+ count: 10,
916
+ },
917
+ {
918
+ name: "Fruit: Cherry",
919
+ section: "Fruit",
920
+ count: 150,
921
+ },
922
+ {
923
+ name: "Fruit: Orange",
924
+ section: "Fruit",
925
+ count: 15,
926
+ },
927
+ {
928
+ name: "Vegetable: Carrot",
929
+ section: "Vegetable",
930
+ count: 34,
931
+ },
932
+ {
933
+ name: "Vegetable: Kale",
934
+ section: "Vegetable",
935
+ },
936
+ {
937
+ name: "Vegetable: Lettuce",
938
+ section: "Vegetable",
939
+ },
940
+ ];
941
+
942
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
943
+
944
+ function handleClickAway() {}
945
+
946
+ function App() {
947
+ const ref = useRef(null);
948
+ const [open, setOpen] = useState(false);
949
+
950
+ useEffect(() => {
951
+ if (ref.current) setOpen(true);
952
+ }, [ref.current]);
953
+
954
+ return (
955
+ <div className="app">
956
+ <Dropdown
957
+ label="Click Target"
958
+ onChange={() => {}}
959
+ options={MENU_ITEMS}
960
+ search
961
+ multiple
962
+ DropdownMenuProps={{
963
+ keepSearchOnSelect: true,
964
+ groupBy: (option: DefaultDropdownMenuOption) =>
965
+ option.section as string,
966
+ }}
967
+ />
968
+ </div>
969
+ );
970
+ }
971
+
972
+ export default App;
973
+ ```
974
+
975
+ **/index.tsx**
976
+
977
+ ```tsx
978
+ import React from "react";
979
+ import { createRoot } from "react-dom/client";
980
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
981
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
982
+ import { Theme } from "@czi-sds/components";
983
+ import CssBaseline from "@mui/material/CssBaseline";
984
+ import useMediaQuery from "@mui/material/useMediaQuery";
985
+
986
+ import App from "./App";
987
+
988
+ const container = document.getElementById("root");
989
+ const root = createRoot(container!);
990
+
991
+ const RootApp = () => {
992
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
993
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
994
+
995
+ return (
996
+ <StyledEngineProvider injectFirst>
997
+ <ThemeProvider theme={theme}>
998
+ <EmotionThemeProvider theme={theme}>
999
+ <CssBaseline />
1000
+ <App />
1001
+ </EmotionThemeProvider>
1002
+ </ThemeProvider>
1003
+ </StyledEngineProvider>
1004
+ );
1005
+ };
1006
+
1007
+ root.render(
1008
+ <React.StrictMode>
1009
+ <RootApp />
1010
+ </React.StrictMode>
1011
+ );
1012
+ ```
1013
+
1014
+ **/public/index.html**
1015
+
1016
+ ```html
1017
+ <!doctype html>
1018
+ <html lang="en">
1019
+ <head>
1020
+ <meta charset="UTF-8" />
1021
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1022
+ <title>Dropdown</title>
1023
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1024
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1025
+ <link
1026
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1027
+ rel="stylesheet"
1028
+ />
1029
+ <link
1030
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1031
+ rel="stylesheet"
1032
+ />
1033
+ </head>
1034
+ <body>
1035
+ <div id="root"></div>
1036
+ </body>
1037
+ </html>
1038
+ ```
1039
+
1040
+ **/package.json**
1041
+
1042
+ ```json
1043
+ {
1044
+ "dependencies": {
1045
+ "react": "^18.0.0",
1046
+ "react-dom": "^18.0.0",
1047
+ "react-scripts": "^4.0.0"
1048
+ },
1049
+ "devDependencies": {
1050
+ "@types/react": "^18.0.0",
1051
+ "@types/react-dom": "^18.0.0",
1052
+ "typescript": "^4.0.0"
1053
+ },
1054
+ "main": "/index.tsx"
1055
+ }
1056
+ ```
1057
+
1058
+ ---
1059
+
1060
+ ---
1061
+
1062
+ ## DropdownMenu
1063
+
1064
+ ### SDS Source Code
1065
+
1066
+ The component's source code in the SDS codebase can be found here.
1067
+
1068
+ ### SDS vs MUI
1069
+
1070
+ 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:
1071
+
1072
+ - 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.
1073
+ - SDS `DropdownMenu` uses MUI `Popper`. A custom `Popper` component can be passed via `props.PopperComponent` and/or `props.PopperBaseProps` to control the popper component.
1074
+ - Similarly, SDS `DropdownMenu` uses MUI `Paper`. A custom `Paper` component can be passed via `props.PaperComponent` to enable custom styling.
1075
+
1076
+ ### MUI Documentation
1077
+
1078
+ Documentation for the underlying MUI `Autocomplete` component can be found here.
1079
+
1080
+ ### Props
1081
+
1082
+ Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
1083
+
1084
+ | **Name** | **Type** | **Default** | **Description** |
1085
+ | ------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------ |
1086
+ | `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 |
1087
+ | `clickAwayListenerProps` | `func` | `-` | Passes props to the MUI `ClickAwayListener` |
1088
+ | `groupBy` | `func` | `-` | Groups menu items according to their assigned `section` |
1089
+ | `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 |
1090
+ | `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) |
1091
+ | `multiple` | `bool` | `false` | Allows user to select multiple menu items; see more in the MUI Autocomplete API |
1092
+ | `noOptionsText` | `string` | `"No options"` | Sets the text that displays if there are no matching search results; see more in the MUI Autocomplete API |
1093
+ | `onClickAway` | `func` | `-` | Sets what happens when a user clicks outside of the DropdownMenu |
1094
+ | `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 |
1095
+ | `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 |
1096
+ | `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 |
1097
+ | `search` | `bool` | `f``alse` | Provides a search bar at the top of the menu from which users can narrow the list of menu items |
1098
+ | `title` | `string` | `-` | Sets a heading for the menu |
1099
+
1100
+ There are many more props that can be used with the `DropdownMenu` component, via those available to MUI's `Autocomplete` component.
1101
+
1102
+ > **Note for use with SDS** **`Table`** **and other components:**
1103
+ >
1104
+ > 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.
1105
+
1106
+ ### Code Examples
1107
+
1108
+ #### Default DropdownMenu
1109
+
1110
+ This example has the minimum props and menu item metadata needed for the `DropdownMenu`.
1111
+
1112
+ 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.
1113
+
1114
+ The minimum width for the `DropdownMenu` is 225px, but longer text will automatically widen the `DropdownMenu` up to the length of the text provided.
1115
+
1116
+ Although examples shows the minimum requirements for the `DropdownMenu`, there are many more options available to include; see subsequent code examples further below.
1117
+
1118
+ **/styles.css**
1119
+
1120
+ ```css
1121
+ body {
1122
+ font-size: 13px;
1123
+ font-weight: 400;
1124
+ letter-spacing: 0.08px;
1125
+ line-height: 20px;
1126
+ text-transform: none;
1127
+ font-family: "Inter", sans-serif;
1128
+ }
1129
+
1130
+ h1 {
1131
+ font-size: 1.5rem;
1132
+ }
1133
+
1134
+ .app {
1135
+ padding: 20px;
1136
+ }
1137
+ ```
1138
+
1139
+ **tsconfig.json**
1140
+
1141
+ ```json
1142
+ {
1143
+ "include": ["./**/*"],
1144
+ "compilerOptions": {
1145
+ "strict": true,
1146
+ "esModuleInterop": true,
1147
+ "lib": ["dom", "es2015"],
1148
+ "jsx": "react-jsx"
1149
+ }
1150
+ }
1151
+ ```
1152
+
1153
+ **/App.tsx**
1154
+
1155
+ ```tsx
1156
+ // Most minimal DropdownMenu (just has the basic requirements)
1157
+
1158
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1159
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1160
+ import "./styles.css";
1161
+
1162
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1163
+ {
1164
+ name: "Menu item 1",
1165
+ },
1166
+ {
1167
+ name: "Menu item 2",
1168
+ },
1169
+ {
1170
+ name: "Menu item 3",
1171
+ },
1172
+ {
1173
+ name: "Longer menu item than the others",
1174
+ },
1175
+ ];
1176
+
1177
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
1178
+
1179
+ function handleClickAway() {}
1180
+
1181
+ function App() {
1182
+ const ref = useRef(null);
1183
+ const [open, setOpen] = useState(false);
1184
+
1185
+ useEffect(() => {
1186
+ if (ref.current) setOpen(true);
1187
+ }, [ref.current]);
1188
+
1189
+ return (
1190
+ <div className="app" style={{ paddingLeft: "10px" }}>
1191
+ <div ref={ref} />
1192
+ {open && (
1193
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1194
+ <DropdownMenu
1195
+ PopperBaseProps={POPPER_BASE_PROPS}
1196
+ anchorEl={ref.current}
1197
+ onClickAway={handleClickAway}
1198
+ open={open}
1199
+ options={MENU_ITEMS}
1200
+ />
1201
+ )}
1202
+ </div>
1203
+ );
1204
+ }
1205
+
1206
+ export default App;
1207
+ ```
1208
+
1209
+ **/index.tsx**
1210
+
1211
+ ```tsx
1212
+ import React from "react";
1213
+ import { createRoot } from "react-dom/client";
1214
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1215
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1216
+ import { Theme } from "@czi-sds/components";
1217
+ import CssBaseline from "@mui/material/CssBaseline";
1218
+ import useMediaQuery from "@mui/material/useMediaQuery";
1219
+
1220
+ import App from "./App";
1221
+
1222
+ const container = document.getElementById("root");
1223
+ const root = createRoot(container!);
1224
+
1225
+ const RootApp = () => {
1226
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1227
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1228
+
1229
+ return (
1230
+ <StyledEngineProvider injectFirst>
1231
+ <ThemeProvider theme={theme}>
1232
+ <EmotionThemeProvider theme={theme}>
1233
+ <CssBaseline />
1234
+ <App />
1235
+ </EmotionThemeProvider>
1236
+ </ThemeProvider>
1237
+ </StyledEngineProvider>
1238
+ );
1239
+ };
1240
+
1241
+ root.render(
1242
+ <React.StrictMode>
1243
+ <RootApp />
1244
+ </React.StrictMode>
1245
+ );
1246
+ ```
1247
+
1248
+ **/public/index.html**
1249
+
1250
+ ```html
1251
+ <!doctype html>
1252
+ <html lang="en">
1253
+ <head>
1254
+ <meta charset="UTF-8" />
1255
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1256
+ <title>Dropdown</title>
1257
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1258
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1259
+ <link
1260
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1261
+ rel="stylesheet"
1262
+ />
1263
+ <link
1264
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1265
+ rel="stylesheet"
1266
+ />
1267
+ </head>
1268
+ <body>
1269
+ <div id="root"></div>
1270
+ </body>
1271
+ </html>
1272
+ ```
1273
+
1274
+ **/package.json**
1275
+
1276
+ ```json
1277
+ {
1278
+ "dependencies": {
1279
+ "react": "^18.0.0",
1280
+ "react-dom": "^18.0.0",
1281
+ "react-scripts": "^4.0.0"
1282
+ },
1283
+ "devDependencies": {
1284
+ "@types/react": "^18.0.0",
1285
+ "@types/react-dom": "^18.0.0",
1286
+ "typescript": "^4.0.0"
1287
+ },
1288
+ "main": "/index.tsx"
1289
+ }
1290
+ ```
1291
+
1292
+ ---
1293
+
1294
+ #### DropdownMenu with custom click target
1295
+
1296
+ 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.
1297
+
1298
+ **/styles.css**
1299
+
1300
+ ```css
1301
+ body {
1302
+ font-size: 13px;
1303
+ font-weight: 400;
1304
+ letter-spacing: 0.08px;
1305
+ line-height: 20px;
1306
+ text-transform: none;
1307
+ font-family: "Inter", sans-serif;
1308
+ }
1309
+
1310
+ h1 {
1311
+ font-size: 1.5rem;
1312
+ }
1313
+
1314
+ .app {
1315
+ padding: 20px;
1316
+ }
1317
+ ```
1318
+
1319
+ **tsconfig.json**
1320
+
1321
+ ```json
1322
+ {
1323
+ "include": ["./**/*"],
1324
+ "compilerOptions": {
1325
+ "strict": true,
1326
+ "esModuleInterop": true,
1327
+ "lib": ["dom", "es2015"],
1328
+ "jsx": "react-jsx"
1329
+ }
1330
+ }
1331
+ ```
1332
+
1333
+ **/App.tsx**
1334
+
1335
+ ```tsx
1336
+ // DropdownMenu with custom click target
1337
+
1338
+ import React, { SyntheticEvent, useState, useRef } from "react";
1339
+ import {
1340
+ DropdownMenu,
1341
+ DefaultDropdownMenuOption,
1342
+ InputDropdown,
1343
+ } from "@czi-sds/components";
1344
+ import "./styles.css";
1345
+
1346
+ function App() {
1347
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1348
+ {
1349
+ name: "Menu item 1",
1350
+ },
1351
+ {
1352
+ name: "Menu item 2",
1353
+ },
1354
+ {
1355
+ name: "Menu item 3",
1356
+ },
1357
+ ];
1358
+
1359
+ const [anchorEl, setAnchorEl] = useState(null);
1360
+ const [open, setOpen] = useState(false);
1361
+ const [value, setValue] = useState(null);
1362
+
1363
+ function handleClick(event) {
1364
+ if (open) {
1365
+ setOpen(false);
1366
+ if (anchorEl) {
1367
+ anchorEl.focus();
1368
+ }
1369
+ setAnchorEl(null);
1370
+ } else {
1371
+ setAnchorEl(event.currentTarget);
1372
+ setOpen(true);
1373
+ }
1374
+ }
1375
+
1376
+ function handleChange(_, newValue) {
1377
+ setOpen(false);
1378
+ setValue(newValue);
1379
+ }
1380
+
1381
+ function handleClickAway() {
1382
+ return open && setOpen(false);
1383
+ }
1384
+
1385
+ return (
1386
+ <div className="app">
1387
+ <InputDropdown
1388
+ onClick={handleClick}
1389
+ label="Click Target"
1390
+ sdsType="label"
1391
+ sdsStyle="minimal"
1392
+ />
1393
+ <DropdownMenu
1394
+ options={MENU_ITEMS}
1395
+ anchorEl={anchorEl}
1396
+ open={!!open}
1397
+ onChange={handleChange}
1398
+ value={value}
1399
+ onClickAway={handleClickAway}
1400
+ />
1401
+ </div>
1402
+ );
1403
+ }
1404
+
1405
+ export default App;
1406
+ ```
1407
+
1408
+ **/index.tsx**
1409
+
1410
+ ```tsx
1411
+ import React from "react";
1412
+ import { createRoot } from "react-dom/client";
1413
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1414
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1415
+ import { Theme } from "@czi-sds/components";
1416
+ import CssBaseline from "@mui/material/CssBaseline";
1417
+ import useMediaQuery from "@mui/material/useMediaQuery";
1418
+
1419
+ import App from "./App";
1420
+
1421
+ const container = document.getElementById("root");
1422
+ const root = createRoot(container!);
1423
+
1424
+ const RootApp = () => {
1425
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1426
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1427
+
1428
+ return (
1429
+ <StyledEngineProvider injectFirst>
1430
+ <ThemeProvider theme={theme}>
1431
+ <EmotionThemeProvider theme={theme}>
1432
+ <CssBaseline />
1433
+ <App />
1434
+ </EmotionThemeProvider>
1435
+ </ThemeProvider>
1436
+ </StyledEngineProvider>
1437
+ );
1438
+ };
1439
+
1440
+ root.render(
1441
+ <React.StrictMode>
1442
+ <RootApp />
1443
+ </React.StrictMode>
1444
+ );
1445
+ ```
1446
+
1447
+ **/public/index.html**
1448
+
1449
+ ```html
1450
+ <!doctype html>
1451
+ <html lang="en">
1452
+ <head>
1453
+ <meta charset="UTF-8" />
1454
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1455
+ <title>Dropdown</title>
1456
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1457
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1458
+ <link
1459
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1460
+ rel="stylesheet"
1461
+ />
1462
+ <link
1463
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1464
+ rel="stylesheet"
1465
+ />
1466
+ </head>
1467
+ <body>
1468
+ <div id="root"></div>
1469
+ </body>
1470
+ </html>
1471
+ ```
1472
+
1473
+ **/package.json**
1474
+
1475
+ ```json
1476
+ {
1477
+ "dependencies": {
1478
+ "react": "^18.0.0",
1479
+ "react-dom": "^18.0.0",
1480
+ "react-scripts": "^4.0.0"
1481
+ },
1482
+ "devDependencies": {
1483
+ "@types/react": "^18.0.0",
1484
+ "@types/react-dom": "^18.0.0",
1485
+ "typescript": "^4.0.0"
1486
+ },
1487
+ "main": "/index.tsx"
1488
+ }
1489
+ ```
1490
+
1491
+ ---
1492
+
1493
+ #### DropdownMenu with search
1494
+
1495
+ An `InputSearch` component can be incorporated into the `DropdownMenu` component by passing it the `search` prop.
1496
+
1497
+ 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.
1498
+
1499
+ 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.
1500
+
1501
+ 1. In the search box, type in "fruit"
1502
+ 2. `DropdownMenu` will display "Fruit: Apple" and "Fruit: Orange"
1503
+ 3. Select "Fruit: Apple"
1504
+ 4. Note that the search text is maintained along with the limited list of items
1505
+
1506
+ **/styles.css**
1507
+
1508
+ ```css
1509
+ body {
1510
+ font-size: 13px;
1511
+ font-weight: 400;
1512
+ letter-spacing: 0.08px;
1513
+ line-height: 20px;
1514
+ text-transform: none;
1515
+ font-family: "Inter", sans-serif;
1516
+ }
1517
+
1518
+ h1 {
1519
+ font-size: 1.5rem;
1520
+ }
1521
+
1522
+ .app {
1523
+ padding: 20px;
1524
+ }
1525
+ ```
1526
+
1527
+ **tsconfig.json**
1528
+
1529
+ ```json
1530
+ {
1531
+ "include": ["./**/*"],
1532
+ "compilerOptions": {
1533
+ "strict": true,
1534
+ "esModuleInterop": true,
1535
+ "lib": ["dom", "es2015"],
1536
+ "jsx": "react-jsx"
1537
+ }
1538
+ }
1539
+ ```
1540
+
1541
+ **/App.tsx**
1542
+
1543
+ ```tsx
1544
+ // DropdownMenu with search
1545
+
1546
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1547
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1548
+ import "./styles.css";
1549
+
1550
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1551
+ {
1552
+ name: "Fruit: Apple",
1553
+ },
1554
+ {
1555
+ name: "Fruit: Orange",
1556
+ },
1557
+ {
1558
+ name: "Vegetable: Carrot",
1559
+ },
1560
+ {
1561
+ name: "Vegetable: Kale",
1562
+ },
1563
+ ];
1564
+
1565
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
1566
+
1567
+ function handleClickAway() {}
1568
+
1569
+ function App() {
1570
+ const ref = useRef(null);
1571
+ const [open, setOpen] = useState(false);
1572
+
1573
+ useEffect(() => {
1574
+ if (ref.current) setOpen(true);
1575
+ }, [ref.current]);
1576
+
1577
+ return (
1578
+ <div className="app" style={{ paddingLeft: "10px" }}>
1579
+ <div ref={ref} />
1580
+ {open && (
1581
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1582
+ <DropdownMenu
1583
+ PopperBaseProps={POPPER_BASE_PROPS}
1584
+ anchorEl={ref.current}
1585
+ noOptionsText="No matches found, try again!"
1586
+ onClickAway={handleClickAway}
1587
+ open
1588
+ options={MENU_ITEMS}
1589
+ search
1590
+ />
1591
+ )}
1592
+ </div>
1593
+ );
1594
+ }
1595
+
1596
+ export default App;
1597
+ ```
1598
+
1599
+ **/index.tsx**
1600
+
1601
+ ```tsx
1602
+ import React from "react";
1603
+ import { createRoot } from "react-dom/client";
1604
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1605
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1606
+ import { Theme } from "@czi-sds/components";
1607
+ import CssBaseline from "@mui/material/CssBaseline";
1608
+ import useMediaQuery from "@mui/material/useMediaQuery";
1609
+
1610
+ import App from "./App";
1611
+
1612
+ const container = document.getElementById("root");
1613
+ const root = createRoot(container!);
1614
+
1615
+ const RootApp = () => {
1616
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1617
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1618
+
1619
+ return (
1620
+ <StyledEngineProvider injectFirst>
1621
+ <ThemeProvider theme={theme}>
1622
+ <EmotionThemeProvider theme={theme}>
1623
+ <CssBaseline />
1624
+ <App />
1625
+ </EmotionThemeProvider>
1626
+ </ThemeProvider>
1627
+ </StyledEngineProvider>
1628
+ );
1629
+ };
1630
+
1631
+ root.render(
1632
+ <React.StrictMode>
1633
+ <RootApp />
1634
+ </React.StrictMode>
1635
+ );
1636
+ ```
1637
+
1638
+ **/public/index.html**
1639
+
1640
+ ```html
1641
+ <!doctype html>
1642
+ <html lang="en">
1643
+ <head>
1644
+ <meta charset="UTF-8" />
1645
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1646
+ <title>Dropdown</title>
1647
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1648
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1649
+ <link
1650
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1651
+ rel="stylesheet"
1652
+ />
1653
+ <link
1654
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1655
+ rel="stylesheet"
1656
+ />
1657
+ </head>
1658
+ <body>
1659
+ <div id="root"></div>
1660
+ </body>
1661
+ </html>
1662
+ ```
1663
+
1664
+ **/package.json**
1665
+
1666
+ ```json
1667
+ {
1668
+ "dependencies": {
1669
+ "react": "^18.0.0",
1670
+ "react-dom": "^18.0.0",
1671
+ "react-scripts": "^4.0.0"
1672
+ },
1673
+ "devDependencies": {
1674
+ "@types/react": "^18.0.0",
1675
+ "@types/react-dom": "^18.0.0",
1676
+ "typescript": "^4.0.0"
1677
+ },
1678
+ "main": "/index.tsx"
1679
+ }
1680
+ ```
1681
+
1682
+ ---
1683
+
1684
+ #### DropdownMenu with multi-select
1685
+
1686
+ 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.
1687
+
1688
+ 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.
1689
+
1690
+ **/styles.css**
1691
+
1692
+ ```css
1693
+ body {
1694
+ font-size: 13px;
1695
+ font-weight: 400;
1696
+ letter-spacing: 0.08px;
1697
+ line-height: 20px;
1698
+ text-transform: none;
1699
+ font-family: "Inter", sans-serif;
1700
+ }
1701
+
1702
+ h1 {
1703
+ font-size: 1.5rem;
1704
+ }
1705
+
1706
+ .app {
1707
+ padding: 20px;
1708
+ }
1709
+ ```
1710
+
1711
+ **tsconfig.json**
1712
+
1713
+ ```json
1714
+ {
1715
+ "include": ["./**/*"],
1716
+ "compilerOptions": {
1717
+ "strict": true,
1718
+ "esModuleInterop": true,
1719
+ "lib": ["dom", "es2015"],
1720
+ "jsx": "react-jsx"
1721
+ }
1722
+ }
1723
+ ```
1724
+
1725
+ **/App.tsx**
1726
+
1727
+ ```tsx
1728
+ // DropdownMenu with multi-select
1729
+
1730
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1731
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1732
+ import "./styles.css";
1733
+
1734
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1735
+ {
1736
+ name: "Menu item 1",
1737
+ },
1738
+ {
1739
+ name: "Menu item 2",
1740
+ },
1741
+ {
1742
+ name: "Menu item 3",
1743
+ },
1744
+ ];
1745
+
1746
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
1747
+
1748
+ function handleClickAway() {}
1749
+
1750
+ function App() {
1751
+ const ref = useRef(null);
1752
+ const [open, setOpen] = useState(false);
1753
+
1754
+ useEffect(() => {
1755
+ if (ref.current) setOpen(true);
1756
+ }, [ref.current]);
1757
+
1758
+ return (
1759
+ <div className="app" style={{ paddingLeft: "10px" }}>
1760
+ <div ref={ref} />
1761
+ {open && (
1762
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1763
+ <DropdownMenu
1764
+ PopperBaseProps={POPPER_BASE_PROPS}
1765
+ anchorEl={ref.current}
1766
+ multiple
1767
+ onClickAway={handleClickAway}
1768
+ open
1769
+ options={MENU_ITEMS}
1770
+ />
1771
+ )}
1772
+ </div>
1773
+ );
1774
+ }
1775
+
1776
+ export default App;
1777
+ ```
1778
+
1779
+ **/index.tsx**
1780
+
1781
+ ```tsx
1782
+ import React from "react";
1783
+ import { createRoot } from "react-dom/client";
1784
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1785
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1786
+ import { Theme } from "@czi-sds/components";
1787
+ import CssBaseline from "@mui/material/CssBaseline";
1788
+ import useMediaQuery from "@mui/material/useMediaQuery";
1789
+
1790
+ import App from "./App";
1791
+
1792
+ const container = document.getElementById("root");
1793
+ const root = createRoot(container!);
1794
+
1795
+ const RootApp = () => {
1796
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1797
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1798
+
1799
+ return (
1800
+ <StyledEngineProvider injectFirst>
1801
+ <ThemeProvider theme={theme}>
1802
+ <EmotionThemeProvider theme={theme}>
1803
+ <CssBaseline />
1804
+ <App />
1805
+ </EmotionThemeProvider>
1806
+ </ThemeProvider>
1807
+ </StyledEngineProvider>
1808
+ );
1809
+ };
1810
+
1811
+ root.render(
1812
+ <React.StrictMode>
1813
+ <RootApp />
1814
+ </React.StrictMode>
1815
+ );
1816
+ ```
1817
+
1818
+ **/public/index.html**
1819
+
1820
+ ```html
1821
+ <!doctype html>
1822
+ <html lang="en">
1823
+ <head>
1824
+ <meta charset="UTF-8" />
1825
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1826
+ <title>Dropdown</title>
1827
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
1828
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1829
+ <link
1830
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
1831
+ rel="stylesheet"
1832
+ />
1833
+ <link
1834
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
1835
+ rel="stylesheet"
1836
+ />
1837
+ </head>
1838
+ <body>
1839
+ <div id="root"></div>
1840
+ </body>
1841
+ </html>
1842
+ ```
1843
+
1844
+ **/package.json**
1845
+
1846
+ ```json
1847
+ {
1848
+ "dependencies": {
1849
+ "react": "^18.0.0",
1850
+ "react-dom": "^18.0.0",
1851
+ "react-scripts": "^4.0.0"
1852
+ },
1853
+ "devDependencies": {
1854
+ "@types/react": "^18.0.0",
1855
+ "@types/react-dom": "^18.0.0",
1856
+ "typescript": "^4.0.0"
1857
+ },
1858
+ "main": "/index.tsx"
1859
+ }
1860
+ ```
1861
+
1862
+ ---
1863
+
1864
+ #### Dropdown with search and multi-select; does _not_ maintain searched query upon selection of a menu item
1865
+
1866
+ 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`.
1867
+
1868
+ 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).
1869
+
1870
+ To see how this works:
1871
+
1872
+ 1. In the search box, type in "fruit"
1873
+ 2. `DropdownMenu` will display "Fruit: Apple" and "Fruit: Orange"
1874
+ 3. Select "Fruit: Apple"
1875
+ 4. Note that the search text is now cleared and all dropdown items reappear
1876
+
1877
+ Compare this to the default behavior in the "DropdownMenu with search" example above, or change the `keepSearchOnSelect` prop in the example below to `true`.
1878
+
1879
+ **/styles.css**
1880
+
1881
+ ```css
1882
+ body {
1883
+ font-size: 13px;
1884
+ font-weight: 400;
1885
+ letter-spacing: 0.08px;
1886
+ line-height: 20px;
1887
+ text-transform: none;
1888
+ font-family: "Inter", sans-serif;
1889
+ }
1890
+
1891
+ h1 {
1892
+ font-size: 1.5rem;
1893
+ }
1894
+
1895
+ .app {
1896
+ padding: 20px;
1897
+ }
1898
+ ```
1899
+
1900
+ **tsconfig.json**
1901
+
1902
+ ```json
1903
+ {
1904
+ "include": ["./**/*"],
1905
+ "compilerOptions": {
1906
+ "strict": true,
1907
+ "esModuleInterop": true,
1908
+ "lib": ["dom", "es2015"],
1909
+ "jsx": "react-jsx"
1910
+ }
1911
+ }
1912
+ ```
1913
+
1914
+ **/App.tsx**
1915
+
1916
+ ```tsx
1917
+ // Dropdown with multi-select that does not maintain searched query upon selection of a menu item
1918
+
1919
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
1920
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
1921
+ import "./styles.css";
1922
+
1923
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
1924
+ {
1925
+ name: "Fruit: Apple",
1926
+ },
1927
+ {
1928
+ name: "Fruit: Orange",
1929
+ },
1930
+ {
1931
+ name: "Vegetable: Carrot",
1932
+ },
1933
+ {
1934
+ name: "Vegetable: Kale",
1935
+ },
1936
+ ];
1937
+
1938
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
1939
+
1940
+ function handleClickAway() {}
1941
+
1942
+ function App() {
1943
+ const ref = useRef(null);
1944
+ const [open, setOpen] = useState(false);
1945
+
1946
+ useEffect(() => {
1947
+ if (ref.current) setOpen(true);
1948
+ }, [ref.current]);
1949
+
1950
+ return (
1951
+ <div className="app" style={{ paddingLeft: "10px" }}>
1952
+ <div ref={ref} />
1953
+ {open && (
1954
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
1955
+ <DropdownMenu
1956
+ PopperBaseProps={POPPER_BASE_PROPS}
1957
+ anchorEl={ref.current}
1958
+ keepSearchOnSelect={false}
1959
+ multiple
1960
+ onClickAway={function handleClickAway() {}}
1961
+ open
1962
+ options={MENU_ITEMS}
1963
+ search
1964
+ />
1965
+ )}
1966
+ </div>
1967
+ );
1968
+ }
1969
+
1970
+ export default App;
1971
+ ```
1972
+
1973
+ **/index.tsx**
1974
+
1975
+ ```tsx
1976
+ import React from "react";
1977
+ import { createRoot } from "react-dom/client";
1978
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
1979
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
1980
+ import { Theme } from "@czi-sds/components";
1981
+ import CssBaseline from "@mui/material/CssBaseline";
1982
+ import useMediaQuery from "@mui/material/useMediaQuery";
1983
+
1984
+ import App from "./App";
1985
+
1986
+ const container = document.getElementById("root");
1987
+ const root = createRoot(container!);
1988
+
1989
+ const RootApp = () => {
1990
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
1991
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
1992
+
1993
+ return (
1994
+ <StyledEngineProvider injectFirst>
1995
+ <ThemeProvider theme={theme}>
1996
+ <EmotionThemeProvider theme={theme}>
1997
+ <CssBaseline />
1998
+ <App />
1999
+ </EmotionThemeProvider>
2000
+ </ThemeProvider>
2001
+ </StyledEngineProvider>
2002
+ );
2003
+ };
2004
+
2005
+ root.render(
2006
+ <React.StrictMode>
2007
+ <RootApp />
2008
+ </React.StrictMode>
2009
+ );
2010
+ ```
2011
+
2012
+ **/public/index.html**
2013
+
2014
+ ```html
2015
+ <!doctype html>
2016
+ <html lang="en">
2017
+ <head>
2018
+ <meta charset="UTF-8" />
2019
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2020
+ <title>Dropdown</title>
2021
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2022
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2023
+ <link
2024
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2025
+ rel="stylesheet"
2026
+ />
2027
+ <link
2028
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2029
+ rel="stylesheet"
2030
+ />
2031
+ </head>
2032
+ <body>
2033
+ <div id="root"></div>
2034
+ </body>
2035
+ </html>
2036
+ ```
2037
+
2038
+ **/package.json**
2039
+
2040
+ ```json
2041
+ {
2042
+ "dependencies": {
2043
+ "react": "^18.0.0",
2044
+ "react-dom": "^18.0.0",
2045
+ "react-scripts": "^4.0.0"
2046
+ },
2047
+ "devDependencies": {
2048
+ "@types/react": "^18.0.0",
2049
+ "@types/react-dom": "^18.0.0",
2050
+ "typescript": "^4.0.0"
2051
+ },
2052
+ "main": "/index.tsx"
2053
+ }
2054
+ ```
2055
+
2056
+ ---
2057
+
2058
+ #### DropdownMenu with grouped sections
2059
+
2060
+ `MenuItems` can be displayed in groups with a label.
2061
+
2062
+ 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:
2063
+
2064
+ `groupBy={(option) => {`
2065
+
2066
+ `return option.section; // this will group items by section`
2067
+
2068
+ `// Another example`
2069
+
2070
+ `// this will group items by the last character of their names`
2071
+
2072
+ `return option.name.at(-1);`
2073
+
2074
+ `}}`
2075
+
2076
+ This `groupBy` prop must be provided or the groups will not render, even if all menu items have `section` properties (see below).
2077
+
2078
+ > **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)
2079
+
2080
+ **/styles.css**
2081
+
2082
+ ```css
2083
+ body {
2084
+ font-size: 13px;
2085
+ font-weight: 400;
2086
+ letter-spacing: 0.08px;
2087
+ line-height: 20px;
2088
+ text-transform: none;
2089
+ font-family: "Inter", sans-serif;
2090
+ }
2091
+
2092
+ h1 {
2093
+ font-size: 1.5rem;
2094
+ }
2095
+
2096
+ .app {
2097
+ padding: 20px;
2098
+ }
2099
+ ```
2100
+
2101
+ **tsconfig.json**
2102
+
2103
+ ```json
2104
+ {
2105
+ "include": ["./**/*"],
2106
+ "compilerOptions": {
2107
+ "strict": true,
2108
+ "esModuleInterop": true,
2109
+ "lib": ["dom", "es2015"],
2110
+ "jsx": "react-jsx"
2111
+ }
2112
+ }
2113
+ ```
2114
+
2115
+ **/App.tsx**
2116
+
2117
+ ```tsx
2118
+ // Dropdown with grouped sections
2119
+
2120
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
2121
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
2122
+ import "./styles.css";
2123
+
2124
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
2125
+ {
2126
+ name: "Menu item A.1",
2127
+ section: "Section A",
2128
+ },
2129
+ {
2130
+ name: "Menu item A.2",
2131
+ section: "Section A",
2132
+ },
2133
+ {
2134
+ name: "Menu item without a specified section",
2135
+ },
2136
+ {
2137
+ name: "Menu item B.1",
2138
+ section: "Section B",
2139
+ },
2140
+ {
2141
+ name: "Menu item C.1",
2142
+ section: "Section C",
2143
+ },
2144
+ {
2145
+ name: "Menu item C.2",
2146
+ section: "Section C",
2147
+ },
2148
+ {
2149
+ name: "Menu item C.3",
2150
+ section: "Section C",
2151
+ },
2152
+ {
2153
+ name: "Menu item B.2",
2154
+ section: "Section B",
2155
+ },
2156
+ ];
2157
+
2158
+ /**
2159
+ * Items need to be sorted by the same groupBy dimension
2160
+ * In this example, we're grouping by the last character of the item name
2161
+ */
2162
+ const SORTED_ITEMS = [...MENU_ITEMS].sort((a, b) => {
2163
+ const aLastLetter = a.name.at(-1);
2164
+ const bLastLetter = b.name.at(-1);
2165
+
2166
+ if (aLastLetter < bLastLetter) {
2167
+ return -1;
2168
+ }
2169
+ if (aLastLetter > bLastLetter) {
2170
+ return 1;
2171
+ }
2172
+ return 0;
2173
+ });
2174
+
2175
+ function groupBy(option) {
2176
+ return option.name.at(-1);
2177
+ }
2178
+
2179
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
2180
+
2181
+ function handleClickAway() {}
2182
+
2183
+ function App() {
2184
+ const ref = useRef(null);
2185
+ const [open, setOpen] = useState(false);
2186
+
2187
+ useEffect(() => {
2188
+ if (ref.current) setOpen(true);
2189
+ }, [ref.current]);
2190
+
2191
+ return (
2192
+ <div className="app" style={{ paddingLeft: "10px" }}>
2193
+ <div ref={ref} />
2194
+ {open && (
2195
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
2196
+ <DropdownMenu
2197
+ PopperBaseProps={POPPER_BASE_PROPS}
2198
+ anchorEl={ref.current}
2199
+ groupBy={groupBy}
2200
+ onClickAway={handleClickAway}
2201
+ open
2202
+ options={SORTED_ITEMS}
2203
+ />
2204
+ )}
2205
+ </div>
2206
+ );
2207
+ }
2208
+
2209
+ export default App;
2210
+ ```
2211
+
2212
+ **/index.tsx**
2213
+
2214
+ ```tsx
2215
+ import React from "react";
2216
+ import { createRoot } from "react-dom/client";
2217
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2218
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2219
+ import { Theme } from "@czi-sds/components";
2220
+ import CssBaseline from "@mui/material/CssBaseline";
2221
+ import useMediaQuery from "@mui/material/useMediaQuery";
2222
+
2223
+ import App from "./App";
2224
+
2225
+ const container = document.getElementById("root");
2226
+ const root = createRoot(container!);
2227
+
2228
+ const RootApp = () => {
2229
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2230
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2231
+
2232
+ return (
2233
+ <StyledEngineProvider injectFirst>
2234
+ <ThemeProvider theme={theme}>
2235
+ <EmotionThemeProvider theme={theme}>
2236
+ <CssBaseline />
2237
+ <App />
2238
+ </EmotionThemeProvider>
2239
+ </ThemeProvider>
2240
+ </StyledEngineProvider>
2241
+ );
2242
+ };
2243
+
2244
+ root.render(
2245
+ <React.StrictMode>
2246
+ <RootApp />
2247
+ </React.StrictMode>
2248
+ );
2249
+ ```
2250
+
2251
+ **/public/index.html**
2252
+
2253
+ ```html
2254
+ <!doctype html>
2255
+ <html lang="en">
2256
+ <head>
2257
+ <meta charset="UTF-8" />
2258
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2259
+ <title>Dropdown</title>
2260
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2261
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2262
+ <link
2263
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2264
+ rel="stylesheet"
2265
+ />
2266
+ <link
2267
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2268
+ rel="stylesheet"
2269
+ />
2270
+ </head>
2271
+ <body>
2272
+ <div id="root"></div>
2273
+ </body>
2274
+ </html>
2275
+ ```
2276
+
2277
+ **/package.json**
2278
+
2279
+ ```json
2280
+ {
2281
+ "dependencies": {
2282
+ "react": "^18.0.0",
2283
+ "react-dom": "^18.0.0",
2284
+ "react-scripts": "^4.0.0"
2285
+ },
2286
+ "devDependencies": {
2287
+ "@types/react": "^18.0.0",
2288
+ "@types/react-dom": "^18.0.0",
2289
+ "typescript": "^4.0.0"
2290
+ },
2291
+ "main": "/index.tsx"
2292
+ }
2293
+ ```
2294
+
2295
+ ---
2296
+
2297
+ #### DropdownMenu with menu items that have count, icon, and / or details
2298
+
2299
+ Each `MenuItem` provided to `DropdownMenu` can optionally include `count`, `details` and `sdsIcon` properties.
2300
+
2301
+ 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).
2302
+
2303
+ Add a text string to the `details` property to display text below the menu item’s main text.
2304
+
2305
+ `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`.
2306
+
2307
+ **/styles.css**
2308
+
2309
+ ```css
2310
+ body {
2311
+ font-size: 13px;
2312
+ font-weight: 400;
2313
+ letter-spacing: 0.08px;
2314
+ line-height: 20px;
2315
+ text-transform: none;
2316
+ font-family: "Inter", sans-serif;
2317
+ }
2318
+
2319
+ h1 {
2320
+ font-size: 1.5rem;
2321
+ }
2322
+
2323
+ .app {
2324
+ padding: 20px;
2325
+ }
2326
+ ```
2327
+
2328
+ **tsconfig.json**
2329
+
2330
+ ```json
2331
+ {
2332
+ "include": ["./**/*"],
2333
+ "compilerOptions": {
2334
+ "strict": true,
2335
+ "esModuleInterop": true,
2336
+ "lib": ["dom", "es2015"],
2337
+ "jsx": "react-jsx"
2338
+ }
2339
+ }
2340
+ ```
2341
+
2342
+ **/App.tsx**
2343
+
2344
+ ```tsx
2345
+ // Dropdown with grouped sections
2346
+
2347
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
2348
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
2349
+ import "./styles.css";
2350
+
2351
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
2352
+ {
2353
+ name: "Menu item with count",
2354
+ count: 3,
2355
+ },
2356
+ {
2357
+ name: "Menu item with details",
2358
+ details: "Details",
2359
+ },
2360
+ {
2361
+ name: "Menu item with icon",
2362
+ sdsIcon: "puzzlePiece",
2363
+ },
2364
+ {
2365
+ name: "Menu item with icon that has its own props",
2366
+ sdsIcon: "copy",
2367
+ sdsIconProps: {
2368
+ className: "custom-class-name",
2369
+ color: "error",
2370
+ },
2371
+ },
2372
+ {
2373
+ name: "Menu item with count, details, & icon",
2374
+ count: 908,
2375
+ details: "Details",
2376
+ sdsIcon: "lightBulb",
2377
+ },
2378
+ ];
2379
+
2380
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
2381
+
2382
+ function handleClickAway() {}
2383
+
2384
+ function App() {
2385
+ const ref = useRef(null);
2386
+ const [open, setOpen] = useState(false);
2387
+
2388
+ useEffect(() => {
2389
+ if (ref.current) setOpen(true);
2390
+ }, [ref.current]);
2391
+
2392
+ return (
2393
+ <div className="app" style={{ paddingLeft: "10px" }}>
2394
+ <div ref={ref} />
2395
+ {open && (
2396
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
2397
+ <DropdownMenu
2398
+ PopperBaseProps={POPPER_BASE_PROPS}
2399
+ anchorEl={ref.current}
2400
+ groupBy={(option: DefaultDropdownMenuOption) => {
2401
+ return option.section as string;
2402
+ }}
2403
+ onClickAway={handleClickAway}
2404
+ open
2405
+ options={MENU_ITEMS}
2406
+ />
2407
+ )}
2408
+ </div>
2409
+ );
2410
+ }
2411
+
2412
+ export default App;
2413
+ ```
2414
+
2415
+ **/index.tsx**
2416
+
2417
+ ```tsx
2418
+ import React from "react";
2419
+ import { createRoot } from "react-dom/client";
2420
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2421
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2422
+ import { Theme } from "@czi-sds/components";
2423
+ import CssBaseline from "@mui/material/CssBaseline";
2424
+ import useMediaQuery from "@mui/material/useMediaQuery";
2425
+
2426
+ import App from "./App";
2427
+
2428
+ const container = document.getElementById("root");
2429
+ const root = createRoot(container!);
2430
+
2431
+ const RootApp = () => {
2432
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2433
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2434
+
2435
+ return (
2436
+ <StyledEngineProvider injectFirst>
2437
+ <ThemeProvider theme={theme}>
2438
+ <EmotionThemeProvider theme={theme}>
2439
+ <CssBaseline />
2440
+ <App />
2441
+ </EmotionThemeProvider>
2442
+ </ThemeProvider>
2443
+ </StyledEngineProvider>
2444
+ );
2445
+ };
2446
+
2447
+ root.render(
2448
+ <React.StrictMode>
2449
+ <RootApp />
2450
+ </React.StrictMode>
2451
+ );
2452
+ ```
2453
+
2454
+ **/public/index.html**
2455
+
2456
+ ```html
2457
+ <!doctype html>
2458
+ <html lang="en">
2459
+ <head>
2460
+ <meta charset="UTF-8" />
2461
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2462
+ <title>Dropdown</title>
2463
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2464
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2465
+ <link
2466
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2467
+ rel="stylesheet"
2468
+ />
2469
+ <link
2470
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2471
+ rel="stylesheet"
2472
+ />
2473
+ </head>
2474
+ <body>
2475
+ <div id="root"></div>
2476
+ </body>
2477
+ </html>
2478
+ ```
2479
+
2480
+ **/package.json**
2481
+
2482
+ ```json
2483
+ {
2484
+ "dependencies": {
2485
+ "react": "^18.0.0",
2486
+ "react-dom": "^18.0.0",
2487
+ "react-scripts": "^4.0.0"
2488
+ },
2489
+ "devDependencies": {
2490
+ "@types/react": "^18.0.0",
2491
+ "@types/react-dom": "^18.0.0",
2492
+ "typescript": "^4.0.0"
2493
+ },
2494
+ "main": "/index.tsx"
2495
+ }
2496
+ ```
2497
+
2498
+ ---
2499
+
2500
+ #### DropdownMenu with components in place of menu items
2501
+
2502
+ In place of the text normally shown for a menu item, a custom component can be to displayed instead.
2503
+
2504
+ 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.
2505
+
2506
+ These custom menu items can optionally be assigned metadata just like regular menu items, including `section`, `count`, and `sdsIcon` and `sdsIconProps`.
2507
+
2508
+ > **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!).
2509
+
2510
+ **/styles.css**
2511
+
2512
+ ```css
2513
+ body {
2514
+ font-size: 13px;
2515
+ font-weight: 400;
2516
+ letter-spacing: 0.08px;
2517
+ line-height: 20px;
2518
+ text-transform: none;
2519
+ font-family: "Inter", sans-serif;
2520
+ }
2521
+
2522
+ h1 {
2523
+ font-size: 1.5rem;
2524
+ }
2525
+
2526
+ .app {
2527
+ padding: 20px;
2528
+ }
2529
+ ```
2530
+
2531
+ **tsconfig.json**
2532
+
2533
+ ```json
2534
+ {
2535
+ "include": ["./**/*"],
2536
+ "compilerOptions": {
2537
+ "strict": true,
2538
+ "esModuleInterop": true,
2539
+ "lib": ["dom", "es2015"],
2540
+ "jsx": "react-jsx"
2541
+ }
2542
+ }
2543
+ ```
2544
+
2545
+ **/App.tsx**
2546
+
2547
+ ```tsx
2548
+ // DropdownMenu with components in place of menu items
2549
+
2550
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
2551
+ import {
2552
+ DropdownMenu,
2553
+ DefaultDropdownMenuOption,
2554
+ Tag,
2555
+ Button,
2556
+ ButtonIcon,
2557
+ } from "@czi-sds/components";
2558
+ import "./styles.css";
2559
+
2560
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
2561
+ {
2562
+ component: (
2563
+ <Button sdsStyle="square" sdsType="primary">
2564
+ Button
2565
+ </Button>
2566
+ ),
2567
+ name: "Button component",
2568
+ section: "Button component",
2569
+ },
2570
+ {
2571
+ component: <ButtonIcon sdsIcon="Copy" sdsSize="large" sdsType="primary" />,
2572
+ name: "ButtonIcon component",
2573
+ section: "ButtonIcon component",
2574
+ },
2575
+ {
2576
+ component: (
2577
+ <div style={{ marginTop: 10 }}>
2578
+ <Tag
2579
+ label="Tag 1"
2580
+ sdsStyle="rounded"
2581
+ sdsType="secondary"
2582
+ color="negative"
2583
+ />
2584
+ <Tag
2585
+ label="Tag 2"
2586
+ sdsStyle="rounded"
2587
+ sdsType="secondary"
2588
+ color="notice"
2589
+ />
2590
+ <Tag
2591
+ label="Tag 3"
2592
+ sdsStyle="rounded"
2593
+ sdsType="secondary"
2594
+ color="neutral"
2595
+ />
2596
+ </div>
2597
+ ),
2598
+ name: "tag components",
2599
+ section: "Tag components",
2600
+ },
2601
+ {
2602
+ component: (
2603
+ <Tag label="Tag" sdsStyle="rounded" sdsType="secondary" color="neutral" />
2604
+ ),
2605
+ name: "Button component",
2606
+ section: "Component with count and icon",
2607
+ count: 37,
2608
+ sdsIcon: "PuzzlePiece",
2609
+ },
2610
+ ];
2611
+
2612
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
2613
+
2614
+ function handleClickAway() {}
2615
+
2616
+ function App() {
2617
+ const ref = useRef(null);
2618
+ const [open, setOpen] = useState(false);
2619
+
2620
+ useEffect(() => {
2621
+ if (ref.current) setOpen(true);
2622
+ }, [ref.current]);
2623
+
2624
+ return (
2625
+ <div className="app" style={{ paddingLeft: "10px" }}>
2626
+ <div ref={ref} />
2627
+ {open && (
2628
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
2629
+ <DropdownMenu
2630
+ PopperBaseProps={POPPER_BASE_PROPS}
2631
+ anchorEl={ref.current}
2632
+ groupBy={(option: DefaultDropdownMenuOption) => {
2633
+ return option.section;
2634
+ }}
2635
+ onClickAway={handleClickAway}
2636
+ open
2637
+ options={MENU_ITEMS}
2638
+ />
2639
+ )}
2640
+ </div>
2641
+ );
2642
+ }
2643
+
2644
+ export default App;
2645
+ ```
2646
+
2647
+ **/index.tsx**
2648
+
2649
+ ```tsx
2650
+ import React from "react";
2651
+ import { createRoot } from "react-dom/client";
2652
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2653
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2654
+ import { Theme } from "@czi-sds/components";
2655
+ import CssBaseline from "@mui/material/CssBaseline";
2656
+ import useMediaQuery from "@mui/material/useMediaQuery";
2657
+
2658
+ import App from "./App";
2659
+
2660
+ const container = document.getElementById("root");
2661
+ const root = createRoot(container!);
2662
+
2663
+ const RootApp = () => {
2664
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2665
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2666
+
2667
+ return (
2668
+ <StyledEngineProvider injectFirst>
2669
+ <ThemeProvider theme={theme}>
2670
+ <EmotionThemeProvider theme={theme}>
2671
+ <CssBaseline />
2672
+ <App />
2673
+ </EmotionThemeProvider>
2674
+ </ThemeProvider>
2675
+ </StyledEngineProvider>
2676
+ );
2677
+ };
2678
+
2679
+ root.render(
2680
+ <React.StrictMode>
2681
+ <RootApp />
2682
+ </React.StrictMode>
2683
+ );
2684
+ ```
2685
+
2686
+ **/public/index.html**
2687
+
2688
+ ```html
2689
+ <!doctype html>
2690
+ <html lang="en">
2691
+ <head>
2692
+ <meta charset="UTF-8" />
2693
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2694
+ <title>Dropdown</title>
2695
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2696
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2697
+ <link
2698
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2699
+ rel="stylesheet"
2700
+ />
2701
+ <link
2702
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2703
+ rel="stylesheet"
2704
+ />
2705
+ </head>
2706
+ <body>
2707
+ <div id="root"></div>
2708
+ </body>
2709
+ </html>
2710
+ ```
2711
+
2712
+ **/package.json**
2713
+
2714
+ ```json
2715
+ {
2716
+ "dependencies": {
2717
+ "react": "^18.0.0",
2718
+ "react-dom": "^18.0.0",
2719
+ "react-scripts": "^4.0.0"
2720
+ },
2721
+ "devDependencies": {
2722
+ "@types/react": "^18.0.0",
2723
+ "@types/react-dom": "^18.0.0",
2724
+ "typescript": "^4.0.0"
2725
+ },
2726
+ "main": "/index.tsx"
2727
+ }
2728
+ ```
2729
+
2730
+ ---
2731
+
2732
+ ---
2733
+
2734
+ ## MenuItem
2735
+
2736
+ ### SDS Source Code
2737
+
2738
+ The component's source code in the SDS codebase can be found here.
2739
+
2740
+ ### Props
2741
+
2742
+ Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
2743
+
2744
+ | Name | Type | Default | Description |
2745
+ | --------------- | ----------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
2746
+ | `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. |
2747
+ | `children` | `node` | `-` | Main content of the MenuItem. |
2748
+ | `disabled` | `bool` | `false` | When set to `true`, the disabled prop indicates that the menu item is disabled and not interactable. |
2749
+ | `isMultiSelect` | `bool` | `false` | When `true`, designates that the menu item supports multi-select functionality. |
2750
+ | `sdsIcon` | `func` | `-` | Allows you to provide a custom SDS icon component to be displayed within the menu item. |
2751
+ | `sdsIconProps` | `func` | `-` | Enables you to pass additional props to customize the SDS icon within the menu item. |
2752
+ | `sdsStyle` | `"determinate"` ` | "indeterminate"` | `"determinate"` | Determines the style of the menu item, whether it is `determinate` or `indeterminate`. |
2753
+ | `selected` | `bool` | `false` | When set to `true`, the `selected` prop signifies that the menu item is currently selected. |
2754
+
2755
+ ### Code Examples
2756
+
2757
+ #### **Default MenuItem**
2758
+
2759
+ This example shows the minimum props needed for rendering a `MenuItem`.
2760
+
2761
+ **/styles.css**
2762
+
2763
+ ```css
2764
+ body {
2765
+ font-size: 13px;
2766
+ font-weight: 400;
2767
+ letter-spacing: 0.08px;
2768
+ line-height: 20px;
2769
+ text-transform: none;
2770
+ font-family: "Inter", sans-serif;
2771
+ }
2772
+
2773
+ h1 {
2774
+ font-size: 1.5rem;
2775
+ }
2776
+
2777
+ .app {
2778
+ padding: 20px;
2779
+ }
2780
+ ```
2781
+
2782
+ **tsconfig.json**
2783
+
2784
+ ```json
2785
+ {
2786
+ "include": ["./**/*"],
2787
+ "compilerOptions": {
2788
+ "strict": true,
2789
+ "esModuleInterop": true,
2790
+ "lib": ["dom", "es2015"],
2791
+ "jsx": "react-jsx"
2792
+ }
2793
+ }
2794
+ ```
2795
+
2796
+ **/App.tsx**
2797
+
2798
+ ```tsx
2799
+ import { MenuItem } from "@czi-sds/components";
2800
+ import "./styles.css";
2801
+
2802
+ function App() {
2803
+ return (
2804
+ <div className="app">
2805
+ <MenuItem>MenuItem 1</MenuItem>
2806
+ </div>
2807
+ );
2808
+ }
2809
+
2810
+ export default App;
2811
+ ```
2812
+
2813
+ **/index.tsx**
2814
+
2815
+ ```tsx
2816
+ import React from "react";
2817
+ import { createRoot } from "react-dom/client";
2818
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2819
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2820
+ import { Theme } from "@czi-sds/components";
2821
+ import CssBaseline from "@mui/material/CssBaseline";
2822
+ import useMediaQuery from "@mui/material/useMediaQuery";
2823
+
2824
+ import App from "./App";
2825
+
2826
+ const container = document.getElementById("root");
2827
+ const root = createRoot(container!);
2828
+
2829
+ const RootApp = () => {
2830
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2831
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2832
+
2833
+ return (
2834
+ <StyledEngineProvider injectFirst>
2835
+ <ThemeProvider theme={theme}>
2836
+ <EmotionThemeProvider theme={theme}>
2837
+ <CssBaseline />
2838
+ <App />
2839
+ </EmotionThemeProvider>
2840
+ </ThemeProvider>
2841
+ </StyledEngineProvider>
2842
+ );
2843
+ };
2844
+
2845
+ root.render(
2846
+ <React.StrictMode>
2847
+ <RootApp />
2848
+ </React.StrictMode>
2849
+ );
2850
+ ```
2851
+
2852
+ **/public/index.html**
2853
+
2854
+ ```html
2855
+ <!doctype html>
2856
+ <html lang="en">
2857
+ <head>
2858
+ <meta charset="UTF-8" />
2859
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2860
+ <title>Dropdown</title>
2861
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
2862
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
2863
+ <link
2864
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
2865
+ rel="stylesheet"
2866
+ />
2867
+ <link
2868
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
2869
+ rel="stylesheet"
2870
+ />
2871
+ </head>
2872
+ <body>
2873
+ <div id="root"></div>
2874
+ </body>
2875
+ </html>
2876
+ ```
2877
+
2878
+ **/package.json**
2879
+
2880
+ ```json
2881
+ {
2882
+ "dependencies": {
2883
+ "react": "^18.0.0",
2884
+ "react-dom": "^18.0.0",
2885
+ "react-scripts": "^4.0.0"
2886
+ },
2887
+ "devDependencies": {
2888
+ "@types/react": "^18.0.0",
2889
+ "@types/react-dom": "^18.0.0",
2890
+ "typescript": "^4.0.0"
2891
+ },
2892
+ "main": "/index.tsx"
2893
+ }
2894
+ ```
2895
+
2896
+ ---
2897
+
2898
+ #### **Multi-Select MenuItem with Icon**
2899
+
2900
+ This example showcases a multi-select `MenuItem` component featuring an SDS `Icon` and a distinct column value.
2901
+
2902
+ > **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.
2903
+
2904
+ **/styles.css**
2905
+
2906
+ ```css
2907
+ body {
2908
+ font-size: 13px;
2909
+ font-weight: 400;
2910
+ letter-spacing: 0.08px;
2911
+ line-height: 20px;
2912
+ text-transform: none;
2913
+ font-family: "Inter", sans-serif;
2914
+ }
2915
+
2916
+ h1 {
2917
+ font-size: 1.5rem;
2918
+ }
2919
+
2920
+ .app {
2921
+ padding: 20px;
2922
+ }
2923
+ ```
2924
+
2925
+ **tsconfig.json**
2926
+
2927
+ ```json
2928
+ {
2929
+ "include": ["./**/*"],
2930
+ "compilerOptions": {
2931
+ "strict": true,
2932
+ "esModuleInterop": true,
2933
+ "lib": ["dom", "es2015"],
2934
+ "jsx": "react-jsx"
2935
+ }
2936
+ }
2937
+ ```
2938
+
2939
+ **/App.tsx**
2940
+
2941
+ ```tsx
2942
+ import { useState } from "react";
2943
+ import { MenuItem } from "@czi-sds/components";
2944
+ import "./styles.css";
2945
+
2946
+ function App() {
2947
+ const [selected, setSelected] = useState(false);
2948
+
2949
+ const handleClick = () => {
2950
+ setSelected((prev) => !prev);
2951
+ };
2952
+
2953
+ return (
2954
+ <div className="app">
2955
+ <MenuItem
2956
+ isMultiSelect
2957
+ column="Column Value"
2958
+ sdsIcon="gear"
2959
+ count={3}
2960
+ sdsIconProps={{
2961
+ color: "beta",
2962
+ }}
2963
+ selected={selected}
2964
+ onClick={handleClick}
2965
+ >
2966
+ MenuItem 1
2967
+ </MenuItem>
2968
+ </div>
2969
+ );
2970
+ }
2971
+
2972
+ export default App;
2973
+ ```
2974
+
2975
+ **/index.tsx**
2976
+
2977
+ ```tsx
2978
+ import React from "react";
2979
+ import { createRoot } from "react-dom/client";
2980
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
2981
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
2982
+ import { Theme } from "@czi-sds/components";
2983
+ import CssBaseline from "@mui/material/CssBaseline";
2984
+ import useMediaQuery from "@mui/material/useMediaQuery";
2985
+
2986
+ import App from "./App";
2987
+
2988
+ const container = document.getElementById("root");
2989
+ const root = createRoot(container!);
2990
+
2991
+ const RootApp = () => {
2992
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
2993
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
2994
+
2995
+ return (
2996
+ <StyledEngineProvider injectFirst>
2997
+ <ThemeProvider theme={theme}>
2998
+ <EmotionThemeProvider theme={theme}>
2999
+ <CssBaseline />
3000
+ <App />
3001
+ </EmotionThemeProvider>
3002
+ </ThemeProvider>
3003
+ </StyledEngineProvider>
3004
+ );
3005
+ };
3006
+
3007
+ root.render(
3008
+ <React.StrictMode>
3009
+ <RootApp />
3010
+ </React.StrictMode>
3011
+ );
3012
+ ```
3013
+
3014
+ **/public/index.html**
3015
+
3016
+ ```html
3017
+ <!doctype html>
3018
+ <html lang="en">
3019
+ <head>
3020
+ <meta charset="UTF-8" />
3021
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3022
+ <title>Dropdown</title>
3023
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3024
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3025
+ <link
3026
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3027
+ rel="stylesheet"
3028
+ />
3029
+ <link
3030
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3031
+ rel="stylesheet"
3032
+ />
3033
+ </head>
3034
+ <body>
3035
+ <div id="root"></div>
3036
+ </body>
3037
+ </html>
3038
+ ```
3039
+
3040
+ **/package.json**
3041
+
3042
+ ```json
3043
+ {
3044
+ "dependencies": {
3045
+ "react": "^18.0.0",
3046
+ "react-dom": "^18.0.0",
3047
+ "react-scripts": "^4.0.0"
3048
+ },
3049
+ "devDependencies": {
3050
+ "@types/react": "^18.0.0",
3051
+ "@types/react-dom": "^18.0.0",
3052
+ "typescript": "^4.0.0"
3053
+ },
3054
+ "main": "/index.tsx"
3055
+ }
3056
+ ```
3057
+
3058
+ ---
3059
+
3060
+ ## Component Recipes
3061
+
3062
+ ## DropdownMenu Recipes
3063
+
3064
+ | 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 |
3065
+ | ----------------------------------------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
3066
+
3067
+ #### **DropdownMenu with customized** **`isOptionEqualToValue`**
3068
+
3069
+ 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.
3070
+
3071
+ This is a MUI prop; learn more in the Autocomplete API.
3072
+
3073
+ **/styles.css**
3074
+
3075
+ ```css
3076
+ body {
3077
+ font-size: 13px;
3078
+ font-weight: 400;
3079
+ letter-spacing: 0.08px;
3080
+ line-height: 20px;
3081
+ text-transform: none;
3082
+ font-family: "Inter", sans-serif;
3083
+ }
3084
+
3085
+ h1 {
3086
+ font-size: 1.5rem;
3087
+ }
3088
+
3089
+ .app {
3090
+ padding: 20px;
3091
+ }
3092
+ ```
3093
+
3094
+ **tsconfig.json**
3095
+
3096
+ ```json
3097
+ {
3098
+ "include": ["./**/*"],
3099
+ "compilerOptions": {
3100
+ "strict": true,
3101
+ "esModuleInterop": true,
3102
+ "lib": ["dom", "es2015"],
3103
+ "jsx": "react-jsx"
3104
+ }
3105
+ }
3106
+ ```
3107
+
3108
+ **/App.tsx**
3109
+
3110
+ ```tsx
3111
+ // DropdownMenu with customized `isOptionEqualToValue`
3112
+
3113
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3114
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3115
+ import "./styles.css";
3116
+
3117
+ function optionIdEqualToValueId(option, value) {
3118
+ return option.id === value.id;
3119
+ }
3120
+
3121
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3122
+ {
3123
+ name: "Menu item 1",
3124
+ id: "one",
3125
+ },
3126
+ {
3127
+ name: "Menu item 2",
3128
+ id: "two",
3129
+ },
3130
+ {
3131
+ name: "Menu item 3",
3132
+ id: "three",
3133
+ },
3134
+ {
3135
+ name: "Longer menu item than the others",
3136
+ id: "four",
3137
+ },
3138
+ ];
3139
+
3140
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
3141
+
3142
+ function handleClickAway() {}
3143
+
3144
+ function App() {
3145
+ const ref = useRef(null);
3146
+ const [open, setOpen] = useState(false);
3147
+
3148
+ useEffect(() => {
3149
+ if (ref.current) setOpen(true);
3150
+ }, [ref.current]);
3151
+
3152
+ return (
3153
+ <div className="app" style={{ paddingLeft: "10px" }}>
3154
+ <div ref={ref} />
3155
+ {open && (
3156
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3157
+ <DropdownMenu
3158
+ PopperBaseProps={POPPER_BASE_PROPS}
3159
+ anchorEl={ref.current}
3160
+ isOptionEqualToValue={optionIdEqualToValueId}
3161
+ onClickAway={handleClickAway}
3162
+ open
3163
+ options={MENU_ITEMS}
3164
+ />
3165
+ )}
3166
+ </div>
3167
+ );
3168
+ }
3169
+
3170
+ export default App;
3171
+ ```
3172
+
3173
+ **/index.tsx**
3174
+
3175
+ ```tsx
3176
+ import React from "react";
3177
+ import { createRoot } from "react-dom/client";
3178
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3179
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3180
+ import { Theme } from "@czi-sds/components";
3181
+ import CssBaseline from "@mui/material/CssBaseline";
3182
+ import useMediaQuery from "@mui/material/useMediaQuery";
3183
+
3184
+ import App from "./App";
3185
+
3186
+ const container = document.getElementById("root");
3187
+ const root = createRoot(container!);
3188
+
3189
+ const RootApp = () => {
3190
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3191
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3192
+
3193
+ return (
3194
+ <StyledEngineProvider injectFirst>
3195
+ <ThemeProvider theme={theme}>
3196
+ <EmotionThemeProvider theme={theme}>
3197
+ <CssBaseline />
3198
+ <App />
3199
+ </EmotionThemeProvider>
3200
+ </ThemeProvider>
3201
+ </StyledEngineProvider>
3202
+ );
3203
+ };
3204
+
3205
+ root.render(
3206
+ <React.StrictMode>
3207
+ <RootApp />
3208
+ </React.StrictMode>
3209
+ );
3210
+ ```
3211
+
3212
+ **/public/index.html**
3213
+
3214
+ ```html
3215
+ <!doctype html>
3216
+ <html lang="en">
3217
+ <head>
3218
+ <meta charset="UTF-8" />
3219
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3220
+ <title>DropdownMenu</title>
3221
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3222
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3223
+ <link
3224
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3225
+ rel="stylesheet"
3226
+ />
3227
+ <link
3228
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3229
+ rel="stylesheet"
3230
+ />
3231
+ </head>
3232
+ <body>
3233
+ <div id="root"></div>
3234
+ </body>
3235
+ </html>
3236
+ ```
3237
+
3238
+ **/package.json**
3239
+
3240
+ ```json
3241
+ {
3242
+ "dependencies": {
3243
+ "react": "^18.0.0",
3244
+ "react-dom": "^18.0.0",
3245
+ "react-scripts": "^4.0.0"
3246
+ },
3247
+ "devDependencies": {
3248
+ "@types/react": "^18.0.0",
3249
+ "@types/react-dom": "^18.0.0",
3250
+ "typescript": "^4.0.0"
3251
+ },
3252
+ "main": "/index.tsx"
3253
+ }
3254
+ ```
3255
+
3256
+ ---
3257
+
3258
+ #### **DropdownMenu with customized** **`onChange`** **prop**
3259
+
3260
+ The optional `onChange` prop specifies what happens when a new selection is made.
3261
+
3262
+ In the example below, `onChange` has been modified such that making a new selection logs the `value` and `reason` to the console.
3263
+
3264
+ This is a MUI prop; learn more in the Autocomplete API.
3265
+
3266
+ **/styles.css**
3267
+
3268
+ ```css
3269
+ body {
3270
+ font-size: 13px;
3271
+ font-weight: 400;
3272
+ letter-spacing: 0.08px;
3273
+ line-height: 20px;
3274
+ text-transform: none;
3275
+ font-family: "Inter", sans-serif;
3276
+ }
3277
+
3278
+ h1 {
3279
+ font-size: 1.5rem;
3280
+ }
3281
+
3282
+ .app {
3283
+ padding: 20px;
3284
+ }
3285
+ ```
3286
+
3287
+ **tsconfig.json**
3288
+
3289
+ ```json
3290
+ {
3291
+ "include": ["./**/*"],
3292
+ "compilerOptions": {
3293
+ "strict": true,
3294
+ "esModuleInterop": true,
3295
+ "lib": ["dom", "es2015"],
3296
+ "jsx": "react-jsx"
3297
+ }
3298
+ }
3299
+ ```
3300
+
3301
+ **/App.tsx**
3302
+
3303
+ ```tsx
3304
+ // DropdownMenu with customized `onInputChange`
3305
+
3306
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3307
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3308
+ import "./styles.css";
3309
+
3310
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3311
+ {
3312
+ name: "Menu item 1",
3313
+ },
3314
+ {
3315
+ name: "Menu item 2",
3316
+ },
3317
+ {
3318
+ name: "Menu item 3",
3319
+ },
3320
+ {
3321
+ name: "Longer menu item than the others",
3322
+ },
3323
+ ];
3324
+
3325
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
3326
+
3327
+ function handleClickAway() {}
3328
+
3329
+ function Message({ message }) {
3330
+ return (
3331
+ <div
3332
+ style={{ position: "absolute", top: "170px", fontFamily: "sans-serif" }}
3333
+ >
3334
+ <p>{JSON.stringify(message)}</p>
3335
+ </div>
3336
+ );
3337
+ }
3338
+
3339
+ function App() {
3340
+ const [message, setMessage] = useState();
3341
+
3342
+ const ref = useRef(null);
3343
+ const [open, setOpen] = useState(false);
3344
+
3345
+ useEffect(() => {
3346
+ if (ref.current) setOpen(true);
3347
+ }, [ref.current]);
3348
+
3349
+ return (
3350
+ <div className="app" style={{ paddingLeft: "10px" }}>
3351
+ <div ref={ref} />
3352
+ {open && (
3353
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3354
+ <>
3355
+ <DropdownMenu
3356
+ PopperBaseProps={POPPER_BASE_PROPS}
3357
+ anchorEl={ref.current}
3358
+ keepSearchOnSelect
3359
+ onClickAway={handleClickAway}
3360
+ onChange={(_, value, reason) => setMessage({ value, reason })}
3361
+ open
3362
+ options={MENU_ITEMS}
3363
+ />
3364
+ <Message message={message} />
3365
+ </>
3366
+ )}
3367
+ </div>
3368
+ );
3369
+ }
3370
+
3371
+ export default App;
3372
+ ```
3373
+
3374
+ **/index.tsx**
3375
+
3376
+ ```tsx
3377
+ import React from "react";
3378
+ import { createRoot } from "react-dom/client";
3379
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3380
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3381
+ import { Theme } from "@czi-sds/components";
3382
+ import CssBaseline from "@mui/material/CssBaseline";
3383
+ import useMediaQuery from "@mui/material/useMediaQuery";
3384
+
3385
+ import App from "./App";
3386
+
3387
+ const container = document.getElementById("root");
3388
+ const root = createRoot(container!);
3389
+
3390
+ const RootApp = () => {
3391
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3392
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3393
+
3394
+ return (
3395
+ <StyledEngineProvider injectFirst>
3396
+ <ThemeProvider theme={theme}>
3397
+ <EmotionThemeProvider theme={theme}>
3398
+ <CssBaseline />
3399
+ <App />
3400
+ </EmotionThemeProvider>
3401
+ </ThemeProvider>
3402
+ </StyledEngineProvider>
3403
+ );
3404
+ };
3405
+
3406
+ root.render(
3407
+ <React.StrictMode>
3408
+ <RootApp />
3409
+ </React.StrictMode>
3410
+ );
3411
+ ```
3412
+
3413
+ **/public/index.html**
3414
+
3415
+ ```html
3416
+ <!doctype html>
3417
+ <html lang="en">
3418
+ <head>
3419
+ <meta charset="UTF-8" />
3420
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3421
+ <title>DropdownMenu</title>
3422
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3423
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3424
+ <link
3425
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3426
+ rel="stylesheet"
3427
+ />
3428
+ <link
3429
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3430
+ rel="stylesheet"
3431
+ />
3432
+ </head>
3433
+ <body>
3434
+ <div id="root"></div>
3435
+ </body>
3436
+ </html>
3437
+ ```
3438
+
3439
+ **/package.json**
3440
+
3441
+ ```json
3442
+ {
3443
+ "dependencies": {
3444
+ "react": "^18.0.0",
3445
+ "react-dom": "^18.0.0",
3446
+ "react-scripts": "^4.0.0"
3447
+ },
3448
+ "devDependencies": {
3449
+ "@types/react": "^18.0.0",
3450
+ "@types/react-dom": "^18.0.0",
3451
+ "typescript": "^4.0.0"
3452
+ },
3453
+ "main": "/index.tsx"
3454
+ }
3455
+ ```
3456
+
3457
+ ---
3458
+
3459
+ #### **DropdownMenu with custom** **`PopperBaseProps`**
3460
+
3461
+ `PopperBaseProps` is an SDS-specific prop made for passing custom props to the `Popper` component, which is included as part of the `DropdownMenu` component.
3462
+
3463
+ 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.
3464
+
3465
+ 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).
3466
+
3467
+ **/styles.css**
3468
+
3469
+ ```css
3470
+ body {
3471
+ font-size: 13px;
3472
+ font-weight: 400;
3473
+ letter-spacing: 0.08px;
3474
+ line-height: 20px;
3475
+ text-transform: none;
3476
+ font-family: "Inter", sans-serif;
3477
+ }
3478
+
3479
+ h1 {
3480
+ font-size: 1.5rem;
3481
+ }
3482
+
3483
+ .app {
3484
+ padding: 20px;
3485
+ }
3486
+ ```
3487
+
3488
+ **tsconfig.json**
3489
+
3490
+ ```json
3491
+ {
3492
+ "include": ["./**/*"],
3493
+ "compilerOptions": {
3494
+ "strict": true,
3495
+ "esModuleInterop": true,
3496
+ "lib": ["dom", "es2015"],
3497
+ "jsx": "react-jsx"
3498
+ }
3499
+ }
3500
+ ```
3501
+
3502
+ **/App.tsx**
3503
+
3504
+ ```tsx
3505
+ // DropdownMenu with custom `PopperBaseProps`
3506
+
3507
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3508
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3509
+ import "./styles.css";
3510
+
3511
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3512
+ {
3513
+ name: "Menu item 1",
3514
+ },
3515
+ {
3516
+ name: "Menu item 2",
3517
+ },
3518
+ {
3519
+ name: "Menu item 3",
3520
+ },
3521
+ {
3522
+ name: "Longer menu item than the others",
3523
+ },
3524
+ ];
3525
+
3526
+ const POPPER_BASE_PROPS = {
3527
+ className: "popper",
3528
+ sx: {
3529
+ width: 500,
3530
+ borderColor: "salmon",
3531
+ borderWidth: 10,
3532
+ },
3533
+ popperOptions: { strategy: "absolute" },
3534
+ };
3535
+
3536
+ function App() {
3537
+ const ref = useRef(null);
3538
+ const [open, setOpen] = useState(false);
3539
+
3540
+ useEffect(() => {
3541
+ if (ref.current) setOpen(true);
3542
+ }, [ref.current]);
3543
+
3544
+ return (
3545
+ <div className="app" style={{ paddingLeft: "10px" }}>
3546
+ <div ref={ref} />
3547
+ {open && (
3548
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3549
+ <DropdownMenu
3550
+ PopperBaseProps={POPPER_BASE_PROPS}
3551
+ anchorEl={ref.current}
3552
+ onClickAway={function handleClickAway() {}}
3553
+ open
3554
+ options={MENU_ITEMS}
3555
+ PopperBaseProps={POPPER_BASE_PROPS}
3556
+ />
3557
+ )}
3558
+ </div>
3559
+ );
3560
+ }
3561
+
3562
+ export default App;
3563
+ ```
3564
+
3565
+ **/index.tsx**
3566
+
3567
+ ```tsx
3568
+ import React from "react";
3569
+ import { createRoot } from "react-dom/client";
3570
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3571
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3572
+ import { Theme } from "@czi-sds/components";
3573
+ import CssBaseline from "@mui/material/CssBaseline";
3574
+ import useMediaQuery from "@mui/material/useMediaQuery";
3575
+
3576
+ import App from "./App";
3577
+
3578
+ const container = document.getElementById("root");
3579
+ const root = createRoot(container!);
3580
+
3581
+ const RootApp = () => {
3582
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3583
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3584
+
3585
+ return (
3586
+ <StyledEngineProvider injectFirst>
3587
+ <ThemeProvider theme={theme}>
3588
+ <EmotionThemeProvider theme={theme}>
3589
+ <CssBaseline />
3590
+ <App />
3591
+ </EmotionThemeProvider>
3592
+ </ThemeProvider>
3593
+ </StyledEngineProvider>
3594
+ );
3595
+ };
3596
+
3597
+ root.render(
3598
+ <React.StrictMode>
3599
+ <RootApp />
3600
+ </React.StrictMode>
3601
+ );
3602
+ ```
3603
+
3604
+ **/public/index.html**
3605
+
3606
+ ```html
3607
+ <!doctype html>
3608
+ <html lang="en">
3609
+ <head>
3610
+ <meta charset="UTF-8" />
3611
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3612
+ <title>DropdownMenu</title>
3613
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3614
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3615
+ <link
3616
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3617
+ rel="stylesheet"
3618
+ />
3619
+ <link
3620
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3621
+ rel="stylesheet"
3622
+ />
3623
+ </head>
3624
+ <body>
3625
+ <div id="root"></div>
3626
+ </body>
3627
+ </html>
3628
+ ```
3629
+
3630
+ **/package.json**
3631
+
3632
+ ```json
3633
+ {
3634
+ "dependencies": {
3635
+ "react": "^18.0.0",
3636
+ "react-dom": "^18.0.0",
3637
+ "react-scripts": "^4.0.0"
3638
+ },
3639
+ "devDependencies": {
3640
+ "@types/react": "^18.0.0",
3641
+ "@types/react-dom": "^18.0.0",
3642
+ "typescript": "^4.0.0"
3643
+ },
3644
+ "main": "/index.tsx"
3645
+ }
3646
+ ```
3647
+
3648
+ ---
3649
+
3650
+ #### **DropdownMenu with custom Popper component**
3651
+
3652
+ In addition to modifying the `Popper`'s styles via `PopperBaseProps`, the styles for the `Popper` component can be overwritten more directly with `styled()`.
3653
+
3654
+ 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`.
3655
+
3656
+ **/styles.css**
3657
+
3658
+ ```css
3659
+ body {
3660
+ font-size: 13px;
3661
+ font-weight: 400;
3662
+ letter-spacing: 0.08px;
3663
+ line-height: 20px;
3664
+ text-transform: none;
3665
+ font-family: "Inter", sans-serif;
3666
+ }
3667
+
3668
+ h1 {
3669
+ font-size: 1.5rem;
3670
+ }
3671
+
3672
+ .app {
3673
+ padding: 20px;
3674
+ }
3675
+ ```
3676
+
3677
+ **tsconfig.json**
3678
+
3679
+ ```json
3680
+ {
3681
+ "include": ["./**/*"],
3682
+ "compilerOptions": {
3683
+ "strict": true,
3684
+ "esModuleInterop": true,
3685
+ "lib": ["dom", "es2015"],
3686
+ "jsx": "react-jsx"
3687
+ }
3688
+ }
3689
+ ```
3690
+
3691
+ **/App.tsx**
3692
+
3693
+ ```tsx
3694
+ // DropdownMenu with custom Popper component
3695
+
3696
+ import React, { SyntheticEvent, useState, useRef, useEffect } from "react";
3697
+ import { Popper, styled } from "@mui/material";
3698
+ import { DropdownMenu, DefaultDropdownMenuOption } from "@czi-sds/components";
3699
+ import "./styles.css";
3700
+
3701
+ const MENU_ITEMS: DefaultDropdownMenuOption[] = [
3702
+ {
3703
+ name: "Menu item 1",
3704
+ },
3705
+ {
3706
+ name: "Menu item 2",
3707
+ },
3708
+ {
3709
+ name: "Menu item 3",
3710
+ },
3711
+ {
3712
+ name: "Longer menu item than the others",
3713
+ },
3714
+ ];
3715
+
3716
+ // Step 1/3: Copy existing base styles for the Popper component from
3717
+ // sci-components/packages/components/src/core/DropdownMenu/style.ts, ~lines 206-229:
3718
+ const getBorders = ({ theme }: CommonThemeProps): Borders | null => {
3719
+ return theme?.app?.borders || null;
3720
+ };
3721
+
3722
+ const getCorners = ({ theme }: CommonThemeProps): Corners | null => {
3723
+ return theme?.app?.corners || null;
3724
+ };
3725
+
3726
+ const getShadows = ({ theme }: CommonThemeProps): Shadows | null => {
3727
+ return theme?.app?.shadows || null;
3728
+ };
3729
+
3730
+ const getSpaces = ({ theme }: CommonThemeProps): Spaces | null => {
3731
+ return theme?.app?.spacing || null;
3732
+ };
3733
+
3734
+ const CustomPopper = styled(Popper)`
3735
+ .MuiAutocomplete-popperDisablePortal {
3736
+ position: relative;
3737
+ width: 100% !important;
3738
+ box-shadow: none;
3739
+ padding: 0;
3740
+ border: none;
3741
+ }
3742
+
3743
+ ${(props) => {
3744
+ const borders = getBorders(props);
3745
+ const corners = getCorners(props);
3746
+ const shadows = getShadows(props);
3747
+ const spacings = getSpaces(props);
3748
+
3749
+ return `
3750
+ background-color: white;
3751
+ border: ${borders?.base?.divider};
3752
+ border-radius: ${corners?.m}px;
3753
+ box-shadow: ${shadows?.m};
3754
+ padding: ${spacings?.xs}px;
3755
+ box-sizing: border-box;
3756
+ z-index: 1400;
3757
+ `;
3758
+ }}
3759
+
3760
+ // Step 2/3: Modify or add to the Popper component's base styles (copied in above)
3761
+ // by putting your custom styles at the end:
3762
+
3763
+ border-color: rebeccapurple;
3764
+ border-style: dotted;
3765
+ border-width: 15px !important;
3766
+ `;
3767
+
3768
+ const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" } };
3769
+
3770
+ function App() {
3771
+ const ref = useRef(null);
3772
+ const [open, setOpen] = useState(false);
3773
+
3774
+ useEffect(() => {
3775
+ if (ref.current) setOpen(true);
3776
+ }, [ref.current]);
3777
+
3778
+ return (
3779
+ <div className="app" style={{ paddingLeft: "10px" }}>
3780
+ <div ref={ref} />
3781
+ {open && (
3782
+ // 👇 Only pay attention to the props here, everything else is just Zeroheight glue code
3783
+ <DropdownMenu
3784
+ PopperBaseProps={POPPER_BASE_PROPS}
3785
+ anchorEl={ref.current}
3786
+ onClickAway={function handleClickAway() {}}
3787
+ open
3788
+ options={MENU_ITEMS}
3789
+ // Step 3/3: Pass the new CustomPopper to PopperComponent prop
3790
+ PopperComponent={CustomPopper}
3791
+ />
3792
+ )}
3793
+ </div>
3794
+ );
3795
+ }
3796
+
3797
+ export default App;
3798
+ ```
3799
+
3800
+ **/index.tsx**
3801
+
3802
+ ```tsx
3803
+ import React from "react";
3804
+ import { createRoot } from "react-dom/client";
3805
+ import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
3806
+ import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
3807
+ import { Theme } from "@czi-sds/components";
3808
+ import CssBaseline from "@mui/material/CssBaseline";
3809
+ import useMediaQuery from "@mui/material/useMediaQuery";
3810
+
3811
+ import App from "./App";
3812
+
3813
+ const container = document.getElementById("root");
3814
+ const root = createRoot(container!);
3815
+
3816
+ const RootApp = () => {
3817
+ const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
3818
+ const theme = prefersDarkMode ? Theme("dark") : Theme("light");
3819
+
3820
+ return (
3821
+ <StyledEngineProvider injectFirst>
3822
+ <ThemeProvider theme={theme}>
3823
+ <EmotionThemeProvider theme={theme}>
3824
+ <CssBaseline />
3825
+ <App />
3826
+ </EmotionThemeProvider>
3827
+ </ThemeProvider>
3828
+ </StyledEngineProvider>
3829
+ );
3830
+ };
3831
+
3832
+ root.render(
3833
+ <React.StrictMode>
3834
+ <RootApp />
3835
+ </React.StrictMode>
3836
+ );
3837
+ ```
3838
+
3839
+ **/public/index.html**
3840
+
3841
+ ```html
3842
+ <!doctype html>
3843
+ <html lang="en">
3844
+ <head>
3845
+ <meta charset="UTF-8" />
3846
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
3847
+ <title>DropdownMenu</title>
3848
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
3849
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
3850
+ <link
3851
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
3852
+ rel="stylesheet"
3853
+ />
3854
+ <link
3855
+ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&display=swap"
3856
+ rel="stylesheet"
3857
+ />
3858
+ </head>
3859
+ <body>
3860
+ <div id="root"></div>
3861
+ </body>
3862
+ </html>
3863
+ ```
3864
+
3865
+ **/package.json**
3866
+
3867
+ ```json
3868
+ {
3869
+ "dependencies": {
3870
+ "react": "^18.0.0",
3871
+ "react-dom": "^18.0.0",
3872
+ "react-scripts": "^4.0.0"
3873
+ },
3874
+ "devDependencies": {
3875
+ "@types/react": "^18.0.0",
3876
+ "@types/react-dom": "^18.0.0",
3877
+ "typescript": "^4.0.0"
3878
+ },
3879
+ "main": "/index.tsx"
3880
+ }
3881
+ ```
3882
+
3883
+ ---