@sproutsocial/seeds-react-menu 1.0.1 → 1.1.1

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 (42) hide show
  1. package/.turbo/turbo-build.log +85 -9
  2. package/CHANGELOG.md +32 -0
  3. package/dist/esm/index.js +505 -122
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.mts +45 -16
  6. package/dist/index.d.ts +45 -16
  7. package/dist/index.js +568 -179
  8. package/dist/index.js.map +1 -1
  9. package/package.json +5 -5
  10. package/src/ActionMenu/ActionMenu.tsx +4 -3
  11. package/src/ActionMenu/ActionMenuTypes.ts +5 -4
  12. package/src/ActionMenu/__tests__/ActionMenu.test.tsx +1 -1
  13. package/src/Autocomplete/Autocomplete.stories.tsx +95 -32
  14. package/src/Autocomplete/AutocompleteTypes.ts +5 -2
  15. package/src/Autocomplete/MultiAutocomplete.tsx +3 -2
  16. package/src/Autocomplete/SingleAutocomplete.tsx +3 -3
  17. package/src/Autocomplete/index.ts +1 -0
  18. package/src/ListboxToggleButton.tsx +65 -0
  19. package/src/MenuContent/MenuContentTypes.ts +1 -2
  20. package/src/MenuContent/styles.tsx +2 -9
  21. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +1 -1
  22. package/src/MenuItem/__tests__/MenuItem.test.tsx +1 -3
  23. package/src/MenuRoot/MenuRoot.tsx +95 -15
  24. package/src/MultiSelectMenu/MultiSelectMenu.stories.tsx +0 -1
  25. package/src/MultiSelectMenu/MultiSelectMenu.tsx +3 -2
  26. package/src/MultiSelectMenu/MultiSelectMenuTypes.ts +5 -3
  27. package/src/NestedMenu/NestedMenu.feature +1 -1
  28. package/src/NestedMenu/NestedMenu.stories.tsx +17 -7
  29. package/src/NestedMenu/NestedMenu.tsx +217 -43
  30. package/src/NestedMenu/NestedMenuContext.tsx +16 -0
  31. package/src/NestedMenu/NestedMenuHeader.tsx +19 -6
  32. package/src/NestedMenu/NestedMenuPopout.tsx +55 -0
  33. package/src/NestedMenu/NestedMenuRoot.tsx +27 -0
  34. package/src/NestedMenu/NestedMenuTypes.ts +3 -2
  35. package/src/NestedMenu/__tests__/NestedMenu.test.tsx +210 -19
  36. package/src/SingleSelectMenu/SingleSelectMenu.stories.tsx +0 -1
  37. package/src/SingleSelectMenu/SingleSelectMenu.tsx +3 -2
  38. package/src/SingleSelectMenu/SingleSelectMenuTypes.ts +5 -3
  39. package/src/__tests__/Menu.test.tsx +1 -1
  40. package/src/index.ts +1 -0
  41. package/src/storybookUtilities/menu-storybook-components.tsx +11 -31
  42. package/src/types.ts +5 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-menu",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "Seeds React Menu",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -21,17 +21,17 @@
21
21
  "@sproutsocial/seeds-react-button": "^1.2.1",
22
22
  "@sproutsocial/seeds-react-checkbox": "^1.3.1",
23
23
  "@sproutsocial/seeds-react-icon": "^1.1.2",
24
- "@sproutsocial/seeds-react-input": "^1.4.1",
24
+ "@sproutsocial/seeds-react-input": "^1.4.2",
25
25
  "@sproutsocial/seeds-react-label": "^1.0.1",
26
- "@sproutsocial/seeds-react-popout": "^2.1.1",
26
+ "@sproutsocial/seeds-react-popout": "^2.2.0",
27
27
  "@sproutsocial/seeds-react-radio": "^1.3.0",
28
28
  "@sproutsocial/seeds-react-switch": "^1.2.1",
29
29
  "@sproutsocial/seeds-react-system-props": "^3.0.2",
30
30
  "@sproutsocial/seeds-react-theme": "^2.3.0",
31
- "@sproutsocial/seeds-react-token-input": "^1.3.0",
31
+ "@sproutsocial/seeds-react-token-input": "^1.4.0",
32
32
  "@sproutsocial/seeds-react-utilities": "^4.2.0",
