@sproutsocial/seeds-react-menu 0.4.1 → 0.6.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 (105) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +32 -0
  3. package/dist/esm/index.js +1553 -423
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.mts +303 -122
  6. package/dist/index.d.ts +303 -122
  7. package/dist/index.js +1561 -405
  8. package/dist/index.js.map +1 -1
  9. package/package.json +4 -1
  10. package/src/ActionMenu/ActionMenu.stories.tsx +426 -0
  11. package/src/ActionMenu/ActionMenu.tsx +30 -26
  12. package/src/ActionMenu/ActionMenuTypes.ts +13 -8
  13. package/src/ActionMenu/__tests__/ActionMenu.test.tsx +17 -30
  14. package/src/ActionMenu/__tests__/ActionMenu.typetest.tsx +32 -3
  15. package/src/Autocomplete/Autocomplete.stories.tsx +61 -2
  16. package/src/Autocomplete/Autocomplete.tsx +15 -74
  17. package/src/Autocomplete/AutocompleteInput.tsx +18 -27
  18. package/src/Autocomplete/AutocompleteTokenInput.tsx +50 -0
  19. package/src/Autocomplete/AutocompleteTypes.ts +27 -11
  20. package/src/Autocomplete/MultiAutocomplete.tsx +189 -0
  21. package/src/Autocomplete/SingleAutocomplete.tsx +74 -0
  22. package/src/Autocomplete/__tests__/Autocomplete.test.tsx +171 -3
  23. package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +116 -3
  24. package/src/MenuContent/MenuContent.stories.tsx +164 -0
  25. package/src/MenuContent/MenuContent.tsx +35 -8
  26. package/src/MenuContent/MenuContentTypes.ts +7 -5
  27. package/src/MenuContent/__tests__/MenuContent.typetest.tsx +18 -2
  28. package/src/MenuContext.tsx +191 -37
  29. package/src/MenuFooter/MenuFooter.stories.tsx +202 -0
  30. package/src/MenuFooter/__tests__/MenuFooter.test.tsx +16 -11
  31. package/src/MenuGroup/MenuGroup.feature +21 -21
  32. package/src/MenuGroup/MenuGroup.stories.tsx +248 -0
  33. package/src/MenuGroup/MenuGroup.tsx +9 -7
  34. package/src/MenuGroup/MenuGroupTypes.ts +8 -6
  35. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +6 -6
  36. package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +23 -4
  37. package/src/MenuGroup/styles.tsx +4 -2
  38. package/src/MenuHeader/MenuHeader.stories.tsx +276 -0
  39. package/src/MenuHeader/MenuHeader.tsx +3 -1
  40. package/src/MenuHeader/__tests__/MenuHeader.test.tsx +23 -13
  41. package/src/MenuHeader/styles.tsx +18 -14
  42. package/src/MenuItem/MenuItem.stories.tsx +507 -0
  43. package/src/MenuItem/MenuItem.tsx +18 -9
  44. package/src/MenuItem/MenuItemTypes.ts +9 -4
  45. package/src/MenuItem/__tests__/MenuItem.test.tsx +6 -6
  46. package/src/MenuItem/styles.tsx +21 -5
  47. package/src/MenuItem/useOptionProps.tsx +10 -8
  48. package/src/MenuLabel.tsx +2 -2
  49. package/src/MenuRoot/MenuRoot.tsx +37 -23
  50. package/src/MenuRoot/__tests__/MenuRoot.test.tsx +59 -32
  51. package/src/MenuSearchInput/MenuSearchInput.tsx +63 -0
  52. package/src/MenuSearchInput/__tests__/MenuSearchInput.test.tsx +157 -0
  53. package/src/MenuSearchInput/__tests__/MenuSearchInput.typetest.tsx +31 -0
  54. package/src/MenuSearchInput/index.ts +1 -0
  55. package/src/MenuSearchTokenInput/MenuSearchTokenInput.tsx +98 -0
  56. package/src/MenuSearchTokenInput/__tests__/MenuSearchTokenInput.test.tsx +247 -0
  57. package/src/MenuSearchTokenInput/__tests__/MenuSearchTokenInput.typetest.tsx +33 -0
  58. package/src/MenuSearchTokenInput/index.ts +1 -0
  59. package/src/MenuToggleButton/MenuToggleButton.stories.tsx +72 -0
  60. package/src/{MenuToggleButton.tsx → MenuToggleButton/MenuToggleButton.tsx} +6 -5
  61. package/src/MenuToggleButton/index.ts +1 -0
  62. package/src/MenuTokenInput.tsx +104 -0
  63. package/src/MultiSelectMenu/MultiSelectMenu.stories.tsx +214 -0
  64. package/src/MultiSelectMenu/MultiSelectMenu.tsx +44 -67
  65. package/src/MultiSelectMenu/MultiSelectMenuTypes.ts +9 -5
  66. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +46 -60
  67. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.typetest.tsx +29 -2
  68. package/src/NestedMenu/NestedMenu.feature +109 -0
  69. package/src/NestedMenu/NestedMenu.stories.tsx +157 -0
  70. package/src/NestedMenu/NestedMenu.tsx +102 -0
  71. package/src/NestedMenu/NestedMenuContent.tsx +71 -0
  72. package/src/NestedMenu/NestedMenuContext.tsx +56 -0
  73. package/src/NestedMenu/NestedMenuHeader.tsx +25 -0
  74. package/src/NestedMenu/NestedMenuItem.tsx +37 -0
  75. package/src/NestedMenu/NestedMenuTypes.ts +47 -0
  76. package/src/NestedMenu/__tests__/NestedMenu.test.tsx +31 -0
  77. package/src/NestedMenu/__tests__/NestedMenu.typetest.tsx +134 -0
  78. package/src/NestedMenu/index.ts +5 -0
  79. package/src/SingleSelectMenu/SingleSelectMenu.stories.tsx +227 -0
  80. package/src/SingleSelectMenu/SingleSelectMenu.tsx +29 -25
  81. package/src/SingleSelectMenu/SingleSelectMenuTypes.ts +9 -5
  82. package/src/SingleSelectMenu/__tests__/SingleSelectMenu.typetest.tsx +32 -2
  83. package/src/SubmenuItem/SubmenuItem.feature +82 -0
  84. package/src/SubmenuItem/SubmenuItem.stories.tsx +163 -0
  85. package/src/SubmenuItem/SubmenuItem.tsx +254 -0
  86. package/src/SubmenuItem/SubmenuItemTypes.ts +24 -0
  87. package/src/SubmenuItem/__tests__/SubmenuItem.test.tsx +3 -0
  88. package/src/SubmenuItem/__tests__/SubmenuItem.typetest.tsx +112 -0
  89. package/src/SubmenuItem/index.ts +2 -0
  90. package/src/__tests__/Menu.test.tsx +38 -0
  91. package/src/hooks/useDownshiftDefaults.tsx +107 -0
  92. package/src/hooks/useItemFiltering.tsx +66 -0
  93. package/src/hooks/useMenuChildren.tsx +279 -0
  94. package/src/index.ts +7 -1
  95. package/src/menuTestingUtils.tsx +21 -10
  96. package/src/reducers/nestedMenuStateReducer.tsx +23 -0
  97. package/src/reducers/useComboboxStateReducer.tsx +26 -8
  98. package/src/reducers/useSelectStateReducer.tsx +24 -6
  99. package/src/storybookUtilities/menu-storybook-components.tsx +5 -3
  100. package/src/types.ts +38 -5
  101. package/src/utils/downshiftDefaults.ts +9 -0
  102. package/src/utils/getMultiSelectOverrides.ts +21 -0
  103. package/src/utils/reduceReducers.ts +19 -0
  104. package/src/hooks/useItemsFromChildren.tsx +0 -131
  105. package/src/utils/itemToString.ts +0 -5
