@sproutsocial/seeds-react-menu 0.5.0 → 1.0.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 (104) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +72 -0
  3. package/dist/esm/index.js +1529 -440
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.mts +275 -92
  6. package/dist/index.d.ts +275 -92
  7. package/dist/index.js +1558 -444
  8. package/dist/index.js.map +1 -1
  9. package/package.json +15 -12
  10. package/src/ActionMenu/ActionMenu.stories.tsx +422 -0
  11. package/src/ActionMenu/ActionMenu.tsx +24 -27
  12. package/src/ActionMenu/ActionMenuTypes.ts +8 -7
  13. package/src/ActionMenu/__tests__/ActionMenu.test.tsx +17 -8
  14. package/src/ActionMenu/__tests__/ActionMenu.typetest.tsx +8 -8
  15. package/src/Autocomplete/Autocomplete.stories.tsx +38 -3
  16. package/src/Autocomplete/Autocomplete.tsx +15 -80
  17. package/src/Autocomplete/AutocompleteInput.tsx +18 -27
  18. package/src/Autocomplete/AutocompleteTokenInput.tsx +50 -0
  19. package/src/Autocomplete/AutocompleteTypes.ts +25 -12
  20. package/src/Autocomplete/MultiAutocomplete.tsx +188 -0
  21. package/src/Autocomplete/SingleAutocomplete.tsx +74 -0
  22. package/src/Autocomplete/__tests__/Autocomplete.test.tsx +207 -3
  23. package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +90 -8
  24. package/src/MenuContent/MenuContent.stories.tsx +164 -0
  25. package/src/MenuContent/MenuContent.tsx +28 -12
  26. package/src/MenuContent/MenuContentTypes.ts +6 -4
  27. package/src/MenuContent/__tests__/MenuContent.typetest.tsx +8 -8
  28. package/src/MenuContext.tsx +192 -37
  29. package/src/MenuFooter/MenuFooter.stories.tsx +202 -0
  30. package/src/MenuFooter/__tests__/MenuFooter.test.tsx +16 -11
  31. package/src/MenuFooter/styles.tsx +1 -1
  32. package/src/MenuGroup/MenuGroup.feature +16 -16
  33. package/src/MenuGroup/MenuGroup.stories.tsx +248 -0
  34. package/src/MenuGroup/MenuGroup.tsx +12 -7
  35. package/src/MenuGroup/MenuGroupTypes.ts +6 -2
  36. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +6 -6
  37. package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +7 -7
  38. package/src/MenuGroup/styles.tsx +4 -2
  39. package/src/MenuHeader/MenuHeader.stories.tsx +276 -0
  40. package/src/MenuHeader/MenuHeader.tsx +12 -7
  41. package/src/MenuHeader/__tests__/MenuHeader.test.tsx +23 -13
  42. package/src/MenuHeader/styles.tsx +22 -14
  43. package/src/MenuItem/MenuItem.stories.tsx +507 -0
  44. package/src/MenuItem/MenuItem.tsx +23 -9
  45. package/src/MenuItem/MenuItemTypes.ts +9 -4
  46. package/src/MenuItem/__tests__/MenuItem.test.tsx +6 -6
  47. package/src/MenuItem/styles.tsx +23 -5
  48. package/src/MenuItem/useOptionProps.tsx +11 -9
  49. package/src/MenuLabel.tsx +2 -2
  50. package/src/MenuRoot/MenuRoot.tsx +52 -24
  51. package/src/MenuRoot/__tests__/MenuRoot.test.tsx +59 -32
  52. package/src/MenuSearchInput/MenuSearchInput.tsx +85 -0
  53. package/src/MenuSearchInput/__tests__/MenuSearchInput.test.tsx +157 -0
  54. package/src/MenuSearchInput/__tests__/MenuSearchInput.typetest.tsx +31 -0
  55. package/src/MenuSearchInput/index.ts +1 -0
  56. package/src/MenuSearchTokenInput/MenuSearchTokenInput.tsx +108 -0
  57. package/src/MenuSearchTokenInput/__tests__/MenuSearchTokenInput.test.tsx +247 -0
  58. package/src/MenuSearchTokenInput/__tests__/MenuSearchTokenInput.typetest.tsx +33 -0
  59. package/src/MenuSearchTokenInput/index.ts +1 -0
  60. package/src/MenuToggleButton/MenuToggleButton.stories.tsx +72 -0
  61. package/src/{MenuToggleButton.tsx → MenuToggleButton/MenuToggleButton.tsx} +6 -5
  62. package/src/MenuToggleButton/index.ts +1 -0
  63. package/src/MenuTokenInput.tsx +104 -0
  64. package/src/MultiSelectMenu/MultiSelectMenu.stories.tsx +222 -0
  65. package/src/MultiSelectMenu/MultiSelectMenu.tsx +38 -68
  66. package/src/MultiSelectMenu/MultiSelectMenuTypes.ts +8 -6
  67. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +46 -17
  68. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.typetest.tsx +6 -6
  69. package/src/NestedMenu/NestedMenu.feature +109 -0
  70. package/src/NestedMenu/NestedMenu.stories.tsx +157 -0
  71. package/src/NestedMenu/NestedMenu.tsx +102 -0
  72. package/src/NestedMenu/NestedMenuContent.tsx +75 -0
  73. package/src/NestedMenu/NestedMenuContext.tsx +56 -0
  74. package/src/NestedMenu/NestedMenuHeader.tsx +25 -0
  75. package/src/NestedMenu/NestedMenuItem.tsx +41 -0
  76. package/src/NestedMenu/NestedMenuTypes.ts +47 -0
  77. package/src/NestedMenu/__tests__/NestedMenu.test.tsx +31 -0
  78. package/src/NestedMenu/__tests__/NestedMenu.typetest.tsx +134 -0
  79. package/src/NestedMenu/index.ts +5 -0
  80. package/src/SingleSelectMenu/SingleSelectMenu.stories.tsx +233 -0
  81. package/src/SingleSelectMenu/SingleSelectMenu.tsx +23 -26
  82. package/src/SingleSelectMenu/SingleSelectMenuTypes.ts +7 -5
  83. package/src/SingleSelectMenu/__tests__/SingleSelectMenu.typetest.tsx +6 -6
  84. package/src/SubmenuItem/SubmenuItem.feature +82 -0
  85. package/src/SubmenuItem/SubmenuItem.stories.tsx +163 -0
  86. package/src/SubmenuItem/SubmenuItem.tsx +258 -0
  87. package/src/SubmenuItem/SubmenuItemTypes.ts +24 -0
  88. package/src/SubmenuItem/__tests__/SubmenuItem.test.tsx +3 -0
  89. package/src/SubmenuItem/__tests__/SubmenuItem.typetest.tsx +112 -0
  90. package/src/SubmenuItem/index.ts +2 -0
  91. package/src/hooks/useDownshiftDefaults.tsx +117 -0
  92. package/src/hooks/useItemFiltering.tsx +68 -0
  93. package/src/hooks/useMenuChildren.tsx +205 -131
  94. package/src/index.ts +6 -0
  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 -30
  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/utils/itemToString.ts +0 -5
