@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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +32 -0
- package/dist/esm/index.js +1553 -423
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +303 -122
- package/dist/index.d.ts +303 -122
- package/dist/index.js +1561 -405
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/src/ActionMenu/ActionMenu.stories.tsx +426 -0
- package/src/ActionMenu/ActionMenu.tsx +30 -26
- package/src/ActionMenu/ActionMenuTypes.ts +13 -8
- package/src/ActionMenu/__tests__/ActionMenu.test.tsx +17 -30
- package/src/ActionMenu/__tests__/ActionMenu.typetest.tsx +32 -3
- package/src/Autocomplete/Autocomplete.stories.tsx +61 -2
- package/src/Autocomplete/Autocomplete.tsx +15 -74
- package/src/Autocomplete/AutocompleteInput.tsx +18 -27
- package/src/Autocomplete/AutocompleteTokenInput.tsx +50 -0
- package/src/Autocomplete/AutocompleteTypes.ts +27 -11
- package/src/Autocomplete/MultiAutocomplete.tsx +189 -0
- package/src/Autocomplete/SingleAutocomplete.tsx +74 -0
- package/src/Autocomplete/__tests__/Autocomplete.test.tsx +171 -3
- package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +116 -3
- package/src/MenuContent/MenuContent.stories.tsx +164 -0
- package/src/MenuContent/MenuContent.tsx +35 -8
- package/src/MenuContent/MenuContentTypes.ts +7 -5
- package/src/MenuContent/__tests__/MenuContent.typetest.tsx +18 -2
- package/src/MenuContext.tsx +191 -37
- package/src/MenuFooter/MenuFooter.stories.tsx +202 -0
- package/src/MenuFooter/__tests__/MenuFooter.test.tsx +16 -11
- package/src/MenuGroup/MenuGroup.feature +21 -21
- package/src/MenuGroup/MenuGroup.stories.tsx +248 -0
- package/src/MenuGroup/MenuGroup.tsx +9 -7
- package/src/MenuGroup/MenuGroupTypes.ts +8 -6
- package/src/MenuGroup/__tests__/MenuGroup.test.tsx +6 -6
- package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +23 -4
- package/src/MenuGroup/styles.tsx +4 -2
- package/src/MenuHeader/MenuHeader.stories.tsx +276 -0
- package/src/MenuHeader/MenuHeader.tsx +3 -1
- package/src/MenuHeader/__tests__/MenuHeader.test.tsx +23 -13
- package/src/MenuHeader/styles.tsx +18 -14
- package/src/MenuItem/MenuItem.stories.tsx +507 -0
- package/src/MenuItem/MenuItem.tsx +18 -9
- package/src/MenuItem/MenuItemTypes.ts +9 -4
- package/src/MenuItem/__tests__/MenuItem.test.tsx +6 -6
- package/src/MenuItem/styles.tsx +21 -5
- package/src/MenuItem/useOptionProps.tsx +10 -8
- package/src/MenuLabel.tsx +2 -2
- package/src/MenuRoot/MenuRoot.tsx +37 -23
- package/src/MenuRoot/__tests__/MenuRoot.test.tsx +59 -32
- package/src/MenuSearchInput/MenuSearchInput.tsx +63 -0
- package/src/MenuSearchInput/__tests__/MenuSearchInput.test.tsx +157 -0
- package/src/MenuSearchInput/__tests__/MenuSearchInput.typetest.tsx +31 -0
- package/src/MenuSearchInput/index.ts +1 -0
- package/src/MenuSearchTokenInput/MenuSearchTokenInput.tsx +98 -0
- package/src/MenuSearchTokenInput/__tests__/MenuSearchTokenInput.test.tsx +247 -0
- package/src/MenuSearchTokenInput/__tests__/MenuSearchTokenInput.typetest.tsx +33 -0
- package/src/MenuSearchTokenInput/index.ts +1 -0
- package/src/MenuToggleButton/MenuToggleButton.stories.tsx +72 -0
- package/src/{MenuToggleButton.tsx → MenuToggleButton/MenuToggleButton.tsx} +6 -5
- package/src/MenuToggleButton/index.ts +1 -0
- package/src/MenuTokenInput.tsx +104 -0
- package/src/MultiSelectMenu/MultiSelectMenu.stories.tsx +214 -0
- package/src/MultiSelectMenu/MultiSelectMenu.tsx +44 -67
- package/src/MultiSelectMenu/MultiSelectMenuTypes.ts +9 -5
- package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +46 -60
- package/src/MultiSelectMenu/__tests__/MultiSelectMenu.typetest.tsx +29 -2
- package/src/NestedMenu/NestedMenu.feature +109 -0
- package/src/NestedMenu/NestedMenu.stories.tsx +157 -0
- package/src/NestedMenu/NestedMenu.tsx +102 -0
- package/src/NestedMenu/NestedMenuContent.tsx +71 -0
- package/src/NestedMenu/NestedMenuContext.tsx +56 -0
- package/src/NestedMenu/NestedMenuHeader.tsx +25 -0
- package/src/NestedMenu/NestedMenuItem.tsx +37 -0
- package/src/NestedMenu/NestedMenuTypes.ts +47 -0
- package/src/NestedMenu/__tests__/NestedMenu.test.tsx +31 -0
- package/src/NestedMenu/__tests__/NestedMenu.typetest.tsx +134 -0
- package/src/NestedMenu/index.ts +5 -0
- package/src/SingleSelectMenu/SingleSelectMenu.stories.tsx +227 -0
- package/src/SingleSelectMenu/SingleSelectMenu.tsx +29 -25
- package/src/SingleSelectMenu/SingleSelectMenuTypes.ts +9 -5
- package/src/SingleSelectMenu/__tests__/SingleSelectMenu.typetest.tsx +32 -2
- package/src/SubmenuItem/SubmenuItem.feature +82 -0
- package/src/SubmenuItem/SubmenuItem.stories.tsx +163 -0
- package/src/SubmenuItem/SubmenuItem.tsx +254 -0
- package/src/SubmenuItem/SubmenuItemTypes.ts +24 -0
- package/src/SubmenuItem/__tests__/SubmenuItem.test.tsx +3 -0
- package/src/SubmenuItem/__tests__/SubmenuItem.typetest.tsx +112 -0
- package/src/SubmenuItem/index.ts +2 -0
- package/src/__tests__/Menu.test.tsx +38 -0
- package/src/hooks/useDownshiftDefaults.tsx +107 -0
- package/src/hooks/useItemFiltering.tsx +66 -0
- package/src/hooks/useMenuChildren.tsx +279 -0
- package/src/index.ts +7 -1
- package/src/menuTestingUtils.tsx +21 -10
- package/src/reducers/nestedMenuStateReducer.tsx +23 -0
- package/src/reducers/useComboboxStateReducer.tsx +26 -8
- package/src/reducers/useSelectStateReducer.tsx +24 -6
- package/src/storybookUtilities/menu-storybook-components.tsx +5 -3
- package/src/types.ts +38 -5
- package/src/utils/downshiftDefaults.ts +9 -0
- package/src/utils/getMultiSelectOverrides.ts +21 -0
- package/src/utils/reduceReducers.ts +19 -0
- package/src/hooks/useItemsFromChildren.tsx +0 -131
- 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,9 @@
|
|
|
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 {
|
|
6
|
+
import { mapMenuItems } from "../hooks/useMenuChildren";
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* @link https://seeds.sproutsocial.com/components/menu-group/
|
|
@@ -23,24 +25,24 @@ import { MenuItem } from "../MenuItem";
|
|
|
23
25
|
* @see {@link https://seeds.sproutsocial.com/components/menu-item/ | MenuItem}
|
|
24
26
|
*/
|
|
25
27
|
|
|
26
|
-
export const MenuGroup = ({
|
|
28
|
+
export const MenuGroup = <I extends TypeMenuItemProps = TypeMenuItemProps>({
|
|
27
29
|
titleAs = "h3",
|
|
28
30
|
children: childrenProp,
|
|
29
31
|
title,
|
|
30
32
|
color,
|
|
31
33
|
isSingleSelect,
|
|
32
34
|
id,
|
|
33
|
-
|
|
34
|
-
MenuItemComponent
|
|
35
|
+
menuItems,
|
|
36
|
+
MenuItemComponent,
|
|
35
37
|
...rest
|
|
36
|
-
}: TypeMenuGroupProps) => {
|
|
38
|
+
}: TypeMenuGroupProps<I>) => {
|
|
37
39
|
// Generate a unique ID for the title
|
|
38
40
|
const titleId = `menu-group-title-${Math.random().toString(36).slice(2, 11)}`;
|
|
39
41
|
|
|
40
42
|
const children =
|
|
41
|
-
childrenProp || !
|
|
43
|
+
childrenProp || !menuItems || !menuItems.length
|
|
42
44
|
? childrenProp
|
|
43
|
-
:
|
|
45
|
+
: mapMenuItems({ menuItems, MenuItemComponent });
|
|
44
46
|
|
|
45
47
|
if (!children) {
|
|
46
48
|
return null;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
1
|
import type {
|
|
3
2
|
TypeBorderSystemProps,
|
|
4
3
|
TypeColorSystemProps,
|
|
@@ -9,7 +8,9 @@ import type {
|
|
|
9
8
|
TypeSpaceSystemProps,
|
|
10
9
|
TypeTypographySystemProps,
|
|
11
10
|
} from "@sproutsocial/seeds-react-system-props";
|
|
12
|
-
import {
|
|
11
|
+
import type { TypeMenuRootProps } from "../MenuRoot";
|
|
12
|
+
import type { TypeMenuItemProps } from "../MenuItem";
|
|
13
|
+
import type { TypeChildrenOrItems } from "../types";
|
|
13
14
|
|
|
14
15
|
export interface TypeMenuGroupStyledProps
|
|
15
16
|
extends TypeBorderSystemProps,
|
|
@@ -21,14 +22,15 @@ export interface TypeMenuGroupStyledProps
|
|
|
21
22
|
TypeSpaceSystemProps,
|
|
22
23
|
TypeTypographySystemProps {}
|
|
23
24
|
|
|
24
|
-
export interface
|
|
25
|
+
export interface TypeMenuGroupBaseProps
|
|
25
26
|
extends TypeMenuGroupStyledProps,
|
|
26
27
|
Omit<React.ComponentPropsWithoutRef<"ul">, "color"> {
|
|
27
28
|
id: string;
|
|
28
29
|
isSingleSelect?: boolean;
|
|
29
30
|
title?: string;
|
|
30
|
-
children?: ReactNode;
|
|
31
|
-
items?: TypeMenuItemProps[];
|
|
32
|
-
MenuItemComponent?: typeof MenuItem;
|
|
33
31
|
titleAs?: string | React.ComponentType<any>;
|
|
34
32
|
}
|
|
33
|
+
|
|
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
|
|
274
|
-
xdescribe("when the
|
|
275
|
-
xit("hides the group if
|
|
276
|
-
xit("renders
|
|
277
|
-
xit("renders
|
|
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
|
|
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,6 +10,14 @@ 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 menuItems prop */}
|
|
14
|
+
<MenuGroup id="group" menuItems={[{ id: "1", children: "1" }]} />
|
|
15
|
+
{/* MenuGroup allows menuItems prop with MenuItemComponent */}
|
|
16
|
+
<MenuGroup
|
|
17
|
+
id="group"
|
|
18
|
+
menuItems={[{ id: "1", children: "1" }]}
|
|
19
|
+
MenuItemComponent={MenuItem}
|
|
20
|
+
/>
|
|
13
21
|
{/* MenuGroup allows isSingleSelect prop */}
|
|
14
22
|
<MenuGroup id="group-1" isSingleSelect={true}>
|
|
15
23
|
<MenuItem id="1">Test 1</MenuItem>
|
|
@@ -34,10 +42,21 @@ export const MenuGroupTypeTest = () => (
|
|
|
34
42
|
<MenuGroup id="group" border="1px solid black" bg="white" width="150px">
|
|
35
43
|
Test
|
|
36
44
|
</MenuGroup>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
|
|
46
|
+
{/* @ts-expect-error - MenuGroup requires children or items */}
|
|
47
|
+
<MenuGroup id="group" />
|
|
48
|
+
{/* @ts-expect-error - MenuGroup does not allow children and menuItems together */}
|
|
49
|
+
<MenuGroup id="group" menuItems={[{ id: "1", children: "1" }]}>
|
|
50
|
+
Test
|
|
51
|
+
</MenuGroup>
|
|
52
|
+
{/* @ts-expect-error - MenuGroup does not allow MenuItemComponent without menuItems */}
|
|
53
|
+
<MenuGroup id="group" MenuItemComponent={MenuItem}>
|
|
54
|
+
Test
|
|
55
|
+
</MenuGroup>
|
|
56
|
+
{/* @ts-expect-error - MenuGroup validates the type of MenuItemComponent */}
|
|
57
|
+
<MenuGroup id="group" MenuItemComponent={Text}>
|
|
58
|
+
Test
|
|
59
|
+
</MenuGroup>
|
|
41
60
|
|
|
42
61
|
{/* @ts-expect-error - MenuGroup title is invalid as number */}
|
|
43
62
|
<MenuGroup id="group" title={123}>
|
package/src/MenuGroup/styles.tsx
CHANGED
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
color,
|
|
10
10
|
space,
|
|
11
11
|
} from "styled-system";
|
|
12
|
-
import type {
|
|
12
|
+
import type { TypeMenuGroupBaseProps } from "./MenuGroupTypes";
|
|
13
13
|
|
|
14
|
-
export const StyledMenuGroup = styled.li<
|
|
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[200].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
|
+
};
|