@@ -30,8 +30,10 @@ const MenuHeader = ({
30
30
  {title && (
31
31
  <MenuTitleText $leftAction={!!leftAction}>{title}</MenuTitleText>
32
32
  )}
33
- {rightAction && rightAction}
33
+
34
+ {rightAction && <div className="right-action">{rightAction}</div>}
34
35
  </MenuHeaderFeatures>
36
+
35
37
  {children && <MenuChildrenContainer>{children}</MenuChildrenContainer>}
36
38
  </MenuHeaderContainer>
37
39
  </>
@@ -1,8 +1,14 @@
1
- import { render, screen } from "@sproutsocial/seeds-react-testing-library";
2
- import { MenuHeader } from "../MenuHeader";
3
- import { MenuRoot } from "../../MenuRoot";
1
+ import {
2
+ render,
3
+ screen,
4
+ actRender,
5
+ act,
6
+ } from "@sproutsocial/seeds-react-testing-library";
4
7
  import Button from "@sproutsocial/seeds-react-button";
5
8
  import Text from "@sproutsocial/seeds-react-text";
9
+ import { MenuHeader } from "../MenuHeader";
10
+ import { MenuToggleButton } from "../../index";
11
+ import { TestWithMenuRoot } from "../../menuTestingUtils";
6
12
 
7
13
  describe("MenuHeader API", () => {
8
14
  it("renders a title when title prop is passed a string", () => {
@@ -49,17 +55,17 @@ describe("MenuHeader API", () => {
49
55
 
50
56
  it.each(contextTests)(
51
57
  "$children rendered when $children provided in MenuRoot",
52
- ({ TestComponent, test }) => {
53
- render(
54
- <MenuRoot
58
+ async ({ TestComponent, test }) => {
59
+ await actRender(
60
+ <TestWithMenuRoot
55
61
  isOpen
56
- menuToggleElement={<Button onClick={jest.fn()}>Open</Button>}
62
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
57
63
  >
58
64
  <MenuHeader>
59
65
  Hello world!
60
66
  {TestComponent}
61
67
  </MenuHeader>
62
- </MenuRoot>
68
+ </TestWithMenuRoot>
63
69
  );
64
70
 
65
71
  test();
@@ -69,7 +75,7 @@ describe("MenuHeader API", () => {
69
75
  it("supports clickable actions", async () => {
70
76
  const handleTestClick = jest.fn();
71
77
 
72
- const { user } = render(
78
+ const { user } = await actRender(
73
79
  <MenuHeader
74
80
  leftAction={<Button onClick={handleTestClick}>Left Action</Button>}
75
81
  rightAction={<Button onClick={handleTestClick}>Right Action</Button>}
@@ -81,17 +87,21 @@ describe("MenuHeader API", () => {
81
87
  const leftAction = screen.getByText("Left Action");
82
88
  const rightAction = screen.getByText("Right Action");
83
89
 
84
- await user.click(leftAction);
90
+ await act(async () => {
91
+ await user.click(leftAction);
92
+ });
85
93
 
86
94
  expect(handleTestClick).toHaveBeenCalledTimes(1);
87
95
 
88
- await user.click(rightAction);
96
+ await act(async () => {
97
+ await user.click(rightAction);
98
+ });
89
99
 
90
100
  expect(handleTestClick).toHaveBeenCalledTimes(2);
91
101
  });
92
102
 
93
- it("can render actions on the left or right of the title", () => {
94
- render(
103
+ it("can render actions on the left or right of the title", async () => {
104
+ await actRender(
95
105
  <MenuHeader
96
106
  leftAction={<Button onClick={() => {}}>Left Action</Button>}
97
107
  rightAction={<Button onClick={() => {}}>Right Action</Button>}
@@ -38,16 +38,15 @@ export const MenuTitleText = styled.h3<TypeMenuHeaderContainerProps>`
38
38
  color: ${({ theme }) => theme.colors.text.headline};
39
39
  font-weight: ${({ theme }) => theme.fontWeights.bold};
40
40
  margin: 0px;
41
- flex-grow: 2;
42
- justify-self: start;
41
+ flex-grow: 1;
43
42
 
44
43
  ${({ theme }) => theme.typography[200]}
45
44
 
46
45
  ${({ $leftAction, theme }) =>
47
46
  $leftAction &&
48
47
  `
49
- padding-left: ${theme.space[350]};
50
- `}
48
+ padding-left: ${theme.space[350]};
49
+ `}
51
50
  `;
52
51
 
53
52
  export const MenuChildrenContainer = styled.div`
@@ -57,18 +56,23 @@ export const MenuChildrenContainer = styled.div`
57
56
 
58
57
  export const MenuHeaderFeatures = styled.div<TypeMenuHeaderContainerProps>`
59
58
  display: flex;
59
+ align-items: center;
60
+ justify-content: space-between;
61
+ width: 100%;
60
62
 
61
- > a {
62
- white-space: nowrap;
63
- }
64
-
65
- &:first-child {
66
- flex-grow: 1;
67
- justify-self: start;
63
+ .right-action {
64
+ margin-left: auto; /* Pushes the right action to the right */
65
+ display: flex;
66
+ justify-content: flex-end;
67
+ flex-shrink: 0; /* Prevents it from shrinking */
68
+ max-width: 40%; /* Prevents it from taking too much space */
69
+ text-align: right;
68
70
  }
69
71
 
70
- &:last-child {
71
- flex-grow: 1;
72
- justify-self: end;
72
+ > a,
73
+ > button {
74
+ display: flex;
75
+ align-items: center;
76
+ word-break: break-word;
73
77
  }
74
78
  `;
@@ -0,0 +1,507 @@
1
+ import { useState } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { action } from "@storybook/addon-actions";
4
+
5
+ import { Box } from "@sproutsocial/seeds-react-box";
6
+ import { Switch } from "@sproutsocial/seeds-react-switch";
7
+ import { Icon } from "@sproutsocial/seeds-react-icon";
8
+ import { Badge } from "@sproutsocial/seeds-react-badge";
9
+ import { Tooltip } from "@sproutsocial/seeds-react-tooltip";
10
+ import { VisuallyHidden } from "@sproutsocial/seeds-react-visually-hidden";
11
+
12
+ import { StorybookMenuToggleButton } from "../storybookUtilities/menu-storybook-components";
13
+ import { TEST_BOOKS } from "../__fixtures__/menu-test-data";
14
+ import {
15
+ MenuItem,
16
+ MenuLabel,
17
+ MenuToggleButton,
18
+ MenuHeader,
19
+ MenuContent,
20
+ ActionMenu,
21
+ SingleSelectMenu,
22
+ MultiSelectMenu,
23
+ MenuContentContext,
24
+ MenuGroup,
25
+ useMenu,
26
+ type TypeMenuItemProps,
27
+ } from "../index";
28
+
29
+ interface MenuItemStoryArgs extends TypeMenuItemProps {
30
+ "Include Switch Children"?: boolean;
31
+ text?: string;
32
+ }
33
+
34
+ type Story = StoryObj<MenuItemStoryArgs>;
35
+
36
+ const meta: Meta<MenuItemStoryArgs> = {
37
+ title: "Under Development/Primitives/MenuItem",
38
+ component: MenuItem,
39
+ decorators: [
40
+ (Story) => (
41
+ <Box display="flex" flexDirection="column" gap="16px" maxWidth={300}>
42
+ <Story />
43
+ </Box>
44
+ ),
45
+ ],
46
+ argTypes: {
47
+ disabled: {
48
+ table: {
49
+ category: "MenuItem",
50
+ },
51
+ description: "disabled state",
52
+ control: { type: "boolean" },
53
+ },
54
+ "Include Switch Children": {
55
+ table: {
56
+ category: "MenuItem",
57
+ },
58
+ description: "Example Switch Child",
59
+ control: { type: "boolean" },
60
+ },
61
+ alignItems: {
62
+ table: {
63
+ category: "MenuItem",
64
+ },
65
+ description: "systemProps",
66
+ control: { type: "text" },
67
+ },
68
+ justifyContent: {
69
+ table: {
70
+ category: "MenuItem",
71
+ },
72
+ description: "systemProps",
73
+ control: { type: "text" },
74
+ },
75
+ display: {
76
+ table: {
77
+ category: "MenuItem",
78
+ },
79
+ description: "systemProps",
80
+ control: { type: "text" },
81
+ },
82
+ inputType: {
83
+ table: {
84
+ category: "MenuItem",
85
+ },
86
+ options: ["checkbox", "switch", "radio", undefined],
87
+ control: { type: "select" },
88
+ },
89
+ href: {
90
+ table: {
91
+ category: "MenuItem",
92
+ },
93
+ description: "href (makes an anchor tag)",
94
+ control: { type: "text" },
95
+ },
96
+ },
97
+ args: {
98
+ disabled: false,
99
+ display: "flex",
100
+ alignItems: "center",
101
+ justifyContent: "start",
102
+ "Include Switch Children": false,
103
+ inputType: undefined,
104
+ href: undefined,
105
+ },
106
+ };
107
+
108
+ export default meta;
109
+
110
+ /**
111
+ * This custom onClick handler is used to log the event and the item name to the Action pane inside the Storybook UI.
112
+ */
113
+ const handleOnClick = (e: React.MouseEvent<HTMLElement>, itemName: string) => {
114
+ action(itemName)(e);
115
+ };
116
+
117
+ /**
118
+ * Similar to how we stub events in SelectableMenuItem, the best way to implement the Switch in MenuItem is to stop the event from bubbling
119
+ * and allowing Downshift to handle it.
120
+ */
121
+ const switchInputStub = (e: React.SyntheticEvent) => {
122
+ e.stopPropagation();
123
+ };
124
+
125
+ /**
126
+ * Story: Story for experimenting with different states of disabled and "switched" behavior with an action menu
127
+ */
128
+ export const MenuWithItems: Story = {
129
+ render: function MenuWithItems(args) {
130
+ const [checked, setChecked] = useState({
131
+ compose: false,
132
+ view: false,
133
+ analyze: false,
134
+ });
135
+
136
+ const handleOnClick = (
137
+ e: React.MouseEvent<HTMLElement>,
138
+ itemName: keyof typeof checked
139
+ ) => {
140
+ action(itemName)(e);
141
+ setChecked((prev) => ({
142
+ ...prev,
143
+ [itemName]: !prev[itemName],
144
+ }));
145
+ };
146
+
147
+ return (
148
+ <ActionMenu
149
+ menuToggleElement={
150
+ <>
151
+ <VisuallyHidden>
152
+ <MenuLabel>Take an Action: </MenuLabel>
153
+ </VisuallyHidden>
154
+ <MenuToggleButton ml={10} appearance="secondary">
155
+ Launch Menu
156
+ </MenuToggleButton>
157
+ </>
158
+ }
159
+ >
160
+ <MenuHeader title="Sprout Menu" />
161
+ <MenuContent>
162
+ <MenuGroup id="group-1">
163
+ <MenuItem
164
+ onClick={(e) => handleOnClick(e, "compose")}
165
+ key={0}
166
+ id="compose"
167
+ disabled={args.disabled}
168
+ display={args.display}
169
+ alignItems={args.alignItems}
170
+ justifyContent={args.justifyContent}
171
+ >
172
+ {args["Include Switch Children"] && (
173
+ <Switch
174
+ onClick={switchInputStub}
175
+ checked={checked["compose"]}
176
+ />
177
+ )}
178
+ Compose Post
179
+ </MenuItem>
180
+ <MenuItem
181
+ onClick={(e) => handleOnClick(e, "view")}
182
+ key={1}
183
+ id="view"
184
+ disabled={args.disabled}
185
+ display={args.display}
186
+ alignItems={args.alignItems}
187
+ justifyContent={args.justifyContent}
188
+ >
189
+ {args["Include Switch Children"] && (
190
+ <Switch onClick={switchInputStub} checked={checked["view"]} />
191
+ )}
192
+ View Messages
193
+ </MenuItem>
194
+ <MenuItem
195
+ onClick={(e) => handleOnClick(e, "analyze")}
196
+ key={2}
197
+ id="analyze"
198
+ disabled={args.disabled}
199
+ display={args.display}
200
+ alignItems={args.alignItems}
201
+ justifyContent={args.justifyContent}
202
+ >
203
+ {args["Include Switch Children"] && (
204
+ <Switch
205
+ onClick={switchInputStub}
206
+ checked={checked["analyze"]}
207
+ />
208
+ )}
209
+ Analyze Post
210
+ </MenuItem>
211
+ </MenuGroup>
212
+ </MenuContent>
213
+ </ActionMenu>
214
+ );
215
+ },
216
+ };
217
+
218
+ /**
219
+ * Story: Story for experimenting with different visual children
220
+ */
221
+ export const MenuWithIcons: Story = {
222
+ parameters: {
223
+ controls: { exclude: ["Include Switch Children"] },
224
+ },
225
+ args: {
226
+ justifyContent: "space-between",
227
+ },
228
+ render: function MenuWithIcons(args) {
229
+ return (
230
+ <ActionMenu
231
+ menuToggleElement={
232
+ <>
233
+ <VisuallyHidden>
234
+ <MenuLabel>Take an Action: </MenuLabel>
235
+ </VisuallyHidden>
236
+ <MenuToggleButton ml={10} appearance="secondary">
237
+ Launch Menu
238
+ </MenuToggleButton>
239
+ </>
240
+ }
241
+ >
242
+ <MenuHeader title="Sprout Menu" />
243
+ <MenuContent>
244
+ <MenuGroup id="group-1">
245
+ <MenuItem
246
+ onClick={(e) => handleOnClick(e, "settings")}
247
+ key={0}
248
+ id="settings"
249
+ disabled={args.disabled}
250
+ display={args.display}
251
+ alignItems={args.alignItems}
252
+ justifyContent={args.justifyContent}
253
+ >
254
+ <span>
255
+ <Icon name="gear" mr={350} />
256
+ Settings
257
+ </span>
258
+ <Badge
259
+ type="secondary"
260
+ badgeColor="red"
261
+ iconName="circle-exclamation"
262
+ >
263
+ Needs Attention
264
+ </Badge>
265
+ </MenuItem>
266
+ <MenuItem
267
+ onClick={(e) => handleOnClick(e, "messages")}
268
+ key={1}
269
+ id="messages"
270
+ disabled={args.disabled}
271
+ display={args.display}
272
+ alignItems={args.alignItems}
273
+ justifyContent={args.justifyContent}
274
+ >
275
+ <span>
276
+ <Icon name="envelope" mr={350} />
277
+ Messages
278
+ </span>
279
+ <Tooltip content="1 Message">
280
+ <Icon name="triangle-exclamation" />
281
+ </Tooltip>
282
+ </MenuItem>
283
+ <MenuItem
284
+ onClick={(e) => handleOnClick(e, "analytics")}
285
+ key={2}
286
+ id="analytics"
287
+ disabled={args.disabled}
288
+ display={args.display}
289
+ alignItems={args.alignItems}
290
+ justifyContent={args.justifyContent}
291
+ >
292
+ <span>
293
+ <Icon name="chart-arrow-up" mr={350} />
294
+ Analytics
295
+ </span>
296
+ <Icon name="link" />
297
+ </MenuItem>
298
+ </MenuGroup>
299
+ </MenuContent>
300
+ </ActionMenu>
301
+ );
302
+ },
303
+ };
304
+
305
+ export const MenuItemsInListbox: Story = {
306
+ name: "Menu Items in Listbox",
307
+ decorators: [
308
+ (Story) => (
309
+ <Box display="flex" flexDirection="column" gap="16px" maxWidth={300}>
310
+ <Story />
311
+ </Box>
312
+ ),
313
+ ],
314
+ render: function MenuItemsInListbox(args) {
315
+ return (
316
+ <SingleSelectMenu
317
+ defaultIsOpen
318
+ initialIsOpen={false}
319
+ menuToggleElement={<StorybookMenuToggleButton />}
320
+ >
321
+ <MenuContent>
322
+ <MenuGroup id="books">
323
+ {TEST_BOOKS.map((book) => {
324
+ return (
325
+ <MenuItem
326
+ {...args}
327
+ key={book.id}
328
+ id={book.id}
329
+ inputType={args.inputType}
330
+ disabled={args.disabled}
331
+ display={args.display}
332
+ alignItems={args.alignItems}
333
+ justifyContent={args.justifyContent}
334
+ >
335
+ {book.title}
336
+ </MenuItem>
337
+ );
338
+ })}
339
+ </MenuGroup>
340
+ </MenuContent>
341
+ </SingleSelectMenu>
342
+ );
343
+ },
344
+ };
345
+
346
+ export const MenuItemsWithInputTypes: Story = {
347
+ name: "Menu Items With Input Types",
348
+ decorators: [
349
+ (Story) => (
350
+ <Box
351
+ display="flex"
352
+ justifyContent="space-between"
353
+ flexDirection="row"
354
+ width="100vw"
355
+ gap={300}
356
+ >
357
+ <Story />
358
+ </Box>
359
+ ),
360
+ ],
361
+ render: function MenuItemsWithInputTypes() {
362
+ return (
363
+ <>
364
+ <Box width="280px" mr={300}>
365
+ <SingleSelectMenu
366
+ isOpen
367
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
368
+ popoutProps={{ placement: "bottom-start" }}
369
+ >
370
+ <MenuContent>
371
+ <MenuGroup id="books">
372
+ {TEST_BOOKS.map((book) => {
373
+ return (
374
+ <MenuItem key={book.id} id={book.id}>
375
+ {book.title}
376
+ </MenuItem>
377
+ );
378
+ })}
379
+ </MenuGroup>
380
+ </MenuContent>
381
+ </SingleSelectMenu>
382
+ </Box>
383
+ <Box width="280px" mr={300}>
384
+ <MultiSelectMenu
385
+ isOpen
386
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
387
+ popoutProps={{ placement: "bottom-start" }}
388
+ >
389
+ <MenuContent>
390
+ <MenuGroup id="books">
391
+ {TEST_BOOKS.map((book) => {
392
+ return (
393
+ <MenuItem key={book.id} id={book.id} inputType="checkbox">
394
+ {book.title}
395
+ </MenuItem>
396
+ );
397
+ })}
398
+ </MenuGroup>
399
+ </MenuContent>
400
+ </MultiSelectMenu>
401
+ </Box>
402
+ <Box width="280px" mr={300}>
403
+ <SingleSelectMenu
404
+ isOpen
405
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
406
+ popoutProps={{ placement: "bottom-start" }}
407
+ >
408
+ <MenuContent>
409
+ <MenuGroup id="books">
410
+ {TEST_BOOKS.map((book) => {
411
+ return (
412
+ <MenuItem
413
+ key={book.id}
414
+ id={book.id}
415
+ inputType="radio"
416
+ inputLabelId={"test-label-" + book.id}
417
+ >
418
+ <label id={"test-label-" + book.id}></label>
419
+ {book.title}
420
+ </MenuItem>
421
+ );
422
+ })}
423
+ </MenuGroup>
424
+ </MenuContent>
425
+ </SingleSelectMenu>
426
+ </Box>
427
+ <Box width="280px" mr={300}>
428
+ <MultiSelectMenu
429
+ isOpen
430
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
431
+ popoutProps={{ placement: "bottom-start" }}
432
+ >
433
+ <MenuContent>
434
+ <MenuGroup id="books">
435
+ {TEST_BOOKS.map((book) => {
436
+ return (
437
+ <MenuItem key={book.id} id={book.id} inputType="switch">
438
+ {book.title}
439
+ </MenuItem>
440
+ );
441
+ })}
442
+ </MenuGroup>
443
+ </MenuContent>
444
+ </MultiSelectMenu>
445
+ </Box>
446
+ </>
447
+ );
448
+ },
449
+ };
450
+
451
+ /**
452
+ * Story: Story for experimenting with different states of disabled and "switched" behavior with an action menu
453
+ */
454
+ export const MenuItemOnly: Story = {
455
+ args: {
456
+ text: "Click Me",
457
+ },
458
+ parameters: {
459
+ controls: { exclude: ["Include Switch Children"] },
460
+ },
461
+ render: function MenuItemOnly(args) {
462
+ const handleOnClick = (e: React.MouseEvent<HTMLElement>, itemName) => {
463
+ action(itemName)(e);
464
+ };
465
+
466
+ return (
467
+ <MenuItem
468
+ {...args}
469
+ onClick={(e) => handleOnClick(e, "compose")}
470
+ key={0}
471
+ id="compose"
472
+ >
473
+ {args.text}
474
+ </MenuItem>
475
+ );
476
+ },
477
+ };
478
+
479
+ /**
480
+ * Story: Story for experimenting with the selectable menu item
481
+ */
482
+ export const ListboxMenuItemOnly: Story = {
483
+ name: "Selectable Menu Item Only",
484
+ render: function MenuItemSelectableOnly(args) {
485
+ const item = {
486
+ id: "any",
487
+ children: "Select Me!",
488
+ index: 0,
489
+ };
490
+ const menuContext = useMenu({
491
+ items: [item],
492
+ itemsMap: { item },
493
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
494
+ // @ts-expect-error - mocked function does not match real type
495
+ getItemProps: (props) => ({ role: "option", ...props }),
496
+ isListbox: true,
497
+ // ...context,
498
+ });
499
+ return (
500
+ <MenuContentContext.Provider value={menuContext}>
501
+ <MenuItem {...args} key="any" id="any">
502
+ Select Me!
503
+ </MenuItem>
504
+ </MenuContentContext.Provider>
505
+ );
506
+ },
507
+ };