@czi-sds/mcp 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/data/component-docs/Accordion.md +291 -0
- package/data/component-docs/Autocomplete.md +525 -0
- package/data/component-docs/Banner.md +231 -0
- package/data/component-docs/Button.md +224 -0
- package/data/component-docs/ButtonDropdown.md +256 -0
- package/data/component-docs/ButtonGroup.md +320 -0
- package/data/component-docs/ButtonToggle.md +397 -0
- package/data/component-docs/Callout.md +268 -0
- package/data/component-docs/CellBasic.md +122 -0
- package/data/component-docs/CellComponent.md +63 -0
- package/data/component-docs/CellHeader.md +128 -0
- package/data/component-docs/ComplexFilter.md +294 -0
- package/data/component-docs/ContentCard.md +406 -0
- package/data/component-docs/Dialog.md +651 -0
- package/data/component-docs/Dropdown.md +155 -0
- package/data/component-docs/DropdownMenu.md +949 -0
- package/data/component-docs/HeatmapChart.md +782 -0
- package/data/component-docs/Hero.md +363 -0
- package/data/component-docs/Icon.md +283 -0
- package/data/component-docs/InputCheckbox.md +205 -0
- package/data/component-docs/InputDropdown.md +482 -0
- package/data/component-docs/InputRadio.md +173 -0
- package/data/component-docs/InputSearch.md +81 -0
- package/data/component-docs/InputSlider.md +189 -0
- package/data/component-docs/InputText.md +110 -0
- package/data/component-docs/InputToggle.md +141 -0
- package/data/component-docs/IntentMessage.md +293 -0
- package/data/component-docs/Legend.md +442 -0
- package/data/component-docs/Link.md +23 -0
- package/data/component-docs/List.md +300 -0
- package/data/component-docs/LoadingIndicator.md +198 -0
- package/data/component-docs/Menu.md +298 -0
- package/data/component-docs/MenuItem.md +100 -0
- package/data/component-docs/NavigationFooter.md +165 -0
- package/data/component-docs/NavigationHeader.md +500 -0
- package/data/component-docs/NavigationJumpTo.md +414 -0
- package/data/component-docs/Notification.md +288 -0
- package/data/component-docs/Pagination.md +67 -0
- package/data/component-docs/Panel.md +636 -0
- package/data/component-docs/Popover.md +448 -0
- package/data/component-docs/PreComposedTable.md +2947 -0
- package/data/component-docs/ProteinStructureViewer.md +1044 -0
- package/data/component-docs/SegmentedControl.md +396 -0
- package/data/component-docs/StackedBarChart.md +475 -0
- package/data/component-docs/Table.md +2000 -0
- package/data/component-docs/TableHeader.md +53 -0
- package/data/component-docs/TableRow.md +74 -0
- package/data/component-docs/Tabs.md +455 -0
- package/data/component-docs/Tag.md +647 -0
- package/data/component-docs/TagFilter.md +131 -0
- package/data/component-docs/Tooltip.md +561 -0
- package/data/component-docs/TooltipCondensed.md +203 -0
- package/data/component-docs/TooltipTable.md +145 -0
- package/data/component-docs/index.json +267 -0
- package/data/component-list.json +2 -5
- package/data/component-props/Accordion.json +31 -11
- package/data/component-props/Alert.json +3 -3
- package/data/component-props/Autocomplete.json +150 -15
- package/data/component-props/Banner.json +31 -17
- package/data/component-props/Button.json +45 -20
- package/data/component-props/ButtonDropdown.json +36 -17
- package/data/component-props/ButtonDropdownLegacy.json +11 -11
- package/data/component-props/ButtonGroup.json +32 -11
- package/data/component-props/ButtonIcon.json +10 -10
- package/data/component-props/ButtonLegacy.json +19 -19
- package/data/component-props/ButtonToggle.json +32 -19
- package/data/component-props/ButtonToggleLegacy.json +11 -11
- package/data/component-props/Callout.json +58 -17
- package/data/component-props/CellBasic.json +94 -41
- package/data/component-props/CellComponent.json +23 -7
- package/data/component-props/CellHeader.json +62 -19
- package/data/component-props/ComplexFilter.json +52 -21
- package/data/component-props/ContentCard.json +70 -47
- package/data/component-props/Dialog.json +47 -14
- package/data/component-props/Dropdown.json +68 -31
- package/data/component-props/DropdownMenu.json +128 -23
- package/data/component-props/HeatmapChart.json +88 -12
- package/data/component-props/Hero.json +80 -39
- package/data/component-props/Icon.json +20 -9
- package/data/component-props/InputCheckbox.json +36 -14
- package/data/component-props/InputDropdown.json +75 -27
- package/data/component-props/InputRadio.json +31 -13
- package/data/component-props/InputSearch.json +41 -18
- package/data/component-props/InputSlider.json +46 -19
- package/data/component-props/InputText.json +26 -19
- package/data/component-props/InputToggle.json +26 -9
- package/data/component-props/IntentMessage.json +38 -9
- package/data/component-props/Link.json +13 -7
- package/data/component-props/LoadingIndicator.json +11 -3
- package/data/component-props/MenuItem.json +38 -19
- package/data/component-props/MenuSelect.json +8 -8
- package/data/component-props/NavigationFooter.json +49 -19
- package/data/component-props/NavigationHeader.json +150 -37
- package/data/component-props/NavigationJumpTo.json +33 -7
- package/data/component-props/Notification.json +39 -21
- package/data/component-props/Pagination.json +39 -11
- package/data/component-props/Panel.json +62 -9
- package/data/component-props/Popover.json +80 -7
- package/data/component-props/PreComposedTable.json +99 -27
- package/data/component-props/ProteinStructureViewer.json +117 -0
- package/data/component-props/SegmentedControl.json +42 -3
- package/data/component-props/StackedBarChart.json +116 -42
- package/data/component-props/TableRow.json +38 -19
- package/data/component-props/Tabs.json +66 -9
- package/data/component-props/Tag.json +34 -17
- package/data/component-props/TagFilter.json +10 -3
- package/data/component-props/Tooltip.json +49 -18
- package/data/component-props/TooltipCondensed.json +18 -9
- package/data/component-props/TooltipTable.json +17 -7
- package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
- package/data/resources/figma-rules.md +2 -2
- package/data/tailwind.json +20 -14
- package/dist/stdio.js +112 -208
- package/package.json +9 -5
- package/data/component-props/ButtonDropdownV2.json +0 -34
- package/data/component-props/ButtonNew.json +0 -47
- package/data/component-props/ButtonToggleV2.json +0 -44
- package/data/component-props/ButtonV2.json +0 -43
- package/data/zeroheight-cache.json +0 -10259
- package/data/zeroheight-exports/Accordion.md +0 -774
- package/data/zeroheight-exports/Banner.md +0 -691
- package/data/zeroheight-exports/Borders.md +0 -117
- package/data/zeroheight-exports/Buttons.md +0 -1854
- package/data/zeroheight-exports/Callout.md +0 -931
- package/data/zeroheight-exports/Colors.md +0 -1755
- package/data/zeroheight-exports/Content-Card.md +0 -309
- package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
- package/data/zeroheight-exports/Control-Inputs.md +0 -2818
- package/data/zeroheight-exports/Corners.md +0 -104
- package/data/zeroheight-exports/Cover-page.md +0 -3
- package/data/zeroheight-exports/Dialog.md +0 -1367
- package/data/zeroheight-exports/Downloads.md +0 -236
- package/data/zeroheight-exports/Drop-Shadows.md +0 -79
- package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
- package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
- package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
- package/data/zeroheight-exports/Field-Inputs.md +0 -1405
- package/data/zeroheight-exports/Filters.md +0 -1111
- package/data/zeroheight-exports/Getting-Started.md +0 -1157
- package/data/zeroheight-exports/Hero.md +0 -414
- package/data/zeroheight-exports/Icons.md +0 -2067
- package/data/zeroheight-exports/Intent.md +0 -86
- package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
- package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
- package/data/zeroheight-exports/Introduction.md +0 -115
- package/data/zeroheight-exports/Link.md +0 -432
- package/data/zeroheight-exports/Lists.md +0 -986
- package/data/zeroheight-exports/Loading-Indicator.md +0 -41
- package/data/zeroheight-exports/Navigation.md +0 -1190
- package/data/zeroheight-exports/Notification.md +0 -1038
- package/data/zeroheight-exports/Panel.md +0 -1030
- package/data/zeroheight-exports/README.md +0 -122
- package/data/zeroheight-exports/Responsive-Design.md +0 -330
- package/data/zeroheight-exports/Search-Input.md +0 -91
- package/data/zeroheight-exports/Search.md +0 -822
- package/data/zeroheight-exports/Segmented-Control.md +0 -609
- package/data/zeroheight-exports/Spacing.md +0 -297
- package/data/zeroheight-exports/Table.md +0 -5345
- package/data/zeroheight-exports/Tabs.md +0 -1324
- package/data/zeroheight-exports/Tags.md +0 -1545
- package/data/zeroheight-exports/Text-Link.md +0 -268
- package/data/zeroheight-exports/Theming.md +0 -362
- package/data/zeroheight-exports/Tooltips.md +0 -2015
- package/data/zeroheight-exports/Typography.md +0 -800
|
@@ -0,0 +1,949 @@
|
|
|
1
|
+
# DropdownMenu
|
|
2
|
+
|
|
3
|
+
> **DropdownMenu is not Menu.** This is the menu half of Dropdown: a list built from an `options` array, for choosing a value your app then keeps, with search and sections and several selections at once. Menu is for commands, whose rows you write yourself and which leave nothing behind once one is picked. Use this one when you need Dropdown's list but not its trigger, and see the Dropdowns overview for the rest of the family.
|
|
4
|
+
|
|
5
|
+
## Source Code
|
|
6
|
+
|
|
7
|
+
The component's source code in the SDS codebase can be found [here](https://github.com/chanzuckerberg/sci-components/blob/main/packages/components/src/core/DropdownMenu/index.tsx).
|
|
8
|
+
|
|
9
|
+
## Import
|
|
10
|
+
|
|
11
|
+
**React TypeScript**
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { DropdownMenu } from "@czi-sds/components";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Code examples
|
|
18
|
+
|
|
19
|
+
### Default
|
|
20
|
+
|
|
21
|
+
This example has the minimum props and menu item metadata needed for the DropdownMenu. The examples that follow build on it.
|
|
22
|
+
|
|
23
|
+
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 “Grouped sections” and “Menu items with count, icon, and details” examples below. Alternatively, a custom component can be displayed instead of a MenuItem component; see the “Components in place of menu items” example below.
|
|
24
|
+
|
|
25
|
+
The menu is 160px wide by default and never narrower, whatever `width` asks for. Options laid out in columns are the exception: those size themselves to their content and ignore `width` altogether.
|
|
26
|
+
|
|
27
|
+
**Example: Default**
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
// Most minimal DropdownMenu (just has the basic requirements)
|
|
31
|
+
|
|
32
|
+
import { useEffect, useRef, useState } from "react";
|
|
33
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
34
|
+
import styled from "@emotion/styled";
|
|
35
|
+
|
|
36
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
37
|
+
{
|
|
38
|
+
name: "Menu item 1",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "Menu item 2",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "Menu item 3",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "Longer menu item than the others",
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
52
|
+
|
|
53
|
+
const Anchor = styled.div`
|
|
54
|
+
position: absolute;
|
|
55
|
+
top: 12px;
|
|
56
|
+
left: 20px;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
function handleClickAway() {}
|
|
60
|
+
|
|
61
|
+
function App() {
|
|
62
|
+
const ref = useRef(null);
|
|
63
|
+
const [open, setOpen] = useState(false);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (ref.current) setOpen(true);
|
|
67
|
+
}, [ref.current]);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="app">
|
|
71
|
+
<Anchor ref={ref} />
|
|
72
|
+
{open && (
|
|
73
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
74
|
+
<DropdownMenu
|
|
75
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
76
|
+
anchorEl={ref.current}
|
|
77
|
+
onClickAway={handleClickAway}
|
|
78
|
+
open={open}
|
|
79
|
+
options={MENU_ITEMS}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default App;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Custom click target
|
|
90
|
+
|
|
91
|
+
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.
|
|
92
|
+
|
|
93
|
+
**Example: CustomClickTarget**
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// DropdownMenu with custom click target
|
|
97
|
+
|
|
98
|
+
import { SyntheticEvent, useState } from "react";
|
|
99
|
+
import {
|
|
100
|
+
DropdownMenu,
|
|
101
|
+
DefaultAutocompleteOption,
|
|
102
|
+
InputDropdown,
|
|
103
|
+
} from "@czi-sds/components";
|
|
104
|
+
|
|
105
|
+
function App() {
|
|
106
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
107
|
+
{
|
|
108
|
+
name: "Menu item 1",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "Menu item 2",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "Menu item 3",
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
|
119
|
+
const [open, setOpen] = useState(false);
|
|
120
|
+
const [value, setValue] = useState<DefaultAutocompleteOption | null>(null);
|
|
121
|
+
|
|
122
|
+
function handleClick(event: SyntheticEvent<HTMLElement>) {
|
|
123
|
+
if (open) {
|
|
124
|
+
setOpen(false);
|
|
125
|
+
if (anchorEl) {
|
|
126
|
+
anchorEl.focus();
|
|
127
|
+
}
|
|
128
|
+
setAnchorEl(null);
|
|
129
|
+
} else {
|
|
130
|
+
setAnchorEl(event.currentTarget);
|
|
131
|
+
setOpen(true);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function handleChange(
|
|
136
|
+
_: SyntheticEvent,
|
|
137
|
+
newValue: DefaultAutocompleteOption | null
|
|
138
|
+
) {
|
|
139
|
+
setOpen(false);
|
|
140
|
+
setValue(newValue);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function handleClickAway() {
|
|
144
|
+
return open && setOpen(false);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="app">
|
|
149
|
+
<InputDropdown
|
|
150
|
+
onClick={handleClick}
|
|
151
|
+
label="Click Target"
|
|
152
|
+
sdsType="label"
|
|
153
|
+
sdsStyle="minimal"
|
|
154
|
+
/>
|
|
155
|
+
<DropdownMenu<DefaultAutocompleteOption, false, false, false>
|
|
156
|
+
options={MENU_ITEMS}
|
|
157
|
+
anchorEl={anchorEl}
|
|
158
|
+
open={!!open}
|
|
159
|
+
onChange={handleChange}
|
|
160
|
+
value={value}
|
|
161
|
+
onClickAway={handleClickAway}
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default App;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Search
|
|
171
|
+
|
|
172
|
+
An InputSearch component can be incorporated into the DropdownMenu component by passing it the `search` prop.
|
|
173
|
+
|
|
174
|
+
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.
|
|
175
|
+
|
|
176
|
+
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 “Search cleared on select” example below.
|
|
177
|
+
|
|
178
|
+
1. In the search box, type in "fruit"
|
|
179
|
+
|
|
180
|
+
2. DropdownMenu will display "Fruit: Apple" and "Fruit: Orange"
|
|
181
|
+
|
|
182
|
+
3. Select "Fruit: Apple"
|
|
183
|
+
|
|
184
|
+
4. Note that the search text is maintained along with the limited list of items
|
|
185
|
+
|
|
186
|
+
**Example: Search**
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
// DropdownMenu with search
|
|
190
|
+
|
|
191
|
+
import { useEffect, useRef, useState } from "react";
|
|
192
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
193
|
+
import styled from "@emotion/styled";
|
|
194
|
+
|
|
195
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
196
|
+
{
|
|
197
|
+
name: "Fruit: Apple",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "Fruit: Orange",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "Vegetable: Carrot",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: "Vegetable: Kale",
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
211
|
+
|
|
212
|
+
const Anchor = styled.div`
|
|
213
|
+
position: absolute;
|
|
214
|
+
top: 12px;
|
|
215
|
+
left: 20px;
|
|
216
|
+
`;
|
|
217
|
+
|
|
218
|
+
function handleClickAway() {}
|
|
219
|
+
|
|
220
|
+
function App() {
|
|
221
|
+
const ref = useRef(null);
|
|
222
|
+
const [open, setOpen] = useState(false);
|
|
223
|
+
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
if (ref.current) setOpen(true);
|
|
226
|
+
}, [ref.current]);
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<div className="app">
|
|
230
|
+
<Anchor ref={ref} />
|
|
231
|
+
{open && (
|
|
232
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
233
|
+
<DropdownMenu
|
|
234
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
235
|
+
anchorEl={ref.current}
|
|
236
|
+
noOptionsText="No matches found, try again!"
|
|
237
|
+
onClickAway={handleClickAway}
|
|
238
|
+
open
|
|
239
|
+
options={MENU_ITEMS}
|
|
240
|
+
search
|
|
241
|
+
/>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export default App;
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Multi-select
|
|
251
|
+
|
|
252
|
+
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.
|
|
253
|
+
|
|
254
|
+
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.
|
|
255
|
+
|
|
256
|
+
**Example: MultiSelect**
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
// DropdownMenu with multi-select
|
|
260
|
+
|
|
261
|
+
import { useEffect, useRef, useState } from "react";
|
|
262
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
263
|
+
import styled from "@emotion/styled";
|
|
264
|
+
|
|
265
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
266
|
+
{
|
|
267
|
+
name: "Menu item 1",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "Menu item 2",
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: "Menu item 3",
|
|
274
|
+
},
|
|
275
|
+
];
|
|
276
|
+
|
|
277
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
278
|
+
|
|
279
|
+
const Anchor = styled.div`
|
|
280
|
+
position: absolute;
|
|
281
|
+
top: 12px;
|
|
282
|
+
left: 20px;
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
function handleClickAway() {}
|
|
286
|
+
|
|
287
|
+
function App() {
|
|
288
|
+
const ref = useRef(null);
|
|
289
|
+
const [open, setOpen] = useState(false);
|
|
290
|
+
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
if (ref.current) setOpen(true);
|
|
293
|
+
}, [ref.current]);
|
|
294
|
+
|
|
295
|
+
return (
|
|
296
|
+
<div className="app">
|
|
297
|
+
<Anchor ref={ref} />
|
|
298
|
+
{open && (
|
|
299
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
300
|
+
<DropdownMenu
|
|
301
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
302
|
+
anchorEl={ref.current}
|
|
303
|
+
multiple
|
|
304
|
+
onClickAway={handleClickAway}
|
|
305
|
+
open
|
|
306
|
+
options={MENU_ITEMS}
|
|
307
|
+
/>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export default App;
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Search cleared on select
|
|
317
|
+
|
|
318
|
+
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`.
|
|
319
|
+
|
|
320
|
+
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).
|
|
321
|
+
|
|
322
|
+
To see how this works:
|
|
323
|
+
|
|
324
|
+
1. In the search box, type in "fruit"
|
|
325
|
+
|
|
326
|
+
2. DropdownMenu will display "Fruit: Apple" and "Fruit: Orange"
|
|
327
|
+
|
|
328
|
+
3. Select "Fruit: Apple"
|
|
329
|
+
|
|
330
|
+
4. Note that the search text is now cleared and all dropdown items reappear
|
|
331
|
+
|
|
332
|
+
Compare this to the default behavior in the " Search " example above, or change the `keepSearchOnSelect` prop in the example below to `true`.
|
|
333
|
+
|
|
334
|
+
**Example: ClearSearchOnSelect**
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
// Dropdown with multi-select that does not maintain searched query upon selection of a menu item
|
|
338
|
+
|
|
339
|
+
import { useEffect, useRef, useState } from "react";
|
|
340
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
341
|
+
import styled from "@emotion/styled";
|
|
342
|
+
|
|
343
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
344
|
+
{
|
|
345
|
+
name: "Fruit: Apple",
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: "Fruit: Orange",
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "Vegetable: Carrot",
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "Vegetable: Kale",
|
|
355
|
+
},
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
359
|
+
|
|
360
|
+
const Anchor = styled.div`
|
|
361
|
+
position: absolute;
|
|
362
|
+
top: 12px;
|
|
363
|
+
left: 20px;
|
|
364
|
+
`;
|
|
365
|
+
|
|
366
|
+
function App() {
|
|
367
|
+
const ref = useRef(null);
|
|
368
|
+
const [open, setOpen] = useState(false);
|
|
369
|
+
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
if (ref.current) setOpen(true);
|
|
372
|
+
}, [ref.current]);
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<div className="app">
|
|
376
|
+
<Anchor ref={ref} />
|
|
377
|
+
{open && (
|
|
378
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
379
|
+
<DropdownMenu
|
|
380
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
381
|
+
anchorEl={ref.current}
|
|
382
|
+
keepSearchOnSelect={false}
|
|
383
|
+
multiple
|
|
384
|
+
onClickAway={function handleClickAway() {}}
|
|
385
|
+
open
|
|
386
|
+
options={MENU_ITEMS}
|
|
387
|
+
search
|
|
388
|
+
/>
|
|
389
|
+
)}
|
|
390
|
+
</div>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export default App;
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Grouped sections
|
|
398
|
+
|
|
399
|
+
MenuItems can be displayed in groups with a label.
|
|
400
|
+
|
|
401
|
+
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](https://mui.com/material-ui/api/autocomplete/#props) is not provided. For example:
|
|
402
|
+
|
|
403
|
+
**React TypeScript**
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
// group items by section
|
|
407
|
+
groupBy={(option) => option.section}
|
|
408
|
+
|
|
409
|
+
// group items by the last character of their names
|
|
410
|
+
groupBy={(option) => option.name.at(-1)}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
This `groupBy` prop must be provided or the groups will not render, even if all menu items have `section` properties (see below).
|
|
414
|
+
|
|
415
|
+
> **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](https://github.com/mui/material-ui/issues/21967#issuecomment-665006321) and example below)
|
|
416
|
+
|
|
417
|
+
**Example: GroupedSections**
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
// Dropdown with grouped sections
|
|
421
|
+
|
|
422
|
+
import { useEffect, useRef, useState } from "react";
|
|
423
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
424
|
+
import styled from "@emotion/styled";
|
|
425
|
+
|
|
426
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
427
|
+
{
|
|
428
|
+
name: "Menu item A.1",
|
|
429
|
+
section: "Section A",
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: "Menu item A.2",
|
|
433
|
+
section: "Section A",
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "Menu item without a specified section",
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: "Menu item B.1",
|
|
440
|
+
section: "Section B",
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: "Menu item C.1",
|
|
444
|
+
section: "Section C",
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "Menu item C.2",
|
|
448
|
+
section: "Section C",
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
name: "Menu item C.3",
|
|
452
|
+
section: "Section C",
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: "Menu item B.2",
|
|
456
|
+
section: "Section B",
|
|
457
|
+
},
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Items need to be sorted by the same groupBy dimension
|
|
462
|
+
* In this example, we're grouping by the last character of the item name
|
|
463
|
+
*/
|
|
464
|
+
const SORTED_ITEMS = [...MENU_ITEMS].sort((a, b) => {
|
|
465
|
+
const aLastLetter = a.name.slice(-1);
|
|
466
|
+
const bLastLetter = b.name.slice(-1);
|
|
467
|
+
|
|
468
|
+
if (aLastLetter < bLastLetter) {
|
|
469
|
+
return -1;
|
|
470
|
+
}
|
|
471
|
+
if (aLastLetter > bLastLetter) {
|
|
472
|
+
return 1;
|
|
473
|
+
}
|
|
474
|
+
return 0;
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
function groupBy(option: DefaultAutocompleteOption) {
|
|
478
|
+
return option.name.slice(-1);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
482
|
+
|
|
483
|
+
const Anchor = styled.div`
|
|
484
|
+
position: absolute;
|
|
485
|
+
top: 12px;
|
|
486
|
+
left: 20px;
|
|
487
|
+
`;
|
|
488
|
+
|
|
489
|
+
function handleClickAway() {}
|
|
490
|
+
|
|
491
|
+
function App() {
|
|
492
|
+
const ref = useRef(null);
|
|
493
|
+
const [open, setOpen] = useState(false);
|
|
494
|
+
|
|
495
|
+
useEffect(() => {
|
|
496
|
+
if (ref.current) setOpen(true);
|
|
497
|
+
}, [ref.current]);
|
|
498
|
+
|
|
499
|
+
return (
|
|
500
|
+
<div className="app">
|
|
501
|
+
<Anchor ref={ref} />
|
|
502
|
+
{open && (
|
|
503
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
504
|
+
<DropdownMenu
|
|
505
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
506
|
+
anchorEl={ref.current}
|
|
507
|
+
groupBy={groupBy}
|
|
508
|
+
onClickAway={handleClickAway}
|
|
509
|
+
open
|
|
510
|
+
options={SORTED_ITEMS}
|
|
511
|
+
/>
|
|
512
|
+
)}
|
|
513
|
+
</div>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export default App;
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Recipes
|
|
521
|
+
|
|
522
|
+
These recipes show a few of the more complex approaches available for customizing Dropdown Menus.
|
|
523
|
+
|
|
524
|
+
### Custom isOptionEqualToValue
|
|
525
|
+
|
|
526
|
+
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.
|
|
527
|
+
|
|
528
|
+
This is a MUI prop; learn more in the [Autocomplete API](https://mui.com/material-ui/api/autocomplete/#props).
|
|
529
|
+
|
|
530
|
+
**Example: CustomIsOptionEqualToValue**
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
// DropdownMenu with customized `isOptionEqualToValue`
|
|
534
|
+
|
|
535
|
+
import { useEffect, useRef, useState } from "react";
|
|
536
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
537
|
+
import styled from "@emotion/styled";
|
|
538
|
+
|
|
539
|
+
type MenuItem = DefaultAutocompleteOption & { id: string };
|
|
540
|
+
|
|
541
|
+
function optionIdEqualToValueId(option: MenuItem, value: MenuItem) {
|
|
542
|
+
return option.id === value.id;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const MENU_ITEMS: MenuItem[] = [
|
|
546
|
+
{
|
|
547
|
+
name: "Menu item 1",
|
|
548
|
+
id: "one",
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
name: "Menu item 2",
|
|
552
|
+
id: "two",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
name: "Menu item 3",
|
|
556
|
+
id: "three",
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
name: "Longer menu item than the others",
|
|
560
|
+
id: "four",
|
|
561
|
+
},
|
|
562
|
+
];
|
|
563
|
+
|
|
564
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
565
|
+
|
|
566
|
+
const Anchor = styled.div`
|
|
567
|
+
position: absolute;
|
|
568
|
+
top: 12px;
|
|
569
|
+
left: 20px;
|
|
570
|
+
`;
|
|
571
|
+
|
|
572
|
+
function handleClickAway() {}
|
|
573
|
+
|
|
574
|
+
function App() {
|
|
575
|
+
const ref = useRef(null);
|
|
576
|
+
const [open, setOpen] = useState(false);
|
|
577
|
+
|
|
578
|
+
useEffect(() => {
|
|
579
|
+
if (ref.current) setOpen(true);
|
|
580
|
+
}, [ref.current]);
|
|
581
|
+
|
|
582
|
+
return (
|
|
583
|
+
<div className="app">
|
|
584
|
+
<Anchor ref={ref} />
|
|
585
|
+
{open && (
|
|
586
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
587
|
+
<DropdownMenu<MenuItem, false, false, false>
|
|
588
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
589
|
+
anchorEl={ref.current}
|
|
590
|
+
isOptionEqualToValue={optionIdEqualToValueId}
|
|
591
|
+
onClickAway={handleClickAway}
|
|
592
|
+
open
|
|
593
|
+
options={MENU_ITEMS}
|
|
594
|
+
/>
|
|
595
|
+
)}
|
|
596
|
+
</div>
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export default App;
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Custom onChange
|
|
604
|
+
|
|
605
|
+
The optional `onChange` prop specifies what happens when a new selection is made.
|
|
606
|
+
|
|
607
|
+
In the example below, `onChange` has been modified such that making a new selection logs the value and reason to the console.
|
|
608
|
+
|
|
609
|
+
This is a MUI prop; learn more in the [Autocomplete API](https://mui.com/material-ui/api/autocomplete/#props).
|
|
610
|
+
|
|
611
|
+
**Example: CustomOnChange**
|
|
612
|
+
|
|
613
|
+
```tsx
|
|
614
|
+
// DropdownMenu with customized `onInputChange`
|
|
615
|
+
|
|
616
|
+
import { SyntheticEvent, useEffect, useRef, useState } from "react";
|
|
617
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
618
|
+
import styled from "@emotion/styled";
|
|
619
|
+
|
|
620
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
621
|
+
{
|
|
622
|
+
name: "Menu item 1",
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: "Menu item 2",
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
name: "Menu item 3",
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
name: "Longer menu item than the others",
|
|
632
|
+
},
|
|
633
|
+
];
|
|
634
|
+
|
|
635
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
636
|
+
|
|
637
|
+
const Anchor = styled.div`
|
|
638
|
+
position: absolute;
|
|
639
|
+
top: 12px;
|
|
640
|
+
left: 20px;
|
|
641
|
+
`;
|
|
642
|
+
|
|
643
|
+
function handleClickAway() {}
|
|
644
|
+
|
|
645
|
+
type ChangeMessage = { reason: string; value: unknown };
|
|
646
|
+
|
|
647
|
+
function Message({ message }: { message?: ChangeMessage }) {
|
|
648
|
+
return (
|
|
649
|
+
<div
|
|
650
|
+
style={{ position: "absolute", top: "170px", fontFamily: "sans-serif" }}
|
|
651
|
+
>
|
|
652
|
+
<p>{JSON.stringify(message)}</p>
|
|
653
|
+
</div>
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function App() {
|
|
658
|
+
const [message, setMessage] = useState<ChangeMessage>();
|
|
659
|
+
|
|
660
|
+
const ref = useRef(null);
|
|
661
|
+
const [open, setOpen] = useState(false);
|
|
662
|
+
|
|
663
|
+
useEffect(() => {
|
|
664
|
+
if (ref.current) setOpen(true);
|
|
665
|
+
}, [ref.current]);
|
|
666
|
+
|
|
667
|
+
return (
|
|
668
|
+
<div className="app">
|
|
669
|
+
<Anchor ref={ref} />
|
|
670
|
+
{open && (
|
|
671
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
672
|
+
<>
|
|
673
|
+
<DropdownMenu
|
|
674
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
675
|
+
anchorEl={ref.current}
|
|
676
|
+
keepSearchOnSelect
|
|
677
|
+
onClickAway={handleClickAway}
|
|
678
|
+
onChange={(_: SyntheticEvent, value: unknown, reason: string) =>
|
|
679
|
+
setMessage({ reason, value })
|
|
680
|
+
}
|
|
681
|
+
open
|
|
682
|
+
options={MENU_ITEMS}
|
|
683
|
+
/>
|
|
684
|
+
<Message message={message} />
|
|
685
|
+
</>
|
|
686
|
+
)}
|
|
687
|
+
</div>
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
export default App;
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Custom PopperBaseProps
|
|
695
|
+
|
|
696
|
+
`PopperBaseProps` is an SDS-specific prop made for passing custom props to the Popper component, which is included as part of the DropdownMenu component.
|
|
697
|
+
|
|
698
|
+
The Popper component is part of MUI; learn more in the [Popper documentation](https://mui.com/material-ui/api/popper/), and see all the props available to pass to it on the [Popper API page](https://mui.com/material-ui/api/popper/).
|
|
699
|
+
|
|
700
|
+
The example below passes the Popper a `className` prop and additionally uses the `sx` prop to make style changes (learn more on the [MUI sx documentation page](https://mui.com/system/getting-started/the-sx-prop/)).
|
|
701
|
+
|
|
702
|
+
**Example: CustomPopperBaseProps**
|
|
703
|
+
|
|
704
|
+
```tsx
|
|
705
|
+
// DropdownMenu with custom `PopperBaseProps`
|
|
706
|
+
|
|
707
|
+
import { useEffect, useRef, useState } from "react";
|
|
708
|
+
import { DropdownMenu, DefaultAutocompleteOption } from "@czi-sds/components";
|
|
709
|
+
import styled from "@emotion/styled";
|
|
710
|
+
|
|
711
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
712
|
+
{
|
|
713
|
+
name: "Menu item 1",
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
name: "Menu item 2",
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
name: "Menu item 3",
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
name: "Longer menu item than the others",
|
|
723
|
+
},
|
|
724
|
+
];
|
|
725
|
+
|
|
726
|
+
const POPPER_BASE_PROPS = {
|
|
727
|
+
className: "popper",
|
|
728
|
+
sx: {
|
|
729
|
+
width: 500,
|
|
730
|
+
borderColor: "salmon",
|
|
731
|
+
borderWidth: 10,
|
|
732
|
+
},
|
|
733
|
+
popperOptions: { strategy: "absolute" as const },
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const Anchor = styled.div`
|
|
737
|
+
position: absolute;
|
|
738
|
+
top: 12px;
|
|
739
|
+
left: 20px;
|
|
740
|
+
`;
|
|
741
|
+
|
|
742
|
+
function App() {
|
|
743
|
+
const ref = useRef(null);
|
|
744
|
+
const [open, setOpen] = useState(false);
|
|
745
|
+
|
|
746
|
+
useEffect(() => {
|
|
747
|
+
if (ref.current) setOpen(true);
|
|
748
|
+
}, [ref.current]);
|
|
749
|
+
|
|
750
|
+
return (
|
|
751
|
+
<div className="app">
|
|
752
|
+
<Anchor ref={ref} />
|
|
753
|
+
{open && (
|
|
754
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
755
|
+
<DropdownMenu
|
|
756
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
757
|
+
anchorEl={ref.current}
|
|
758
|
+
onClickAway={function handleClickAway() {}}
|
|
759
|
+
open
|
|
760
|
+
options={MENU_ITEMS}
|
|
761
|
+
/>
|
|
762
|
+
)}
|
|
763
|
+
</div>
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export default App;
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Custom Popper component
|
|
771
|
+
|
|
772
|
+
In addition to modifying the Popper's styles via `PopperBaseProps` above, the styles for the Popper component can be overwritten more directly with `styled()`.
|
|
773
|
+
|
|
774
|
+
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`.
|
|
775
|
+
|
|
776
|
+
**Example: CustomPopperComponent**
|
|
777
|
+
|
|
778
|
+
```tsx
|
|
779
|
+
// DropdownMenu with custom Popper component
|
|
780
|
+
|
|
781
|
+
import { useEffect, useRef, useState } from "react";
|
|
782
|
+
import { Popper, styled } from "@mui/material";
|
|
783
|
+
import {
|
|
784
|
+
DropdownMenu,
|
|
785
|
+
DefaultAutocompleteOption,
|
|
786
|
+
Borders,
|
|
787
|
+
CommonThemeProps,
|
|
788
|
+
Corners,
|
|
789
|
+
Shadows,
|
|
790
|
+
Spaces,
|
|
791
|
+
} from "@czi-sds/components";
|
|
792
|
+
|
|
793
|
+
const MENU_ITEMS: DefaultAutocompleteOption[] = [
|
|
794
|
+
{
|
|
795
|
+
name: "Menu item 1",
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
name: "Menu item 2",
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
name: "Menu item 3",
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
name: "Longer menu item than the others",
|
|
805
|
+
},
|
|
806
|
+
];
|
|
807
|
+
|
|
808
|
+
// Step 1/3: Copy existing base styles for the Popper component from
|
|
809
|
+
// sci-components/packages/components/src/core/DropdownMenu/style.ts, ~lines 206-229:
|
|
810
|
+
const getBorders = ({ theme }: CommonThemeProps): Borders | null => {
|
|
811
|
+
return theme?.app?.borders || null;
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
const getCorners = ({ theme }: CommonThemeProps): Corners | null => {
|
|
815
|
+
return theme?.app?.corners || null;
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
const getShadows = ({ theme }: CommonThemeProps): Shadows | null => {
|
|
819
|
+
return theme?.app?.shadows || null;
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
const getSpaces = ({ theme }: CommonThemeProps): Spaces | null => {
|
|
823
|
+
return theme?.app?.spacing || null;
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
const CustomPopper = styled(Popper)`
|
|
827
|
+
.MuiAutocomplete-popperDisablePortal {
|
|
828
|
+
position: relative;
|
|
829
|
+
width: 100% !important;
|
|
830
|
+
box-shadow: none;
|
|
831
|
+
padding: 0;
|
|
832
|
+
border: none;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
${(props) => {
|
|
836
|
+
const borders = getBorders(props);
|
|
837
|
+
const corners = getCorners(props);
|
|
838
|
+
const shadows = getShadows(props);
|
|
839
|
+
const spacings = getSpaces(props);
|
|
840
|
+
|
|
841
|
+
return `
|
|
842
|
+
background-color: white;
|
|
843
|
+
border: ${borders?.base?.divider};
|
|
844
|
+
border-radius: ${corners?.m}px;
|
|
845
|
+
box-shadow: ${shadows?.m};
|
|
846
|
+
padding: ${spacings?.xs}px;
|
|
847
|
+
box-sizing: border-box;
|
|
848
|
+
z-index: 1400;
|
|
849
|
+
`;
|
|
850
|
+
}}
|
|
851
|
+
|
|
852
|
+
border-color: purple;
|
|
853
|
+
border-style: dotted;
|
|
854
|
+
border-width: 15px !important;
|
|
855
|
+
`;
|
|
856
|
+
|
|
857
|
+
const POPPER_BASE_PROPS = { popperOptions: { strategy: "absolute" as const } };
|
|
858
|
+
|
|
859
|
+
const Anchor = styled("div")`
|
|
860
|
+
position: absolute;
|
|
861
|
+
top: 12px;
|
|
862
|
+
left: 20px;
|
|
863
|
+
`;
|
|
864
|
+
|
|
865
|
+
function App() {
|
|
866
|
+
const ref = useRef(null);
|
|
867
|
+
const [open, setOpen] = useState(false);
|
|
868
|
+
|
|
869
|
+
useEffect(() => {
|
|
870
|
+
if (ref.current) setOpen(true);
|
|
871
|
+
}, [ref.current]);
|
|
872
|
+
|
|
873
|
+
return (
|
|
874
|
+
<div className="app">
|
|
875
|
+
<Anchor ref={ref} />
|
|
876
|
+
{open && (
|
|
877
|
+
// 👇 Only pay attention to the props here, everything else is just glue to mount the example
|
|
878
|
+
<DropdownMenu
|
|
879
|
+
PopperBaseProps={POPPER_BASE_PROPS}
|
|
880
|
+
anchorEl={ref.current}
|
|
881
|
+
onClickAway={function handleClickAway() {}}
|
|
882
|
+
open
|
|
883
|
+
options={MENU_ITEMS}
|
|
884
|
+
// Step 3/3: Pass the new CustomPopper to PopperComponent prop
|
|
885
|
+
PopperComponent={CustomPopper}
|
|
886
|
+
/>
|
|
887
|
+
)}
|
|
888
|
+
</div>
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
export default App;
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
897
|
+
## SDS vs MUI
|
|
898
|
+
|
|
899
|
+
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:
|
|
900
|
+
|
|
901
|
+
- SDS DropdownMenu uses [MUI Autocomplete](https://mui.com/material-ui/react-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.
|
|
902
|
+
|
|
903
|
+
- SDS DropdownMenu uses [MUI Popper](https://mui.com/material-ui/react-popper/). A custom Popper component can be passed via `props.PopperComponent` and/or `props.PopperBaseProps` to control the popper component.
|
|
904
|
+
|
|
905
|
+
- Similarly, SDS DropdownMenu uses [MUI Paper](https://mui.com/material-ui/react-paper/). A custom Paper component can be passed via `props.PaperComponent` to enable custom styling.
|
|
906
|
+
|
|
907
|
+
## MUI Documentation
|
|
908
|
+
|
|
909
|
+
Documentation for the underlying MUI Autocomplete component can be found [here](https://mui.com/material-ui/react-autocomplete/).
|
|
910
|
+
|
|
911
|
+
## Props
|
|
912
|
+
|
|
913
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
914
|
+
|
|
915
|
+
| **Name** | **Type** | **Default** | **Description** |
|
|
916
|
+
| ------------------------ | ------------------------------------ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
917
|
+
| `anchorEl` | `HTMLElement` \| `null` | - | Required. The element the menu is positioned against, usually the control that opens it. Until it is set the menu renders an empty container: the list is only mounted once there is something to anchor it to. See the [MUI Popper API](https://mui.com/material-ui/api/popper/). |
|
|
918
|
+
| `open` | `boolean` | `false` | Whether the menu is showing. The menu holds no open state of its own, so this and `anchorEl` are what the opening control sets. |
|
|
919
|
+
| `options` | `T[]` | - | Required. One object per menu item, each needing at least a `name`. An option that itself has an `options` array turns the menu into columns, which changes how it is sized. |
|
|
920
|
+
| `children` | `JSX.Element` \| `null` | - | Rendered inside the menu below the list, for footers such as an apply button. |
|
|
921
|
+
| `ClickAwayListenerProps` | `Partial<ClickAwayListenerProps>` | - | Passes props to the [MUI ClickAwayListener](https://mui.com/base-ui/react-click-away-listener/components-api/). |
|
|
922
|
+
| `groupBy` | `function` | - | Groups menu items according to their assigned section. |
|
|
923
|
+
| `headerComponentSlot` | `JSX.Element` | - | Rendered in the menu's header, below `title`. Either one is enough to give the menu a header. |
|
|
924
|
+
| `InputBaseProps` | `Partial<InputSearchProps>` | - | Passed to the InputSearch the `search` prop renders. The menu sets `autoFocus` from `isSearchAutoFocus` and `onClick` to a no-op after these, so those two are the menu's to decide. |
|
|
925
|
+
| `isOptionEqualToValue` | `function` | `(option, val) => option.name === val.name` | Changes how a selected value is compared against the available options; see more in the [MUI Autocomplete API](https://mui.com/material-ui/api/autocomplete/#props) or in the Recipes section. |
|
|
926
|
+
| `isSearchAutoFocus` | `boolean` | `true` | Puts the cursor in the search input as the menu opens. It overrides any `autoFocus` given through `InputBaseProps`. |
|
|
927
|
+
| `keepSearchOnSelect` | `boolean` | `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). |
|
|
928
|
+
| `label` | `string` | `"Search"` | The placeholder text of the search input. |
|
|
929
|
+
| `multiple` | `boolean` | `false` | Allows user to select multiple menu items; see more in the [MUI Autocomplete API](https://mui.com/material-ui/api/autocomplete/#props). |
|
|
930
|
+
| `noOptionsText` | `string` | `"No options"` | Sets the text that displays if there are no matching search results; see more in the [MUI Autocomplete API](https://mui.com/material-ui/api/autocomplete/#props). |
|
|
931
|
+
| `onClickAway` | `function` | `noop` | Sets what happens when a user clicks outside of the DropdownMenu. This is where a menu is normally closed. **Signature:** `function(event: MouseEvent \| TouchEvent) => void` |
|
|
932
|
+
| `onClose` | `function` | - | Called when the list asks to close, such as on selecting an option in a single-select menu. Closing is still yours to carry out through `open`. **Signature:** `function(event: SyntheticEvent, reason: AutocompleteCloseReason) => void` |
|
|
933
|
+
| `onInputChange` | `function` | - | Called as the search text changes; see more in the [MUI Autocomplete API](https://mui.com/material-ui/api/autocomplete/#props) or in the Recipes section. **Signature:** `function(event: SyntheticEvent, value: string, reason: AutocompleteInputChangeReason) => void` |
|
|
934
|
+
| `onOpen` | `function` | - | Called when the list asks to open. **Signature:** `function(event: SyntheticEvent) => void` |
|
|
935
|
+
| `PaperComponent` | `JSXElementConstructor<PaperProps>` | SDS's `StyledPaper` | The surface the menu is drawn on. Replace it to restyle the menu's background, border and shadow. |
|
|
936
|
+
| `PopperBaseProps` | `Partial<PopperProps>` | `{}` | Passes custom props to the MUI Popper the menu is placed with. Learn more in the [Popper documentation](https://mui.com/material-ui/react-popper/), and see all the props available to pass to it on the [Popper API page](https://mui.com/material-ui/api/popper/). See an example of `PopperBaseProps` in use in the Recipes section. Its `sx` is merged over the menu's own sizing, so this is also how the 160px minimum width is lifted. |
|
|
937
|
+
| `PopperComponent` | `JSXElementConstructor<PopperProps>` | SDS's `StyledPopper` | The [MUI Popper component](https://mui.com/material-ui/react-popper/) is included as part of the SDS DropdownMenu component, and its styles can be changed or added to. |
|
|
938
|
+
| `PopperPlacement` | `PopperProps["placement"]` | `"bottom-start"` | Where the menu sits relative to its anchor, as MUI's Popper understands it: one of `"auto"`, `"top"`, `"bottom"`, `"left"`, `"right"`, each also available with a `-start` or `-end` suffix. |
|
|
939
|
+
| `renderInput` | `function` | - | Replaces the search input entirely. Most menus want `InputBaseProps` instead, which keeps the SDS input. **Signature:** `function(params: AutocompleteRenderInputParams) => ReactNode` |
|
|
940
|
+
| `search` | `boolean` | `false` | Provides a search bar at the top of the menu from which users can narrow the list of menu items. |
|
|
941
|
+
| `subTitle` | `ReactNode` | - | A second line below the header's title. It is only rendered when `title` is set as well. |
|
|
942
|
+
| `title` | `ReactNode` | - | Sets a heading for the menu. |
|
|
943
|
+
| `width` | `number` \| `string` | `160` | The width of the menu, as a number of pixels or any CSS width. The 160px minimum still applies underneath it, and a menu whose options are laid out in columns ignores this and sizes itself to its content. |
|
|
944
|
+
|
|
945
|
+
There are many more props that can be used with the DropdownMenu component, via those available to [MUI's Autocomplete component](https://mui.com/material-ui/api/autocomplete/#props).
|
|
946
|
+
|
|
947
|
+
> **Note for use with SDS** **Table** **and other components:**
|
|
948
|
+
>
|
|
949
|
+
> 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.
|