@@ -0,0 +1,248 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Banner } from "@sproutsocial/seeds-react-banner";
3
+ import { Box } from "@sproutsocial/seeds-react-box";
4
+ import { Icon } from "@sproutsocial/seeds-react-icon";
5
+ import { Text } from "@sproutsocial/seeds-react-text";
6
+ import { Token } from "@sproutsocial/seeds-react-token";
7
+
8
+ import { StorybookMenuToggleButton } from "../storybookUtilities/menu-storybook-components";
9
+ import {
10
+ ActionMenu,
11
+ MenuGroup,
12
+ MenuItem,
13
+ SingleSelectMenu,
14
+ MenuContent,
15
+ MenuHeader,
16
+ MultiSelectMenu,
17
+ } from "../index";
18
+
19
+ const meta: Meta<typeof MenuGroup> = {
20
+ title: "Under Development/Primitives/MenuGroup",
21
+ component: MenuGroup,
22
+ decorators: [
23
+ (Story) => (
24
+ <Box display="flex" gap="16px" flexDirection="column" maxWidth={300}>
25
+ <Story />
26
+ </Box>
27
+ ),
28
+ ],
29
+ argTypes: {
30
+ title: {
31
+ table: {
32
+ category: "MenuGroup",
33
+ },
34
+ description: "Change title of group title",
35
+ control: "text",
36
+ },
37
+ titleAs: {
38
+ table: {
39
+ category: "MenuGroup",
40
+ },
41
+ description: "Example options for title component",
42
+ options: ["h3", "h2", "h1", "p"],
43
+ control: { type: "select" },
44
+ },
45
+ },
46
+ args: {
47
+ titleAs: "h3",
48
+ title: "Group Example",
49
+ },
50
+ };
51
+
52
+ export default meta;
53
+ type Story = StoryObj<typeof MenuGroup>;
54
+
55
+ const menuItemsOne = [
56
+ { id: "item-1", label: "Item 1" },
57
+ { id: "item-2", label: "Item 2" },
58
+ { id: "item-3", label: "Item 3" },
59
+ ];
60
+
61
+ const menuItemsTwo = [
62
+ { id: "item-4", label: "Item 4" },
63
+ { id: "item-5", label: "Item 5" },
64
+ { id: "item-6", label: "Item 6" },
65
+ ];
66
+
67
+ const menuItemsThree = [
68
+ { id: "item-7", label: "Item 7" },
69
+ { id: "item-8", label: "Item 8" },
70
+ { id: "item-9", label: "Item 9" },
71
+ ];
72
+
73
+ const itemsAsProps = [
74
+ {
75
+ id: "item-1",
76
+ children: "Item 1",
77
+ onClick: () => alert("Item 1 clicked"),
78
+ },
79
+ { id: "item-2", children: "Item 2", onClick: () => alert("Item 2 clicked") },
80
+ { id: "item-3", children: "Item 3", href: "https://seeds.sproutsocial.com/" },
81
+ ];
82
+
83
+ export const ItemsAsProps: Story = {
84
+ name: "Items as Props",
85
+ render: () => {
86
+ return (
87
+ <ActionMenu
88
+ defaultIsOpen
89
+ menuToggleElement={<StorybookMenuToggleButton />}
90
+ >
91
+ <MenuContent>
92
+ <MenuGroup id="1" menuItems={itemsAsProps} title="Group 1" />
93
+ <MenuGroup id="2" title="Group 2">
94
+ <MenuItem id="item-4" onClick={() => alert("Item 4 clicked")}>
95
+ Item 4
96
+ </MenuItem>
97
+ </MenuGroup>
98
+ </MenuContent>
99
+ </ActionMenu>
100
+ );
101
+ },
102
+ };
103
+
104
+ export const StandardGroup: Story = {
105
+ name: "Standard Group",
106
+ render: (args) => {
107
+ return (
108
+ <SingleSelectMenu
109
+ defaultIsOpen
110
+ menuToggleElement={<StorybookMenuToggleButton />}
111
+ >
112
+ <MenuContent>
113
+ <MenuGroup title={args.title} titleAs={args.titleAs} id="items">
114
+ {menuItemsOne.map(({ id, label }) => (
115
+ <MenuItem key={id} id={id}>
116
+ {label}
117
+ </MenuItem>
118
+ ))}
119
+ </MenuGroup>
120
+ </MenuContent>
121
+ </SingleSelectMenu>
122
+ );
123
+ },
124
+ };
125
+
126
+ export const GroupWithItems: Story = {
127
+ name: "Multiple Menu Groups",
128
+ args: {
129
+ title: "Menu Group Title",
130
+ },
131
+ render: (args) => {
132
+ return (
133
+ <SingleSelectMenu
134
+ defaultIsOpen
135
+ menuToggleElement={<StorybookMenuToggleButton />}
136
+ >
137
+ <MenuContent>
138
+ <MenuGroup id="items" title={args.title} titleAs={args.titleAs}>
139
+ {menuItemsOne.map(({ id, label }) => (
140
+ <MenuItem key={id} id={id}>
141
+ {label}
142
+ </MenuItem>
143
+ ))}
144
+ </MenuGroup>
145
+ <MenuGroup id="items-2" title={args.title} titleAs={args.titleAs}>
146
+ {menuItemsTwo.map(({ id, label }) => (
147
+ <MenuItem key={id} id={id}>
148
+ {label}
149
+ </MenuItem>
150
+ ))}
151
+ </MenuGroup>
152
+ <MenuGroup id="items-3" title={args.title} titleAs={args.titleAs}>
153
+ {menuItemsThree.map(({ id, label }) => (
154
+ <MenuItem key={id} id={id}>
155
+ {label}
156
+ </MenuItem>
157
+ ))}
158
+ </MenuGroup>
159
+ </MenuContent>
160
+ </SingleSelectMenu>
161
+ );
162
+ },
163
+ };
164
+
165
+ export const DifferentInputTypes: Story = {
166
+ name: "Different Input Types",
167
+ args: {
168
+ title: "Menu Group Title",
169
+ },
170
+ render: ({ title, titleAs }) => {
171
+ return (
172
+ <MultiSelectMenu
173
+ defaultIsOpen
174
+ menuToggleElement={<StorybookMenuToggleButton />}
175
+ >
176
+ <MenuContent>
177
+ <MenuGroup id="items" title={title} titleAs={titleAs}>
178
+ {menuItemsOne.map(({ id, label }) => (
179
+ <MenuItem key={id} id={id} inputType="checkbox">
180
+ {label}
181
+ </MenuItem>
182
+ ))}
183
+ </MenuGroup>
184
+ <MenuGroup
185
+ id="items-2"
186
+ title="Select One"
187
+ titleAs={titleAs}
188
+ isSingleSelect
189
+ >
190
+ {menuItemsTwo.map(({ id, label }) => (
191
+ <MenuItem key={id} id={id} inputType="radio">
192
+ {label}
193
+ </MenuItem>
194
+ ))}
195
+ </MenuGroup>
196
+ </MenuContent>
197
+ </MultiSelectMenu>
198
+ );
199
+ },
200
+ };
201
+
202
+ type customContentStory = StoryObj<
203
+ React.ComponentProps<typeof MenuGroup> & {
204
+ bannerText: string;
205
+ }
206
+ >;
207
+
208
+ export const CustomContent: customContentStory = {
209
+ name: "Custom Content",
210
+ args: {
211
+ title: "Menu Group Title",
212
+ bannerText: "Only one item can be selected",
213
+ },
214
+ render: ({ title, bannerText }: { title?: string; bannerText?: string }) => {
215
+ return (
216
+ <SingleSelectMenu
217
+ defaultIsOpen
218
+ menuToggleElement={<StorybookMenuToggleButton />}
219
+ >
220
+ <MenuContent>
221
+ <MenuHeader title="Group Menu" />
222
+ <MenuGroup id="group-1">
223
+ <Box
224
+ px={300}
225
+ display="flex"
226
+ alignItems="center"
227
+ justifyContent="space-between"
228
+ >
229
+ <Text.SmallBodyCopy>Detected Language</Text.SmallBodyCopy>
230
+ <Token closeable={false}>
231
+ <Icon name="language" />
232
+ English
233
+ </Token>
234
+ </Box>
235
+ </MenuGroup>
236
+ <MenuGroup id="group-2" title={title}>
237
+ <Banner mb={300} text={bannerText} />
238
+ {menuItemsOne.map(({ id, label }) => (
239
+ <MenuItem key={id} id={id} inputType="radio">
240
+ {label}
241
+ </MenuItem>
242
+ ))}
243
+ </MenuGroup>
244
+ </MenuContent>
245
+ </SingleSelectMenu>
246
+ );
247
+ },
248
+ };
@@ -1,7 +1,8 @@
1
1
  import React from "react";
