@sproutsocial/seeds-react-menu 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +27 -0
  3. package/dist/esm/index.js +243 -75
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.mts +81 -49
  6. package/dist/index.d.ts +81 -49
  7. package/dist/index.js +237 -67
  8. package/dist/index.js.map +1 -1
  9. package/package.json +3 -1
  10. package/src/Autocomplete/Autocomplete.feature +14 -43
  11. package/src/Autocomplete/Autocomplete.stories.tsx +128 -0
  12. package/src/Autocomplete/Autocomplete.tsx +29 -11
  13. package/src/Autocomplete/AutocompleteInput.tsx +51 -0
  14. package/src/Autocomplete/AutocompleteTypes.ts +8 -1
  15. package/src/Autocomplete/__tests__/Autocomplete.test.tsx +142 -89
  16. package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +25 -43
  17. package/src/Autocomplete/index.ts +1 -0
  18. package/src/MenuContent/MenuContent.tsx +2 -2
  19. package/src/MenuContext.tsx +11 -5
  20. package/src/MenuGroup/MenuGroup.feature +19 -37
  21. package/src/MenuGroup/MenuGroup.tsx +18 -3
  22. package/src/MenuGroup/MenuGroupTypes.ts +6 -1
  23. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +167 -9
  24. package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +27 -13
  25. package/src/MenuItem/MenuItem.tsx +7 -4
  26. package/src/MenuLabel.tsx +4 -4
  27. package/src/MenuRoot/MenuRoot.tsx +5 -3
  28. package/src/MenuRoot/__tests__/MenuRoot.test.tsx +1 -1
  29. package/src/MultiSelectMenu/MultiSelectMenu.tsx +8 -0
  30. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +6 -6
  31. package/src/SingleSelectMenu/SingleSelectMenu.tsx +2 -2
  32. package/src/SingleSelectMenu/__tests__/SingleSelectMenu.test.tsx +3 -3
  33. package/src/__fixtures__/menu-test-data.ts +36 -0
  34. package/src/__tests__/Menu.test.tsx +29 -96
  35. package/src/hooks/useItemsFromChildren.tsx +71 -21
  36. package/src/index.ts +1 -0
  37. package/src/reducers/useComboboxStateReducer.tsx +11 -8
  38. package/src/storybookUtilities/menu-storybook-components.tsx +39 -0
  39. package/src/types.ts +3 -0
@@ -1,27 +1,26 @@
1
1
  import * as React from "react";
2
- import {
3
- MultiSelectMenu,
4
- MenuContent,
5
- MenuHeader,
6
- MenuFooter,
7
- } from "../../index";
2
+ import { Autocomplete, MenuContent, MenuHeader, MenuFooter } from "../../index";
8
3
 