33
33
  "downshift": "9.0.4",
34
- "motion": "^11.11.17"
34
+ "motion": "^12.6.3"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@sproutsocial/seeds-react-tooltip": "^1.0.1",
@@ -9,7 +9,7 @@ import { useMenuChildren } from "../hooks/useMenuChildren";
9
9
  import { useDownshiftDefaults } from "../hooks/useDownshiftDefaults";
10
10
  import { reduceReducers } from "../utils/reduceReducers";
11
11
  import { itemToString } from "../utils/downshiftDefaults";
12
- import { MenuRoot, type TypeMenuRootProps } from "../MenuRoot";
12
+ import { MenuRoot } from "../MenuRoot";
13
13
  import type { TypeMenuItemProps } from "../MenuItem";
14
14
 
15
15
  import type { TypeActionMenuProps } from "./ActionMenuTypes";
@@ -86,6 +86,7 @@ export const ActionMenu = <I extends TypeMenuItemProps = TypeMenuItemProps>({
86
86
  menuItems,
87
87
  MenuItemComponent,
88
88
  stateReducer: externalStateReducer,
89
+ MenuRootComponent = MenuRoot,
89
90
  ...useSelectProps
90
91
  }: TypeActionMenuProps<I>) => {
91
92
  const { allMenuItems, menuItemsMap, children } = useMenuChildren({
@@ -116,7 +117,7 @@ export const ActionMenu = <I extends TypeMenuItemProps = TypeMenuItemProps>({
116
117
  }, [useSelectReturnProps.selectedItem]);
117
118
 
118
119
  return (
119
- <MenuRoot
120
+ <MenuRootComponent
120
121
  {...{
121
122
  isListbox: false,
122
123
  items: allMenuItems,
@@ -129,7 +130,7 @@ export const ActionMenu = <I extends TypeMenuItemProps = TypeMenuItemProps>({
129
130
  }}
130
131
  >
131
132
  {children}
132
- </MenuRoot>
133
+ </MenuRootComponent>
133
134
  );
134
135
  };
135
136
 
@@ -1,15 +1,16 @@
1
- import { type TypeMenuRootOwnProps } from "../MenuRoot";
2
1
  import type { TypeDownshiftSelectProps } from "../MenuContext";
3
- import type { TypeMenuRootProps } from "../MenuRoot";
4
2
  import type { TypeMenuItemProps } from "../MenuItem";
5
- import type { TypeChildrenOrItems } from "../types";
3
+ import type {
4
+ TypeChildrenOrItems,
5
+ TypeHigherOrderComponentProps,
6
+ } from "../types";
6
7
 
7
8
  type TypeInvalidMenuProps =
8
9
  // There is no selection state on action menus.
9
10
  "initialSelectedItem" | "defaultSelectedItem" | "selectedItem";
10
11
 
11
12
  export interface TypeActionMenuBaseProps
12
- extends TypeMenuRootOwnProps,
13
+ extends TypeHigherOrderComponentProps,
13
14
  Omit<TypeDownshiftSelectProps, TypeInvalidMenuProps> {}
14
15
 
15
16
  export type TypeActionMenuProps<
@@ -5,8 +5,8 @@ import {
5
5
  screen,
6
6
  waitFor,
7
7
  act,
8
+ PointerEventsCheckLevel,
8
9
  } from "@sproutsocial/seeds-react-testing-library";
9
- import { PointerEventsCheckLevel } from "@testing-library/user-event";
10
10
  import { type UserEvent } from "@testing-library/user-event";
11
11
  import { ActionMenu } from "../ActionMenu";
12
12
  import { MenuContent, MenuItem, MenuToggleButton } from "../../index";
@@ -5,7 +5,14 @@ import { action } from "@storybook/addon-actions";
5
5
 
6
6
  import { Box } from "@sproutsocial/seeds-react-box";
7
7
  import { Icon } from "@sproutsocial/seeds-react-icon";
8
- import { MenuContent, MenuItem, MenuLabel, MenuToggleButton } from "../index";
8
+ import {
9
+ MenuContent,
10
+ MenuGroup,
11
+ MenuItem,
12
+ MenuLabel,
13
+ MenuToggleButton,
14
+ type TypeInternalItemProps,
15
+ } from "../index";
9
16
  // eslint-disable-next-line no-restricted-imports
10
17
  import { TEST_BOOKS } from "../__fixtures__/menu-test-data";
11
18
 
@@ -154,34 +161,90 @@ export const AutocompleteTokenInputExample: Story = {
154
161
  },
155
162
  };
156
163
 
157
- // export const AutocompleteControlled: Story = {
158
- // name: "Controlled State",
159
- // render: ({ inputType, ...args }) => {
160
- // const [isOpen, setIsOpen] = React.useState<boolean>(false);
161
- // const [selectedItem, setSelectedItem] =
162
- // React.useState<TypeInternalItemProps | null>(null);
163
- // return (
164
- // <Autocomplete
165
- // isOpen={isOpen}
166
- // onIsOpenChange={({ isOpen }) => setIsOpen(isOpen)}
167
- // selectedItem={selectedItem}
168
- // onSelectedItemChange={({ selectedItem: newSelectedItem }) => {
169
- // setSelectedItem(newSelectedItem);
170
- // }}
171
- // itemToString={(item) => (item ? TEST_BOOKS[item.index].title : "")}
172
- // menuToggleElement={<AutocompleteInput />}
173
- // {...(args as object)}
174
- // >
175
- // <MenuContent>
176
- // <MenuGroup>
177
- // {TEST_BOOKS.map(({ id, title }) => (
178
- // <MenuItem key={id} id={id} inputType={inputType}>
179
- // {title}
180
- // </MenuItem>
181
- // ))}
182
- // </MenuGroup>
183
- // </MenuContent>
184
- // </Autocomplete>
185
- // );
186
- // },
187
- // };
164
+ const TestControlledSingleAutocomplete = ({
165
+ ...args
166
+ }: CustomAutocompleteArgs) => {
167
+ const [isOpen, setIsOpen] = React.useState<boolean>(false);
168
+ const [selectedItem, setSelectedItem] =
169
+ React.useState<TypeInternalItemProps | null>(null);
170
+
171
+ return (
172
+ <Autocomplete
173
+ isOpen={isOpen}
174
+ onIsOpenChange={({ isOpen }) => setIsOpen(isOpen)}
175
+ selectedItem={selectedItem}
176
+ onSelectedItemChange={({ selectedItem: newSelectedItem }) => {
177
+ setSelectedItem(newSelectedItem);
178
+ }}
179
+ itemToString={(item) => (item ? TEST_BOOKS[item.index].title : "")}
180
+ menuToggleElement={<AutocompleteInput />}
181
+ {...(args as object)}
182
+ >
183
+ <MenuContent>
184
+ <MenuGroup id="books">
185
+ {TEST_BOOKS.map(({ id, title }) => (
186
+ <MenuItem key={id} id={id}>
187
+ {title}
188
+ </MenuItem>
189
+ ))}
190
+ </MenuGroup>
191
+ </MenuContent>
192
+ </Autocomplete>
193
+ );
194
+ };
195
+
196
+ export const AutocompleteControlled: Story = {
197
+ name: "Single Autocomplete Controlled State",
198
+ render: (args: CustomAutocompleteArgs) => (
199
+ <TestControlledSingleAutocomplete {...args} />
200
+ ),
201
+ };
202
+
203
+ const TestControlledMultiAutocomplete = (args: CustomAutocompleteArgs) => {
204
+ const [isOpen, setIsOpen] = React.useState<boolean>(false);
205
+ const [selectedItems, setSelectedItems] = React.useState<
206
+ TypeInternalItemProps[]
207
+ >([]);
208
+
209
+ return (
210
+ <Autocomplete
211
+ multiSelect
212
+ isOpen={isOpen}
213
+ onIsOpenChange={({ isOpen }) => setIsOpen(isOpen)}
214
+ selectedItems={selectedItems}
215
+ onSelectedItemsChange={({ selectedItems: newSelectedItems }) =>
216
+ setSelectedItems(newSelectedItems)
217
+ }
218
+ itemToString={(item) => (item ? TEST_BOOKS[item.index].title : "")}
219
+ menuToggleElement={
220
+ <>
221
+ <MenuLabel htmlFor="my-combobox" mb={300}>
222
+ Choose a book
223
+ </MenuLabel>
224
+ <AutocompleteTokenInput />
225
+ <MenuToggleButton mt={200} width="100%" p="0">
226
+ <Icon name="chevron-down" aria-label="Open Menu" />
227
+ </MenuToggleButton>
228
+ </>
229
+ }
230
+ {...(args as object)}
231
+ >
232
+ <MenuContent>
233
+ <MenuGroup id="books">
234
+ {TEST_BOOKS.map(({ id, title }) => (
235
+ <MenuItem key={id} id={id}>
236
+ {title}
237
+ </MenuItem>
238
+ ))}
239
+ </MenuGroup>
240
+ </MenuContent>
241
+ </Autocomplete>
242
+ );
243
+ };
244
+
245
+ export const MultiAutocompleteControlled: Story = {
246
+ name: "MultiAutocomplete Controlled State",
247
+ render: (args: CustomAutocompleteArgs) => (
248
+ <TestControlledMultiAutocomplete {...args} />
249
+ ),
250
+ };
@@ -6,11 +6,14 @@ import type {
6
6
  import type { TypeGetIsItemVisibleFn } from "../hooks/useItemFiltering";
7
7
  import type { TypeMenuRootProps } from "../MenuRoot";
8
8
  import type { TypeMenuItemProps } from "../MenuItem";
9
- import type { TypeChildrenOrItems } from "../types";
9
+ import type {
10
+ TypeChildrenOrItems,
11
+ TypeHigherOrderComponentProps,
12
+ } from "../types";
10
13
 
11
14
  export type TypeSingleAutocompleteProps<
12
15
  I extends TypeMenuItemProps = TypeMenuItemProps
13
- > = TypeMenuRootOwnProps &
16
+ > = TypeHigherOrderComponentProps &
14
17
  TypeChildrenOrItems<I> &
15
18
  TypeDownshiftComboboxProps & {
16
19
  getIsItemVisible?: TypeGetIsItemVisibleFn;
@@ -26,6 +26,7 @@ export const MultiAutocomplete = <
26
26
  stateReducer: externalStateReducer,
27
27
  menuItems,
28
28
  MenuItemComponent,
29
+ MenuRootComponent = MenuRoot,
29
30
  itemToString = defaultItemToString,
30
31
  itemToKey = defaultItemToKey,
31
32
  getIsItemVisible = getDefaultGetIsItemVisible(itemToString),
@@ -160,7 +161,7 @@ export const MultiAutocomplete = <
160
161
  });
161
162
 
162
163
  return (
163
- <MenuRoot
164
+ <MenuRootComponent
164
165
  {...{
165
166
  isListbox: true,
166
167
  items: allMenuItems,
@@ -183,6 +184,6 @@ export const MultiAutocomplete = <
183
184
  }}
184
185
  >
185
186
  {children}
186
- </MenuRoot>
187
+ </MenuRootComponent>
187
188
  );
188
189
  };
@@ -8,7 +8,6 @@ import { useItemFiltering } from "../hooks/useItemFiltering";
8
8
  import { MenuRoot } from "../MenuRoot";
9
9
 
10
10
  import type { TypeMenuItemProps } from "../MenuItem";
11
- import type { TypeMenuRootProps } from "../MenuRoot";
12
11
  import type { TypeAutocompleteProps } from "./AutocompleteTypes";
13
12
 
14
13
  export const SingleAutocomplete = <
@@ -17,6 +16,7 @@ export const SingleAutocomplete = <
17
16
  children: childrenProp,
18
17
  stateReducer: externalStateReducer,
19
18
  menuItems,
19
+ MenuRootComponent = MenuRoot,
20
20
  MenuItemComponent,
21
21
  itemToString = defaultItemToString,
22
22
  getIsItemVisible,
@@ -53,7 +53,7 @@ export const SingleAutocomplete = <
53
53
  });