2
+ import type { TypeMenuItemProps } from "../MenuItem";
3
+ import type { TypeMenuRootProps } from "../MenuRoot";
2
4
  import type { TypeMenuGroupProps } from "./MenuGroupTypes";
3
5
  import { StyledMenuGroup, StyledTitle, StyledMenuGroupUl } from "./styles";
4
- import { MenuItem } from "../MenuItem";
5
6
  import { mapMenuItems } from "../hooks/useMenuChildren";
6
7
 
7
8
  /**
@@ -24,24 +25,24 @@ import { mapMenuItems } from "../hooks/useMenuChildren";
24
25
  * @see {@link https://seeds.sproutsocial.com/components/menu-item/ | MenuItem}
25
26
  */
26
27
 
27
- export const MenuGroup = ({
28
+ const MenuGroup = <I extends TypeMenuItemProps = TypeMenuItemProps>({
28
29
  titleAs = "h3",
29
30
  children: childrenProp,
30
31
  title,
31
32
  color,
32
33
  isSingleSelect,
33
34
  id,
34
- items,
35
- MenuItemComponent = MenuItem,
35
+ menuItems,
36
+ MenuItemComponent,
36
37
  ...rest
37
- }: TypeMenuGroupProps) => {
38
+ }: TypeMenuGroupProps<I>) => {
38
39
  // Generate a unique ID for the title
39
40
  const titleId = `menu-group-title-${Math.random().toString(36).slice(2, 11)}`;
40
41
 
41
42
  const children =
42
- childrenProp || !items || !items.length
43
+ childrenProp || !menuItems || !menuItems.length
43
44
  ? childrenProp
44
- : mapMenuItems({ items, MenuItemComponent });
45
+ : mapMenuItems({ menuItems, MenuItemComponent });
45
46
 
46
47
  if (!children) {
47
48
  return null;
@@ -67,3 +68,7 @@ export const MenuGroup = ({
67
68
  </StyledMenuGroup>
68
69
  );
69
70
  };
71
+
72
+ MenuGroup.__MENU_PRIMITIVE_TYPE = "MenuGroup";
73
+
74
+ export { MenuGroup };
@@ -8,6 +8,8 @@ import type {
8
8
  TypeSpaceSystemProps,
9
9
  TypeTypographySystemProps,
10
10
  } from "@sproutsocial/seeds-react-system-props";
11
+ import type { TypeMenuRootProps } from "../MenuRoot";
12
+ import type { TypeMenuItemProps } from "../MenuItem";
11
13
  import type { TypeChildrenOrItems } from "../types";
12
14
 
13
15
  export interface TypeMenuGroupStyledProps
@@ -20,7 +22,7 @@ export interface TypeMenuGroupStyledProps
20
22
  TypeSpaceSystemProps,
21
23
  TypeTypographySystemProps {}
22
24
 
23
- interface TypeBaseMenuGroupProps
25
+ export interface TypeMenuGroupBaseProps
24
26
  extends TypeMenuGroupStyledProps,
25
27
  Omit<React.ComponentPropsWithoutRef<"ul">, "color"> {
26
28
  id: string;
@@ -29,4 +31,6 @@ interface TypeBaseMenuGroupProps
29
31
  titleAs?: string | React.ComponentType<any>;
30
32
  }
31
33
 
32
- export type TypeMenuGroupProps = TypeBaseMenuGroupProps & TypeChildrenOrItems;
34
+ export type TypeMenuGroupProps<
35
+ I extends TypeMenuItemProps = TypeMenuItemProps
36
+ > = TypeMenuGroupBaseProps & TypeChildrenOrItems<I>;
@@ -270,16 +270,16 @@ describe("MenuGroup", () => {
270
270
  ])("$name", () => {});
271
271
  });
272
272
 
273
- // To be implemented when items, customMenuItem and menuItemType props are added
274
- xdescribe("when the items prop is provided", () => {
275
- xit("hides the group if items is empty", () => {});
276
- xit("renders items using MenuItem by default", () => {});
277
- xit("renders items using customMenuItem when provided", () => {});
273
+ // To be implemented when menuItems, customMenuItem and menuItemType props are added
274
+ xdescribe("when the menuItems prop is provided", () => {
275
+ xit("hides the group if menuItems is empty", () => {});
276
+ xit("renders menuItems using MenuItem by default", () => {});
277
+ xit("renders menuItems using customMenuItem when provided", () => {});
278
278
  // use a .each to test each item type
279
279
  xit("sets MenuItem types when menuItemType provided", () => {});
280
280
  });
281
281
 
282
- xdescribe("when the items prop is not provided", () => {
282
+ xdescribe("when the menuItems prop is not provided", () => {
283
283
  xit("ignores the menuItemType prop", () => {});
284
284
  xit("ignores the customMenuItem prop", () => {});
285
285
  });
@@ -10,12 +10,12 @@ export const MenuGroupTypeTest = () => (
10
10
  <MenuItem id="1">Test 1</MenuItem>
11
11
  <MenuItem id="2">Test 2</MenuItem>
12
12
  </MenuGroup>
13
- {/* MenuGroup allows items prop */}
14
- <MenuGroup id="group" items={[{ id: "1", children: "1" }]} />
15
- {/* MenuGroup allows items prop with MenuItemComponent */}
13
+ {/* MenuGroup allows menuItems prop */}
14
+ <MenuGroup id="group" menuItems={[{ id: "1", children: "1" }]} />
15
+ {/* MenuGroup allows menuItems prop with MenuItemComponent */}
16
16
  <MenuGroup
17
17
  id="group"
18
- items={[{ id: "1", children: "1" }]}
18
+ menuItems={[{ id: "1", children: "1" }]}
19
19
  MenuItemComponent={MenuItem}
20
20
  />
21
21
  {/* MenuGroup allows isSingleSelect prop */}
@@ -45,11 +45,11 @@ export const MenuGroupTypeTest = () => (
45
45
 
46
46
  {/* @ts-expect-error - MenuGroup requires children or items */}
47
47
  <MenuGroup id="group" />
48
- {/* @ts-expect-error - MenuGroup does not allow children and items together */}
49
- <MenuGroup id="group" items={[{ id: "1", children: "1" }]}>
48
+ {/* @ts-expect-error - MenuGroup does not allow children and menuItems together */}
49
+ <MenuGroup id="group" menuItems={[{ id: "1", children: "1" }]}>
50
50
  Test
51
51
  </MenuGroup>
52
- {/* @ts-expect-error - MenuGroup does not allow MenuItemComponent without items */}
52
+ {/* @ts-expect-error - MenuGroup does not allow MenuItemComponent without menuItems */}
53
53
  <MenuGroup id="group" MenuItemComponent={MenuItem}>
54
54
  Test
55
55
  </MenuGroup>
@@ -9,9 +9,9 @@ import {
9
9
  color,
10
10
  space,
11
11
  } from "styled-system";
12
- import type { TypeMenuGroupProps } from "./MenuGroupTypes";
12
+ import type { TypeMenuGroupBaseProps } from "./MenuGroupTypes";
13
13
 
14
- export const StyledMenuGroup = styled.li<TypeMenuGroupProps>`
14
+ export const StyledMenuGroup = styled.li<TypeMenuGroupBaseProps>`
15
15
  &:not(:last-child) {
16
16
  border-bottom: 1px solid
17
17
  ${({ theme }) => theme.colors.container.border.base};
@@ -29,9 +29,11 @@ export const StyledMenuGroup = styled.li<TypeMenuGroupProps>`
29
29
  `;
30
30
 
31
31
  export const StyledTitle = styled(Text.SmallSubHeadline)`
32
+ font-size: ${({ theme }) => theme.typography[100].fontSize};
32
33
  margin: ${({ theme }) => theme.space[400]} ${({ theme }) => theme.space[400]}
33
34
  0px ${({ theme }) => theme.space[400]};
34
35
  `;
36
+
35
37
  export const StyledMenuGroupUl = styled.ul`
36
38
  margin: 0;
37
39
  padding: 0;
@@ -0,0 +1,276 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { action } from "@storybook/addon-actions";
3
+
4
+ import { Box } from "@sproutsocial/seeds-react-box";
5
+ import { Banner } from "@sproutsocial/seeds-react-banner";
6
+ import { Button } from "@sproutsocial/seeds-react-button";
7
+ import { Icon } from "@sproutsocial/seeds-react-icon";
8
+ import { Input } from "@sproutsocial/seeds-react-input";
9
+ import { Link } from "@sproutsocial/seeds-react-link";
10
+ import { Text } from "@sproutsocial/seeds-react-text";
11
+ import { Token } from "@sproutsocial/seeds-react-token";
12
+
13
+ import { StorybookMenuToggleButton } from "../storybookUtilities/menu-storybook-components";
14
+ import { TEST_BOOKS } from "../__fixtures__/menu-test-data";
15
+ import {
16
+ MenuHeader,
17
+ MenuContent,
18
+ MenuGroup,
19
+ SingleSelectMenu,
20
+ MenuFooter,
21
+ MenuItem,
22
+ type TypeMenuHeaderProps,
23
+ } from "../index";
24
+
25
+ interface MenuHeaderStoryArgs extends TypeMenuHeaderProps {
26
+ "Trigger Text"?: string;
27
+ "Header Children"?: string;
28
+ }
29
+ type Story = StoryObj<MenuHeaderStoryArgs>;
30
+
31
+ const meta: Meta<MenuHeaderStoryArgs> = {
32
+ title: "Under Development/Primitives/MenuHeader",
33
+ component: MenuHeader,
34
+ parameters: {
35
+ controls: { expanded: true, sort: "requiredFirst" },
36
+ },
37
+ argTypes: {
38
+ "Trigger Text": {
39
+ table: {
40
+ category: "Button",
41
+ },
42
+ control: "text",
43
+ },
44
+ title: {
45
+ table: {
46
+ category: "MenuHeader",
47
+ },
48
+ description: "Header Title Text",
49
+ control: "text",
50
+ },
51
+ "Header Children": {
52
+ table: {
53
+ category: "MenuHeader",
54
+ },
55
+ description: "Example children for header",
56
+ options: ["Input", "Banner", undefined],
57
+ control: { type: "select" },
58
+ },
59
+ rightAction: {
60
+ table: {
61
+ category: "MenuHeader",
62
+ },
63
+ description: "Optional right header action prop",
64
+ options: ["button", "link", undefined],
65
+ control: { type: "select" },
66
+ },
67
+ leftAction: {
68
+ table: {
69
+ category: "MenuHeader",
70
+ },
71
+ description: "Optional left header action prop",
72
+ options: ["button", undefined],
73
+ control: { type: "select" },
74
+ },
75
+ },
76
+ args: {
77
+ "Trigger Text": "Select a Book",
78
+ title: "Select a Book",
79
+ leftAction: "button",
80
+ rightAction: "link",
81
+ "Header Children": "Banner",
82
+ },
83
+ };
84
+
85
+ export default meta;
86
+
87
+ const handleOnClick = (e: React.MouseEvent, itemName: string) => {
88
+ action(`${itemName}`)(e);
89
+ };
90
+
91
+ const StoryActionExamples = ({ type }) => {
92
+ switch (type) {
93
+ case "button":
94
+ return (
95
+ <Button
96
+ width="24px"
97
+ height="24px"
98
+ display="flex"
99
+ justifyContent="center"
100
+ alignItems="center"
101
+ >
102
+ <Icon name="arrow-left" />
103
+ </Button>
104
+ );
105
+ case "link":
106
+ return (
107
+ <Link mt="space.100" href="#">
108
+ Help
109
+ </Link>
110
+ );
111
+ default:
112
+ case undefined:
113
+ return null;
114
+ }
115
+ };
116
+
117
+ const StoryHeaderExamples = (args) => {
118
+ switch (args.type) {
119
+ case "Input":
120
+ return (
121
+ <Input
122
+ id="search"
123
+ name="search"
124
+ placeholder="Search for a book"
125
+ type="search"
126
+ />
127
+ );
128
+ case "Banner":
129
+ return (
130
+ <Banner
131
+ display="block"
132
+ type="opportunity"
133
+ text="Upgrade and get two free books!"
134
+ />
135
+ );
136
+ default:
137
+ case undefined:
138
+ return null;
139
+ }
140
+ };
141
+
142
+ export const MenuHeaderSandbox: Story = {
143
+ name: "Sandbox",
144
+ render: function MenuWithHeader(args) {
145
+ return (
146
+ <Box display="flex" gap="16px" flexDirection="column" maxWidth={300}>
147
+ <SingleSelectMenu
148
+ defaultIsOpen
149
+ menuToggleElement={<StorybookMenuToggleButton />}
150
+ >
151
+ <MenuHeader
152
+ alignItems={args.alignItems}
153
+ leftAction={
154
+ args.leftAction && <StoryActionExamples type={args.leftAction} />
155
+ }
156
+ rightAction={<StoryActionExamples type={args.rightAction} />}
157
+ title={args.title}
158
+ />
159
+ <MenuContent>
160
+ <MenuGroup id="books">
161
+ {TEST_BOOKS.map((book) => (
162
+ <MenuItem
163
+ key={book.id}
164
+ onClick={(e) => handleOnClick(e, book.title)}
165
+ id={book.id}
166
+ >
167
+ {book.title}
168
+ </MenuItem>
169
+ ))}
170
+ </MenuGroup>
171
+ </MenuContent>
172
+ <MenuFooter>
173
+ <Link>Manage book settings</Link>
174
+ </MenuFooter>
175
+ </SingleSelectMenu>
176
+ </Box>
177
+ );
178
+ },
179
+ };
180
+
181
+ export const HeadeWithChildren: Story = {
182
+ name: "Header With Children",
183
+ render: function HeaderWithChildren(args) {
184
+ return (
185
+ <Box display="flex" gap="16px" flexDirection="column" maxWidth={300}>
186
+ <SingleSelectMenu
187
+ defaultIsOpen
188
+ menuToggleElement={<StorybookMenuToggleButton />}
189
+ >
190
+ <MenuHeader
191
+ alignItems={args.alignItems}
192
+ leftAction={
193
+ args.leftAction && <StoryActionExamples type={args.leftAction} />
194
+ }
195
+ rightAction={<StoryActionExamples type={args.rightAction} />}
196
+ title={args.title}
197
+ >
198
+ <StoryHeaderExamples type={args["Header Children"]} />
199
+ </MenuHeader>
200
+ <MenuContent>
201
+ <MenuGroup id="language">
202
+ <Box
203
+ px={300}
204
+ display="flex"
205
+ alignItems="center"
206
+ justifyContent="space-between"
207
+ >
208
+ <Text.SmallBodyCopy>Detected Language</Text.SmallBodyCopy>
209
+ <Token closeable={false}>
210
+ <Icon name="language" />
211
+ English
212
+ </Token>
213
+ </Box>
214
+ </MenuGroup>
215
+ <MenuGroup id="books">
216
+ {TEST_BOOKS.map((book) => (
217
+ <MenuItem
218
+ key={book.id}
219
+ onClick={(e) => handleOnClick(e, book.title)}
220
+ id={book.id}
221
+ >
222
+ {book.title}
223
+ </MenuItem>
224
+ ))}
225
+ </MenuGroup>
226
+ </MenuContent>
227
+ <MenuFooter>
228
+ <Link>Manage book settings</Link>
229
+ </MenuFooter>
230
+ </SingleSelectMenu>
231
+ </Box>
232
+ );
233
+ },
234
+ };
235
+
236
+ export const MenuHeaderLongTitle: Story = {
237
+ name: "MenuHeader w/ Long Title",
238
+ args: {
239
+ title: "This is a really long title that should wrap",
240
+ },
241
+ render: function MenuWithHeader(args) {
242
+ return (
243
+ <Box display="flex" gap="16px" flexDirection="column" maxWidth={300}>
244
+ <SingleSelectMenu
245
+ defaultIsOpen
246
+ menuToggleElement={<StorybookMenuToggleButton />}
247
+ >
248
+ <MenuHeader
249
+ alignItems={args.alignItems}
250
+ leftAction={
251
+ args.leftAction && <StoryActionExamples type={args.leftAction} />
252
+ }
253
+ rightAction={<StoryActionExamples type={args.rightAction} />}
254
+ title={args.title}
255
+ />
256
+ <MenuContent>
257
+ <MenuGroup id="books">
258
+ {TEST_BOOKS.map((book) => (
259
+ <MenuItem
260
+ key={book.id}
261
+ onClick={(e) => handleOnClick(e, book.title)}
262
+ id={book.id}
263
+ >
264
+ {book.title}
265
+ </MenuItem>
266
+ ))}
267
+ </MenuGroup>
268
+ </MenuContent>
269
+ <MenuFooter>
270
+ <Link>Manage book settings</Link>
271
+ </MenuFooter>
272
+ </SingleSelectMenu>
273
+ </Box>
274
+ );
275
+ },
276
+ };