9
- export const MultiSelectMenuTypeTest = () => (
4
+ export const AutocompleteTypeTest = () => (
10
5
  <>
11
- {/* Valid MultiSelectMenu with minimal required props */}
12
- <MultiSelectMenu menuToggleElement={<>test</>}>Test</MultiSelectMenu>
13
- {/* Valid MultiSelectMenu allows MenuContent by itself as a child */}
14
- <MultiSelectMenu menuToggleElement={<>test</>}>
6
+ {/* Valid Autocomplete with minimal required props */}
7
+ <Autocomplete menuToggleElement={<>test</>}>Test</Autocomplete>
8
+ {/* Valid Autocomplete allows MenuContent by itself as a child */}
9
+ <Autocomplete menuToggleElement={<>test</>}>
15
10
  <MenuContent>Test</MenuContent>
16
- </MultiSelectMenu>
17
- {/* Valid MultiSelectMenu allows multiple children without fragment */}
18
- <MultiSelectMenu menuToggleElement={<>test</>}>
11
+ </Autocomplete>
12
+ {/* Valid Autocomplete allows multiple children without fragment */}
13
+ <Autocomplete menuToggleElement={<>test</>}>
19
14
  <MenuHeader>Test</MenuHeader>
20
15
  <MenuContent>Test</MenuContent>
21
16
  <MenuFooter>Test</MenuFooter>
22
- </MultiSelectMenu>
23
- {/* Valid MultiSelectMenu accepts Downshift props and functions for useSelect */}
24
- <MultiSelectMenu
17
+ </Autocomplete>
18
+ {/* Valid Autocomplete allows getIsItemVisible function */}
19
+ <Autocomplete menuToggleElement={<>test</>} getIsItemVisible={() => true}>
20
+ Test
21
+ </Autocomplete>
22
+ {/* Valid Autocomplete accepts Downshift props and functions for useCombobox */}
23
+ <Autocomplete
25
24
  menuToggleElement={<>test</>}
26
25
  onSelectedItemChange={() => {}}
27
26
  selectedItem={null}
@@ -31,31 +30,18 @@ export const MultiSelectMenuTypeTest = () => (
31
30
  isOpen
32
31
  >
33
32
  Test
34
- </MultiSelectMenu>
35
- {/* Valid MultiSelectMenu accepts Downshift props and functions for useMultipleSelection */}
36
- <MultiSelectMenu
37
- menuToggleElement={<>test</>}
38
- onSelectedItemsChange={() => {}}
39
- selectedItems={[]}
40
- multiSelectStateReducer={(c) => c}
41
- onMultiSelectStateChange={() => {}}
42
- getA11yMultiStatusMessage={() => ""}
43
- >
44
- Test
45
- </MultiSelectMenu>
46
-
33
+ </Autocomplete>
47
34
  {/* @ts-expect-error - menuToggleElement is a required prop */}
48
- <MultiSelectMenu>Test</MultiSelectMenu>
49
- <MultiSelectMenu
35
+ <Autocomplete>Test</Autocomplete>
36
+ <Autocomplete
50
37
  /* @ts-expect-error - menuToggleElement must be a valid react element */
51
38
  menuToggleElement="test"
52
39
  >
53
40
  Test
54
- </MultiSelectMenu>
41
+ </Autocomplete>
55
42
  {/* @ts-expect-error - when no children provided */}
56
- <MultiSelectMenu menuToggleElement="test" />
57
-
58
- <MultiSelectMenu
43
+ <Autocomplete menuToggleElement="test" />
44
+ <Autocomplete
59
45
  menuToggleElement={<>test</>}
60
46
  /* @ts-expect-error - isOpen can't be a string */
61
47
  isOpen="false"
@@ -65,14 +51,10 @@ export const MultiSelectMenuTypeTest = () => (
65
51
  selectedItem="test"
66
52
  /* @ts-expect-error - stateReducer must have a return value */
67
53
  stateReducer={() => {}}
68
- /* @ts-expect-error - onMultiSelectStateChange can't be a string */
69
- onMultiSelectStateChange="test"
70
- /* @ts-expect-error - selectedItems can't be a string */
71
- selectedItems="test"
72
- /* @ts-expect-error - multiSelectStateReducer must have a return value */
73
- multiSelectStateReducer="test"
54
+ /* @ts-expect-error - getIsItemVisible must be a valid function */
55
+ getIsItemVisible={true}
74
56
  >
75
57
  Test
76
- </MultiSelectMenu>
58
+ </Autocomplete>
77
59
  </>
78
60
  );
@@ -1,2 +1,3 @@
1
1
  export * from "./Autocomplete";
2
+ export * from "./AutocompleteInput";
2
3
  export * from "./AutocompleteTypes";
@@ -11,10 +11,10 @@ import { useMenuContext } from "../MenuContext";
11
11
  * <ActionMenu>
12
12
  * <MenuHeader>Header</MenuHeader>
13
13
  * <MenuContent>
14
- * <MenuGroup>
14
+ * <MenuGroup id="1">
15
15
  * <MenuItem>Item 1</MenuItem>
16
16
  * </MenuGroup>
17
- * <MenuGroup>
17
+ * <MenuGroup id="2">
18
18
  * <MenuItem>Item 2</MenuItem>
19
19
  * </MenuGroup>
20
20
  * </MenuContent>
@@ -63,6 +63,7 @@ export interface TypeMenuContextProps
63
63
  TypeDownshiftMultiSelectReturnValue,
64
64
  TypeDownshiftMultiSelectProps {
65
65
  items: TypeDefaultItemProps[];
66
+ itemsMap?: { [key: string]: TypeDefaultItemProps };
66
67
  isListbox?: boolean;
67
68
  onHighlightedIndexChange?:
68
69
  | TypeDownshiftComboboxProps["onHighlightedIndexChange"]
@@ -94,8 +95,9 @@ export interface TypeMenuContext extends TypeMenuContextProps {
94
95
  itemsMap: { [key: string]: TypeDefaultItemProps };
95
96
  }
96
97
 
97
- export interface TypeMenuProviderProps extends Partial<TypeMenuContextProps> {
98
+ export interface TypeMenuProviderProps {
98
99
  children: TypeNotEmptyChildren;
100
+ menuProps: Partial<TypeMenuContextProps>;
99
101
  }
100
102
 
101
103
  /**
@@ -113,17 +115,21 @@ export const useMenu = ({
113
115
  items = [],
114
116
  isListbox = false,
115
117
  isOpen = false,
118
+ itemsMap: itemsMapFromProps,
116
119
  ...rest
117
120
  }: Partial<TypeMenuContextProps> = {}): TypeMenuContext => {
118
121
  const menuProps = useMemo(() => ({ ...rest }), [rest]);
119
122
 
120
123
  const itemsMap = useMemo(
121
124
  () =>
125
+ itemsMapFromProps ||
122
126
  items.reduce((map, item) => {
123
- map[`${item.id}`] = item;
127
+ map[`${item.id}`] = {
128
+ ...item,
129
+ };
124
130
  return map;
125
131
  }, {} as TypeMenuContext["itemsMap"]),
126
- [items]
132
+ [items, itemsMapFromProps]
127
133
  );
128
134
 
129
135
  return {
@@ -145,9 +151,9 @@ export const MenuContext = createContext<TypeMenuContext>(defaultMenuContext);
145
151
  */
146
152
  export const MenuProvider = ({
147
153
  children,
148
- ...overrides
154
+ menuProps,
149
155
  }: TypeMenuProviderProps) => {
150
- const filterContext = useMenu(overrides);
156
+ const filterContext = useMenu(menuProps);
151
157
 
152
158
  return (
153
159
  <MenuContext.Provider value={{ ...filterContext }}>
@@ -6,20 +6,19 @@ Feature: Menu Group
6
6
  Background:
7
7
  Given an end user is using the component
8
8
 
9
- Scenario: MenuGroup has isSingleSelect true
10
- When the MenuGroup isSingleSelect is true
11
- And MenuContext is set to multiple selection
12
- Then the group behaves like a single select
9
+ Scenario Outline: MenuGroup can be single select
10
+ When the MenuGroup has isSingleSelect set to <value>
11
+ And MenuContext is set to <menu type>
12
+ Then <selection behavior>
13
13
 
14
- Scenario: MenuGroup has isSingleSelect undefined
15
- When the MenuGroup isSingleSelect is undefined
16
- And MenuContext is set to multiple selection
17
- Then the group behaves like a multi select
18
-
19
- Scenario: MenuGroup has isSingleSelect false
20
- When the MenuGroup isSingleSelect is false
21
- And MenuContext is set to multiple selection
22
- Then the group behaves like a multi select
14
+ Examples:
15
+ | value | menu type | selection behavior |
16
+ | true | single | the group behaves like a single select |
17
+ | false | single | the group behaves like a single select |
18
+ | undefined | single | the group behaves like a single select |
19
+ | true | multiple | the group behaves like a single select |
20
+ | false | multiple | the group behaves like a multi select |
21
+ | undefined | multiple | the group behaves like a multi select |
23
22
 
24
23
  Rule: API
25
24
  Background:
@@ -59,26 +58,9 @@ Feature: Menu Group
59
58
  Given a list of items is not empty
60
59
  And the items are a valid object shape
61
60
  When that list of items is provided to the items prop on a MenuGroup
62
- And a valid component is provided to the customMenuItem prop
61
+ And a valid component is provided to the customMenuItem prop
63
62
  Then the MenuGroup will render the items using the customMenuItem component
64
63
 
65
- Scenario: MenuGroup ignores the menuItemType without items prop
66
- When a MenuGroup has menuItemType set
67
- And is not provided with a valid items prop
68
- Then the menuItemType will be ignored
69
-
70
- Scenario Outline: MenuGroup can set the type for all MenuItems
71
- When menuItemType is set to <menu item type>
72
- And MenuGroup is provided with a valid items prop
73
- Then each MenuItem rendered by the MenuGroup with use this type
74
-
75
- Examples: Prop Types
76
- | menu item type |
77
- | undefined (default) |
78
- | checkbox |
79
- | radio |
80
- | switch |
81
-
82
64
  Scenario Outline: MenuGroup has custom content
83
65
  When <custom content> is present at the <location>
84
66
  Then <displayed content> will render
@@ -94,9 +76,9 @@ Feature: Menu Group
94
76
  Then MenuRoot expects it to be of type <propType>
95
77
 
96
78
  Examples: Prop Types
97
- | propName | propType |
98
- | children | ReactNode |
99
- | title | string |
100
- | items | TypeItem[] |
101
- | menuItemType | TypeMenuItemProps['type'] |
102
- | customMenuItem | tyepof MenuItem |
79
+ | propName | propType |
80
+ | children | ReactNode |
81
+ | title | string |
82
+ | items | TypeMenuItemProps[] |
83
+ | isSingleSelect | boolean |
84
+ | MenuItemComponent | tyepof MenuItem |
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import type { TypeMenuGroupProps } from "./MenuGroupTypes";
3
3
  import { StyledMenuGroup, StyledTitle, StyledMenuGroupUl } from "./styles";
4
+ import { MenuItem } from "../MenuItem";
4
5
 
5
6
  /**
6
7
  * @link https://seeds.sproutsocial.com/components/menu-group/
@@ -10,10 +11,10 @@ import { StyledMenuGroup, StyledTitle, StyledMenuGroupUl } from "./styles";
10
11
  * @example
11
12
  * <ActionMenu>
12
13
  * <MenuContent>
13
- * <MenuGroup title="Group 1">
14
+ * <MenuGroup title="Group 1" id="1">
14
15
  * <MenuItem>Item 1</MenuItem>
15
16
  * </MenuGroup>
16
- * <MenuGroup title="Group 1">
17
+ * <MenuGroup title="Group 2" id="2">
17
18
  * <MenuItem>Item 2</MenuItem>
18
19
  * </MenuGroup>
19
20
  * </MenuContent>
@@ -24,17 +25,31 @@ import { StyledMenuGroup, StyledTitle, StyledMenuGroupUl } from "./styles";
24
25
 
25
26
  export const MenuGroup = ({
26
27
  titleAs = "h3",
27
- children,
28
+ children: childrenProp,
28
29
  title,
29
30
  color,
31
+ isSingleSelect,
32
+ id,
33
+ items,
34
+ MenuItemComponent = MenuItem,
30
35
  ...rest
31
36
  }: TypeMenuGroupProps) => {
32
37
  // Generate a unique ID for the title
33
38
  const titleId = `menu-group-title-${Math.random().toString(36).slice(2, 11)}`;
34
39
 
40
+ const children =
41
+ childrenProp || !items || !items.length
42
+ ? childrenProp
43
+ : items.map((item) => <MenuItemComponent key={item.id} {...item} />);
44
+
45
+ if (!children) {
46
+ return null;
47
+ }
35
48
  return (
36
49
  <StyledMenuGroup
37
50
  role="group"
51
+ id={id}
52
+ isSingleSelect={isSingleSelect}
38
53
  aria-labelledby={title ? titleId : undefined}
39
54
  // TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.
40
55
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -9,6 +9,7 @@ import type {
9
9
  TypeSpaceSystemProps,
10
10
  TypeTypographySystemProps,
11
11
  } from "@sproutsocial/seeds-react-system-props";
12
+ import { MenuItem, type TypeMenuItemProps } from "../MenuItem/index";
12
13
 
13
14
  export interface TypeMenuGroupStyledProps
14
15
  extends TypeBorderSystemProps,
@@ -23,7 +24,11 @@ export interface TypeMenuGroupStyledProps
23
24
  export interface TypeMenuGroupProps
24
25
  extends TypeMenuGroupStyledProps,
25
26
  Omit<React.ComponentPropsWithoutRef<"ul">, "color"> {
27
+ id: string;
28
+ isSingleSelect?: boolean;
26
29
  title?: string;
27
- children: ReactNode;
30
+ children?: ReactNode;
31
+ items?: TypeMenuItemProps[];
32
+ MenuItemComponent?: typeof MenuItem;
28
33
  titleAs?: string | React.ComponentType<any>;
29
34
  }
@@ -1,6 +1,13 @@
1
- import { render, screen } from "@sproutsocial/seeds-react-testing-library";
1
+ import {
2
+ render,
3
+ screen,
4
+ waitFor,
5
+ act,
6
+ } from "@sproutsocial/seeds-react-testing-library";
7
+ import { PointerEventsCheckLevel } from "@testing-library/user-event";
2
8
  import {
3
9
  SingleSelectMenu,
10
+ MultiSelectMenu,
4
11
  MenuContent,
5
12
  MenuGroup,
6
13
  MenuToggleButton,
@@ -14,11 +21,162 @@ const menuItems = [
14
21
  ];
15
22
 
16
23
  describe("MenuGroup", () => {
17
- xdescribe("User Behavior", () => {
18
- // To be enabled when isSingleSelect prop added
19
- xit("behaves as it normally would when isSingleSelect is undefined", () => {});
20
- xit("behaves as it normally would when isSingleSelect is false", () => {});
21
- xit("behaves like a single select when isSingleSelect is true", () => {});
24
+ describe("User Behavior", () => {
25
+ it("allows multiple selections per group when isSingleSelect is undefined", async () => {
26
+ const { user } = render(
27
+ <MultiSelectMenu
28
+ defaultIsOpen
29
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
30
+ >
31
+ <MenuContent>
32
+ <MenuGroup id="group">
33
+ {menuItems.map(({ id, label }) => (
34
+ <MenuItem key={id} id={id}>
35
+ {label}
36
+ </MenuItem>
37
+ ))}
38
+ </MenuGroup>
39
+ </MenuContent>
40
+ </MultiSelectMenu>
41
+ );
42
+
43
+ const options = screen.getAllByRole("option");
44
+
45
+ // Click on each option
46
+ for (const option of screen.getAllByRole("option")) {
47
+ await user
48
+ .setup({
49
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
50
+ })
51
+ .click(option);
52
+ }
53
+
54
+ // Expect all options to be selected.
55
+ for (const option of options) {
56
+ expect(option).toHaveAttribute("aria-selected", "true");
57
+ }
58
+ });
59
+ it("allows multiple selections per group when isSingleSelect is false", async () => {
60
+ const { user } = render(
61
+ <MultiSelectMenu
62
+ defaultIsOpen
63
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
64
+ >
65
+ <MenuContent>
66
+ <MenuGroup id="group" isSingleSelect={false}>
67
+ {menuItems.map(({ id, label }) => (
68
+ <MenuItem key={id} id={id}>
69
+ {label}
70
+ </MenuItem>
71
+ ))}
72
+ </MenuGroup>
73
+ </MenuContent>
74
+ </MultiSelectMenu>
75
+ );
76
+
77
+ const options = screen.getAllByRole("option");
78
+
79
+ // Click on each option
80
+ for (const option of screen.getAllByRole("option")) {
81
+ await user
82
+ .setup({
83
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
84
+ })
85
+ .click(option);
86
+ }
87
+
88
+ // Expect all options to be selected.
89
+ for (const option of options) {
90
+ expect(option).toHaveAttribute("aria-selected", "true");
91
+ }
92
+ });
93
+ it("only allows one selection per group when isSingleSelect is true", async () => {
94
+ const { user } = render(
95
+ <MultiSelectMenu
96
+ defaultIsOpen
97
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
98
+ >
99
+ <MenuContent>
100
+ <MenuGroup id="group" isSingleSelect={true}>
101
+ {menuItems.map(({ id, label }) => (
102
+ <MenuItem key={id} id={id}>
103
+ {label}
104
+ </MenuItem>
105
+ ))}
106
+ </MenuGroup>
107
+ </MenuContent>
108
+ </MultiSelectMenu>
109
+ );
110
+
111
+ const options = screen.getAllByRole("option");
112
+
113
+ // Click on the first option
114
+ await user
115
+ .setup({
116
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
117
+ })
118
+ .click(options[0]);
119
+
120
+ // Confirm it is selected
121
+ await waitFor(() =>
122
+ expect(options[0]).toHaveAttribute("aria-selected", "true")
123
+ );
124
+
125
+ // Click on the second option
126
+ await user
127
+ .setup({
128
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
129
+ })
130
+ .click(options[1]);
131
+
132
+ // Expect only the most recently clicked option to be selected.
133
+ await waitFor(() =>
134
+ expect(options[1]).toHaveAttribute("aria-selected", "true")
135
+ );
136
+ await waitFor(() =>
137
+ expect(options[0]).toHaveAttribute("aria-selected", "false")
138
+ );
139
+ });
140
+ it("only allows one selection per group in SingleSelectMenu, even if isSingleSelect is false", async () => {
141
+ const { user } = render(
142
+ <SingleSelectMenu
143
+ defaultIsOpen
144
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
145
+ >
146
+ <MenuContent>
147
+ <MenuGroup id="group" isSingleSelect={false}>
148
+ {menuItems.map(({ id, label }) => (
149
+ <MenuItem key={id} id={id}>
150
+ {label}
151
+ </MenuItem>
152
+ ))}
153
+ </MenuGroup>
154
+ </MenuContent>
155
+ </SingleSelectMenu>
156
+ );
157
+
158
+ const options = screen.getAllByRole("option");
159
+
160
+ // Click on each option
161
+ for (const option of screen.getAllByRole("option")) {
162
+ await user
163
+ .setup({
164
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
165
+ })
166
+ .click(option);
167
+ }
168
+
169
+ // Expect only the last option to be selected.
170
+ await waitFor(() =>
171
+ expect(options[0]).toHaveAttribute("aria-selected", "false")
172
+ );
173
+ await waitFor(() =>
174
+ expect(options[1]).toHaveAttribute("aria-selected", "false")
175
+ );
176
+ await waitFor(() =>
177
+ expect(options[2]).toHaveAttribute("aria-selected", "true")
178
+ );
179
+ });
22
180
  });
23
181
  describe("API", () => {
24
182
  it("renders a group with a title", () => {
@@ -29,7 +187,7 @@ describe("MenuGroup", () => {
29
187
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
30
188
  >
31
189
  <MenuContent>
32
- <MenuGroup title={menuTitle}>
190
+ <MenuGroup title={menuTitle} id={menuTitle}>
33
191
  {menuItems.map(({ id, label }) => (
34
192
  <MenuItem key={id} id={id}>
35
193
  {label}
@@ -55,7 +213,7 @@ describe("MenuGroup", () => {
55
213
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
56
214
  >
57
215
  <MenuContent>
58
- <MenuGroup>
216
+ <MenuGroup id="group">
59
217
  {menuItems.map(({ id, label }) => (
60
218
  <MenuItem key={id} id={id}>
61
219
  {label}
@@ -78,7 +236,7 @@ describe("MenuGroup", () => {
78
236
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
79
237
  >
80
238
  <MenuContent>
81
- <MenuGroup>
239
+ <MenuGroup id="group">
82
240
  {menuItems.map(({ id, label }) => (
83
241
  <MenuItem key={id} id={id}>
84
242
  {label}
@@ -4,41 +4,55 @@ import { Text } from "@sproutsocial/seeds-react-text";
4
4
  export const MenuGroupTypeTest = () => (
5
5
  <>
6
6
  {/* MenuGroup allows children */}
7
- <MenuGroup>Test</MenuGroup>
7
+ <MenuGroup id="group">Test</MenuGroup>
8
8
  {/* MenuGroup allows children with MenuItems */}
9
- <MenuGroup>
9
+ <MenuGroup id="group">
10
10
  <MenuItem id="1">Test 1</MenuItem>
11
11
  <MenuItem id="2">Test 2</MenuItem>
12
12
  </MenuGroup>
13
+ {/* MenuGroup allows isSingleSelect prop */}
14
+ <MenuGroup id="group-1" isSingleSelect={true}>
15
+ <MenuItem id="1">Test 1</MenuItem>
16
+ </MenuGroup>
13
17
  {/* MenuGroup allows title as a string */}
14
- <MenuGroup title="title">Test</MenuGroup>
18
+ <MenuGroup id="group" title="title">
19
+ Test
20
+ </MenuGroup>
15
21
  {/* MenuGroup allows titleAs as a string */}
16
- <MenuGroup title="title" titleAs="h3">
22
+ <MenuGroup id="group" title="title" titleAs="h3">
17
23
  Test
18
24
  </MenuGroup>
19
25
  {/* MenuGroup allows titleAs as a component */}
20
- <MenuGroup title="title" titleAs={Text}>
26
+ <MenuGroup id="group" title="title" titleAs={Text}>
21
27
  Test
22
28
  </MenuGroup>
23
29
  {/* MenuGroup accepts ul props */}
24
- <MenuGroup aria-label="test label">Test</MenuGroup>
30
+ <MenuGroup id="group" aria-label="test label">
31
+ Test
32
+ </MenuGroup>
25
33
  {/* MenuGroup accepts style props */}
26
- <MenuGroup border="1px solid black" bg="white" width="150px">
34
+ <MenuGroup id="group" border="1px solid black" bg="white" width="150px">
27
35
  Test
28
36
  </MenuGroup>
37
+ {/* MenuGroup allows children prop */}
38
+ <MenuGroup id="group">Test</MenuGroup>
39
+ {/* MenuGroup allows items prop */}
40
+ <MenuGroup id="group" items={[{ id: "1", children: "1" }]} />
29
41
 
30
- {/* @ts-expect-error - MenuGroup requires children */}
31
- <MenuGroup />
32
42
  {/* @ts-expect-error - MenuGroup title is invalid as number */}
33
- <MenuGroup title={123}>Test</MenuGroup>
43
+ <MenuGroup id="group" title={123}>
44
+ Test
45
+ </MenuGroup>
34
46
  {/* @ts-expect-error - MenuGroup title is invalid as boolean */}
35
- <MenuGroup title={false}>Test</MenuGroup>
47
+ <MenuGroup id="group" title={false}>
48
+ Test
49
+ </MenuGroup>
36
50
  {/* @ts-expect-error - MenuGroup titleAs is invalid as number */}
37
- <MenuGroup title="title" titleAs={123}>
51
+ <MenuGroup id="group" title="title" titleAs={123}>
38
52
  Test
39
53
  </MenuGroup>
40
54
  {/* @ts-expect-error - MenuGroup titleAs is invalid as boolean */}
41
- <MenuGroup title="title" titleAs={false}>
55
+ <MenuGroup id="group" title="title" titleAs={false}>
42
56
  Test
43
57
  </MenuGroup>
44
58
  </>
@@ -1,5 +1,5 @@
1
- import React, { useContext } from "react";
2
- import { MenuContext } from "../MenuContext";
1
+ import { useMemo } from "react";
2
+ import { useMenuContext } from "../MenuContext";
3
3
  import type { TypeDefaultItemProps } from "../types";
4
4
  import type { TypeMenuItemProps } from "./MenuItemTypes";
5
5
  import { MenuItemContainer, StyledMenuItem } from "./styles";
@@ -24,16 +24,19 @@ export const MenuItem = ({
24
24
  ...props
25
25
  }: TypeMenuItemProps) => {
26
26
  const { getItemProps, itemsMap, highlightedIndex, isListbox } =
27
- useContext(MenuContext);
27
+ useMenuContext();
28
28
  const { ...optionProps } = useOptionProps({
29
29
  id,
30
30
  children,
31
31
  ...props,
32
32
  });
33
33
  const item = (itemsMap[id] || { index: 0, id }) as TypeDefaultItemProps;
34
+
34
35
  const highlighted = highlightedIndex === item?.index;
35
36
 
36
- return (
37
+ return item.hidden ? (
38
+ <></>
39
+ ) : (
37
40
  <MenuItemContainer role="none">
38
41
  <StyledMenuItem
39
42
  id={id}
package/src/MenuLabel.tsx CHANGED
@@ -1,12 +1,12 @@
1
1
  import React from "react";
2
- import { Text, type TypeTextProps } from "@sproutsocial/seeds-react-text";
2
+ import { Label, type TypeLabelProps } from "@sproutsocial/seeds-react-label";
3
3
  import { useMenuContext } from "./MenuContext";
4
4
 
5
- export const MenuLabel = ({ children, ...rest }: TypeTextProps) => {
5
+ export const MenuLabel = ({ children, ...rest }: Partial<TypeLabelProps>) => {
6
6
  const { getLabelProps } = useMenuContext();
7
7
  return (
8
- <Text as="label" color="text.body" {...getLabelProps?.()} {...rest}>
8
+ <Label htmlFor="fallback_menu_label" {...getLabelProps?.()} {...rest}>
9
9
  {children}
10
- </Text>
10
+ </Label>
11
11
  );
12
12
  };