54
54
 
55
55
  return (
56
- <MenuRoot
56
+ <MenuRootComponent
57
57
  {...{
58
58
  isListbox: true,
59
59
  items: allMenuItems,
@@ -69,6 +69,6 @@ export const SingleAutocomplete = <
69
69
  }}
70
70
  >
71
71
  {children}
72
- </MenuRoot>
72
+ </MenuRootComponent>
73
73
  );
74
74
  };
@@ -1,3 +1,4 @@
1
1
  export * from "./Autocomplete";
2
2
  export * from "./AutocompleteInput";
3
+ export * from "./AutocompleteTokenInput";
3
4
  export * from "./AutocompleteTypes";
@@ -0,0 +1,65 @@
1
+ import styled, { css } from "styled-components";
2
+ import { Icon } from "@sproutsocial/seeds-react-icon";
3
+ import {
4
+ MenuToggleButton,
5
+ type TypeMenuToggleButtonProps,
6
+ } from "./MenuToggleButton";
7
+ import { useMenuToggleContext } from "./MenuContext";
8
+
9
+ export interface TypeListboxToggleButtonProps
10
+ extends TypeMenuToggleButtonProps {
11
+ invalid?: boolean;
12
+ }
13
+
14
+ export const StyledListboxToggleButton = styled(
15
+ MenuToggleButton
16
+ )<TypeListboxToggleButtonProps>`
17
+ ${(props) =>
18
+ props.invalid &&
19
+ css`
20
+ border-color: ${(props) => props.theme.colors.form.border.error};
21
+ `}
22
+ `;
23
+
24
+ export interface TypeStyledChevronProps {
25
+ $isOpen?: boolean;
26
+ invalid?: boolean;
27
+ }
28
+
29
+ const StyledChevron = styled.div<TypeStyledChevronProps>`
30
+ margin-left: ${({ theme }) => theme.space[300]};
31
+ ${({ $isOpen }) =>
32
+ $isOpen &&
33
+ `
34
+ transform: rotate(-180deg);
35
+ `}
36
+ transition: transform 0.15s;
37
+
38
+ ${({ invalid }) =>
39
+ invalid &&
40
+ css`
41
+ color: ${(props) => props.theme.colors.icon.error};
42
+ `}
43
+ `;
44
+
45
+ const StyledInnerButton = styled.div`
46
+ display: flex;
47
+ justify-content: space-between;
48
+ `;
49
+
50
+ export const ListboxToggleButton = ({
51
+ children,
52
+ ...buttonProps
53
+ }: TypeListboxToggleButtonProps) => {
54
+ const { isOpen } = useMenuToggleContext();
55
+ return (
56
+ <StyledListboxToggleButton appearance="secondary" {...buttonProps}>
57
+ <StyledInnerButton>
58
+ {children}
59
+ <StyledChevron $isOpen={isOpen} invalid={buttonProps.invalid}>
60
+ <Icon name="chevron-down-outline" fixedWidth aria-hidden />
61
+ </StyledChevron>
62
+ </StyledInnerButton>
63
+ </StyledListboxToggleButton>
64
+ );
65
+ };
@@ -1,9 +1,8 @@
1
- import { type HTMLMotionProps } from "motion/react";
2
1
  import type { TypeMenuItemProps } from "../MenuItem";
3
2
  import type { TypeChildrenOrItems } from "../types";
4
3
 
5
4
  export type TypeMenuContentProps<
6
5
  I extends TypeMenuItemProps = TypeMenuItemProps
7
- > = Omit<HTMLMotionProps<"ul">, "children"> & {
6
+ > = Omit<React.ComponentPropsWithoutRef<"ul">, "children"> & {
8
7
  innerRef?: React.Ref<HTMLUListElement>;
9
8
  } & TypeChildrenOrItems<I>;
@@ -1,14 +1,7 @@
1
1
  import styled from "styled-components";
2
- import { motion } from "motion/react";
3
- //import { focusRing } from "@sproutsocial/seeds-react-mixins";
4
2
 
5
- export const StyledMenuContent = styled(motion.ul)`
3
+ export const StyledMenuContent = styled.ul`
6
4
  margin: 0;
7
5
  padding: 0;
6
+ outline: none;
8
7
  `;
9
-
10
- // Removing this for now because it's insanely intrusive and distracting to the user... interestingly the out of the box browser focus ring works better only appears when the content is actually focused.
11
-
12
- // &:focus {
13
- // ${focusRing}
14
- // }
@@ -3,8 +3,8 @@ import {
3
3
  screen,
4
4
  waitFor,
5
5
  act,
6
+ PointerEventsCheckLevel,
6
7
  } from "@sproutsocial/seeds-react-testing-library";
7
- import { PointerEventsCheckLevel } from "@testing-library/user-event";
8
8
  import {
9
9
  SingleSelectMenu,
10
10
  MultiSelectMenu,
@@ -5,11 +5,9 @@ import {
5
5
  render,
6
6
  waitFor,
7
7
  act,
8
- } from "@sproutsocial/seeds-react-testing-library";
9
- import {
10
8
  PointerEventsCheckLevel,
11
9
  type UserEvent,
12
- } from "@testing-library/user-event";
10
+ } from "@sproutsocial/seeds-react-testing-library";
13
11
  import type { ByRoleMatcher, ByRoleOptions } from "@testing-library/react";
14
12
  import { theme } from "@sproutsocial/seeds-react-theme";
15
13
  import {
@@ -3,12 +3,13 @@ import styled from "styled-components";
3
3
  import { Popout, type TypePopoutProps } from "@sproutsocial/seeds-react-popout";
4
4
 
5
5
  import {
6
- MenuToggleProvider,
7
6
  MenuContentProvider,
8
- useMenu,
7
+ MenuToggleProvider,
9
8
  type TypeMenuContextProps,
10
9
  type TypeMenuProviderProps,
10
+ useMenu,
11
11
  } from "../MenuContext";
12
+ import { MOTION_DURATION_FAST } from "@sproutsocial/seeds-motion/unitless";
12
13
 
13
14
  export interface TypeMenuRootOwnProps {
14
15
  menuToggleElement: React.ReactElement<any>;
@@ -22,12 +23,84 @@ export interface TypeMenuRootProps
22
23
  Pick<TypeMenuProviderProps, "children">,
23
24
  TypeMenuRootOwnProps {}
24
25
 
25
- const CustomPopoutContent = styled(Popout.Content)`
26
+ export const CustomPopoutContent = styled(Popout.Content)`
26
27
  padding: 0;
27
28
  margin-left: 0;
28
29
  margin-right: 0;
29
30
  `;
30
31
 
32
+ const menuAnimationConfig = {
33
+ initial: {
34
+ scale: 0,
35
+ opacity: 0,
36
+ originX: 0,
37
+ originY: 0,
38
+ },
39
+ animate: {
40
+ opacity: 1,
41
+ scale: 1,
42
+ originX: 0,
43
+ originY: 0,
44
+
45
+ transition: {
46
+ type: "tween",
47
+ duration: MOTION_DURATION_FAST,
48
+ ease: "circOut",
49
+ },
50
+ },
51
+ exit: {
52
+ opacity: 0,
53
+ scale: 1,
54
+ transition: {
55
+ type: "tween",
56
+ duration: MOTION_DURATION_FAST,
57
+ ease: "circIn",
58
+ },
59
+ },
60
+ };
61
+
62
+ export interface TypeMenuPopoutProps
63
+ extends Pick<Partial<TypeMenuContextProps>, "openMenu" | "closeMenu">,
64
+ TypePopoutProps {}
65
+
66
+ export const MenuPopout = ({
67
+ content,
68
+ width = "300px",
69
+ isOpen,
70
+ openMenu,
71
+ closeMenu,
72
+ focusLockProps,
73
+ ...popoutProps
74
+ }: TypeMenuPopoutProps) => {
75
+ const setIsOpen = useCallback(
76
+ (newIsOpen: boolean) => {
77
+ newIsOpen ? openMenu?.() : closeMenu?.();
78
+ },
79
+ [openMenu, closeMenu]
80
+ );
81
+
82
+ return (
83
+ <Popout
84
+ isOpen={!!isOpen}
85
+ setIsOpen={setIsOpen}
86
+ content={(props) => (
87
+ <CustomPopoutContent width={popoutProps.fullWidth ? "initial" : width}>
88
+ {typeof content === "function" ? content(props) : content}
89
+ </CustomPopoutContent>
90
+ )}
91
+ focusLockProps={{
92
+ // correct jerking motion when scrolling while the Popout is open
93
+ // returnFocus: false,
94
+ crossFrame: false,
95
+ ...focusLockProps,
96
+ }}
97
+ disableWrapperAria
98
+ animationConfig={menuAnimationConfig}
99
+ {...popoutProps}
100
+ />
101
+ );
102
+ };
103
+
31
104
  /**
32
105
  * @link https://seeds.sproutsocial.com/components/menu-root/
33
106
  *
@@ -67,23 +140,16 @@ export const MenuRoot = ({
67
140
  const menuContext = useMenu(contextProps);
68
141
 
69
142
  return (
70
- <Popout
71
- menuPopout
143
+ <MenuPopout
72
144
  placement={placement}
73
145
  isOpen={!!isOpen}
74
- setIsOpen={setIsOpen}
146
+ openMenu={openMenu}
147
+ closeMenu={closeMenu}
75
148
  content={
76
149
  <MenuContentProvider menuContext={menuContext}>
77
- <CustomPopoutContent width={width}>{children}</CustomPopoutContent>
150
+ {children}
78
151
  </MenuContentProvider>
79
152
  }
80
- focusLockProps={{
81
- // correct jerking motion when scrolling while the Popout is open
82
- // returnFocus: false,
83
- crossFrame: false,
84
- ...focusLockProps,
85
- }}
86
- disableWrapperAria
87
153
  {...popoutProps}
88
154
  >
89
155
  {(toggleProps) => (
@@ -91,6 +157,20 @@ export const MenuRoot = ({
91
157
  {menuToggleElement}
92
158
  </MenuToggleProvider>
93
159
  )}
94
- </Popout>
160
+ </MenuPopout>
161
+ );
162
+ };
163
+
164
+ export const MenuRootNoPopout = ({
165
+ children,
166
+ menuToggleElement,
167
+ ...contextProps
168
+ }: TypeMenuRootProps) => {
169
+ const menuContext = useMenu(contextProps);
170
+
171
+ return (
172
+ <MenuContentProvider menuContext={menuContext}>
173
+ {children}
174
+ </MenuContentProvider>
95
175
  );
96
176
  };
@@ -2,7 +2,6 @@
2
2
  import React from "react";
3
3
 
4
4
  import { Box } from "@sproutsocial/seeds-react-box";
5
- import { action } from "@storybook/addon-actions";
6
5
  import type { Meta, StoryObj } from "@storybook/react";
7
6
  import { StorybookMenuToggleButton } from "../storybookUtilities/menu-storybook-components";
8
7
  import { TEST_BOOKS, EXTRA_BOOKS } from "../__fixtures__/menu-test-data";
@@ -25,6 +25,7 @@ export const MultiSelectMenu = <
25
25
  MenuItemComponent,
26
26
  onSelectedItemChange: externalOnSelectedItemChange,
27
27
  stateReducer: externalStateReducer,
28
+ MenuRootComponent = MenuRoot,
28
29
  getA11yMultiStatusMessage,
29
30
  onMultiSelectStateChange,
30
31
  multiSelectStateReducer,
@@ -98,7 +99,7 @@ export const MultiSelectMenu = <
98
99
  });
99
100
 
100
101
  return (
101
- <MenuRoot
102
+ <MenuRootComponent
102
103
  {...{
103
104
  isListbox: true,
104
105
  items: allMenuItems,
@@ -120,6 +121,6 @@ export const MultiSelectMenu = <
120
121
  }}
121
122
  >
122
123
  {children}
123
- </MenuRoot>
124
+ </MenuRootComponent>
124
125
  );
125
126
  };
@@ -1,13 +1,15 @@
1
- import type { TypeMenuRootOwnProps, TypeMenuRootProps } from "../MenuRoot";
2
1
  import type {
3
2
  TypeDownshiftSelectProps,
4
3
  TypeDownshiftMultiSelectProps,
5
4
  } from "../MenuContext";
6
5
  import type { TypeMenuItemProps } from "../MenuItem";
7
- import type { TypeChildrenOrItems } from "../types";
6
+ import type {
7
+ TypeChildrenOrItems,
8
+ TypeHigherOrderComponentProps,
9
+ } from "../types";
8
10
 
9
11
  export interface TypeMultiSelectMenuBaseProps
10
- extends TypeMenuRootOwnProps,
12
+ extends TypeHigherOrderComponentProps,
11
13
  TypeDownshiftSelectProps,
12
14
  TypeDownshiftMultiSelectProps {}
13
15
 
@@ -105,5 +105,5 @@ Feature: NestedMenu
105
105
  | propName | propType |
106
106
  | --------------------- | -------------------------------------------------------------------------------- |
107
107
  | menus | Record<string, {MenuComponent, menuProps, MenuHeaderComponent, menuHeaderProps}> |
108
- | selectedMenuId | string |
108
+ | initialMenuId | string |
109
109
  | onBackButtonClick | function |