@sproutsocial/seeds-react-menu 0.3.0 → 0.4.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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +33 -0
- package/dist/esm/index.js +234 -80
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +43 -17
- package/dist/index.d.ts +43 -17
- package/dist/index.js +228 -72
- package/dist/index.js.map +1 -1
- package/package.json +12 -10
- package/src/Autocomplete/Autocomplete.feature +14 -43
- package/src/Autocomplete/Autocomplete.stories.tsx +128 -0
- package/src/Autocomplete/Autocomplete.tsx +29 -11
- package/src/Autocomplete/AutocompleteInput.tsx +51 -0
- package/src/Autocomplete/AutocompleteTypes.ts +8 -1
- package/src/Autocomplete/__tests__/Autocomplete.test.tsx +142 -89
- package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +25 -43
- package/src/Autocomplete/index.ts +1 -0
- package/src/MenuContext.tsx +11 -5
- package/src/MenuGroup/MenuGroup.feature +6 -24
- package/src/MenuGroup/MenuGroup.tsx +12 -1
- package/src/MenuGroup/MenuGroupTypes.ts +4 -1
- package/src/MenuGroup/__tests__/MenuGroup.test.tsx +41 -18
- package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +4 -2
- package/src/MenuItem/MenuItem.tsx +8 -5
- package/src/MenuLabel.tsx +4 -4
- package/src/MenuRoot/MenuRoot.tsx +5 -3
- package/src/SingleSelectMenu/SingleSelectMenu.tsx +2 -2
- package/src/__fixtures__/menu-test-data.ts +36 -0
- package/src/__tests__/Menu.test.tsx +29 -96
- package/src/hooks/useItemsFromChildren.tsx +70 -27
- package/src/index.ts +1 -0
- package/src/reducers/useComboboxStateReducer.tsx +11 -8
- package/src/storybookUtilities/menu-storybook-components.tsx +39 -0
- package/src/types.ts +1 -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
|
|
4
|
+
export const AutocompleteTypeTest = () => (
|
|
10
5
|
<>
|
|
11
|
-
{/* Valid
|
|
12
|
-
<
|
|
13
|
-
{/* Valid
|
|
14
|
-
<
|
|
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
|
-
</
|
|
17
|
-
{/* Valid
|
|
18
|
-
<
|
|
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
|
-
</
|
|
23
|
-
{/* Valid
|
|
24
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
49
|
-
<
|
|
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
|
-
</
|
|
41
|
+
</Autocomplete>
|
|
55
42
|
{/* @ts-expect-error - when no children provided */}
|
|
56
|
-
<
|
|
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 -
|
|
69
|
-
|
|
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
|
-
</
|
|
58
|
+
</Autocomplete>
|
|
77
59
|
</>
|
|
78
60
|
);
|
package/src/MenuContext.tsx
CHANGED
|
@@ -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
|
|
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}`] =
|
|
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
|
-
|
|
154
|
+
menuProps,
|
|
149
155
|
}: TypeMenuProviderProps) => {
|
|
150
|
-
const filterContext = useMenu(
|
|
156
|
+
const filterContext = useMenu(menuProps);
|
|
151
157
|
|
|
152
158
|
return (
|
|
153
159
|
<MenuContext.Provider value={{ ...filterContext }}>
|
|
@@ -61,23 +61,6 @@ Feature: Menu Group
|
|
|
61
61
|
And a valid component is provided to the customMenuItem prop
|
|
62
62
|
Then the MenuGroup will render the items using the customMenuItem component
|
|
63
63
|
|
|
64
|
-
Scenario: MenuGroup ignores the menuItemType without items prop
|
|
65
|
-
When a MenuGroup has menuItemType set
|
|
66
|
-
And is not provided with a valid items prop
|
|
67
|
-
Then the menuItemType will be ignored
|
|
68
|
-
|
|
69
|
-
Scenario Outline: MenuGroup can set the type for all MenuItems
|
|
70
|
-
When menuItemType is set to <menu item type>
|
|
71
|
-
And MenuGroup is provided with a valid items prop
|
|
72
|
-
Then each MenuItem rendered by the MenuGroup with use this type
|
|
73
|
-
|
|
74
|
-
Examples: Prop Types
|
|
75
|
-
| menu item type |
|
|
76
|
-
| undefined (default) |
|
|
77
|
-
| checkbox |
|
|
78
|
-
| radio |
|
|
79
|
-
| switch |
|
|
80
|
-
|
|
81
64
|
Scenario Outline: MenuGroup has custom content
|
|
82
65
|
When <custom content> is present at the <location>
|
|
83
66
|
Then <displayed content> will render
|
|
@@ -93,10 +76,9 @@ Feature: Menu Group
|
|
|
93
76
|
Then MenuRoot expects it to be of type <propType>
|
|
94
77
|
|
|
95
78
|
Examples: Prop Types
|
|
96
|
-
| propName
|
|
97
|
-
| children
|
|
98
|
-
| title
|
|
99
|
-
| items
|
|
100
|
-
| isSingleSelect
|
|
101
|
-
|
|
|
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/
|
|
@@ -24,16 +25,26 @@ 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,
|
|
30
31
|
isSingleSelect,
|
|
31
32
|
id,
|
|
33
|
+
items,
|
|
34
|
+
MenuItemComponent = MenuItem,
|
|
32
35
|
...rest
|
|
33
36
|
}: TypeMenuGroupProps) => {
|
|
34
37
|
// Generate a unique ID for the title
|
|
35
38
|
const titleId = `menu-group-title-${Math.random().toString(36).slice(2, 11)}`;
|
|
36
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
|
+
}
|
|
37
48
|
return (
|
|
38
49
|
<StyledMenuGroup
|
|
39
50
|
role="group"
|
|
@@ -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,
|
|
@@ -26,6 +27,8 @@ export interface TypeMenuGroupProps
|
|
|
26
27
|
id: string;
|
|
27
28
|
isSingleSelect?: boolean;
|
|
28
29
|
title?: string;
|
|
29
|
-
children
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
items?: TypeMenuItemProps[];
|
|
32
|
+
MenuItemComponent?: typeof MenuItem;
|
|
30
33
|
titleAs?: string | React.ComponentType<any>;
|
|
31
34
|
}
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
render,
|
|
3
3
|
screen,
|
|
4
4
|
waitFor,
|
|
5
|
-
fireEvent,
|
|
6
5
|
act,
|
|
7
6
|
} from "@sproutsocial/seeds-react-testing-library";
|
|
7
|
+
import { PointerEventsCheckLevel } from "@testing-library/user-event";
|
|
8
8
|
import {
|
|
9
9
|
SingleSelectMenu,
|
|
10
10
|
MultiSelectMenu,
|
|
@@ -22,8 +22,8 @@ const menuItems = [
|
|
|
22
22
|
|
|
23
23
|
describe("MenuGroup", () => {
|
|
24
24
|
describe("User Behavior", () => {
|
|
25
|
-
it("allows multiple selections per group when isSingleSelect is undefined", () => {
|
|
26
|
-
render(
|
|
25
|
+
it("allows multiple selections per group when isSingleSelect is undefined", async () => {
|
|
26
|
+
const { user } = render(
|
|
27
27
|
<MultiSelectMenu
|
|
28
28
|
defaultIsOpen
|
|
29
29
|
menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
|
|
@@ -44,7 +44,11 @@ describe("MenuGroup", () => {
|
|
|
44
44
|
|
|
45
45
|
// Click on each option
|
|
46
46
|
for (const option of screen.getAllByRole("option")) {
|
|
47
|
-
|
|
47
|
+
await user
|
|
48
|
+
.setup({
|
|
49
|
+
pointerEventsCheck: PointerEventsCheckLevel.Never,
|
|
50
|
+
})
|
|
51
|
+
.click(option);
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
// Expect all options to be selected.
|
|
@@ -52,8 +56,8 @@ describe("MenuGroup", () => {
|
|
|
52
56
|
expect(option).toHaveAttribute("aria-selected", "true");
|
|
53
57
|
}
|
|
54
58
|
});
|
|
55
|
-
it("allows multiple selections per group when isSingleSelect is false", () => {
|
|
56
|
-
render(
|
|
59
|
+
it("allows multiple selections per group when isSingleSelect is false", async () => {
|
|
60
|
+
const { user } = render(
|
|
57
61
|
<MultiSelectMenu
|
|
58
62
|
defaultIsOpen
|
|
59
63
|
menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
|
|
@@ -74,7 +78,11 @@ describe("MenuGroup", () => {
|
|
|
74
78
|
|
|
75
79
|
// Click on each option
|
|
76
80
|
for (const option of screen.getAllByRole("option")) {
|
|
77
|
-
|
|
81
|
+
await user
|
|
82
|
+
.setup({
|
|
83
|
+
pointerEventsCheck: PointerEventsCheckLevel.Never,
|
|
84
|
+
})
|
|
85
|
+
.click(option);
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
// Expect all options to be selected.
|
|
@@ -83,7 +91,7 @@ describe("MenuGroup", () => {
|
|
|
83
91
|
}
|
|
84
92
|
});
|
|
85
93
|
it("only allows one selection per group when isSingleSelect is true", async () => {
|
|
86
|
-
render(
|
|
94
|
+
const { user } = render(
|
|
87
95
|
<MultiSelectMenu
|
|
88
96
|
defaultIsOpen
|
|
89
97
|
menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
|
|
@@ -102,24 +110,35 @@ describe("MenuGroup", () => {
|
|
|
102
110
|
|
|
103
111
|
const options = screen.getAllByRole("option");
|
|
104
112
|
|
|
105
|
-
// Click on
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
113
|
+
// Click on the first option
|
|
114
|
+
await user
|
|
115
|
+
.setup({
|
|
116
|
+
pointerEventsCheck: PointerEventsCheckLevel.Never,
|
|
117
|
+
})
|
|
118
|
+
.click(options[0]);
|
|
109
119
|
|
|
110
|
-
//
|
|
120
|
+
// Confirm it is selected
|
|
111
121
|
await waitFor(() =>
|
|
112
|
-
expect(options[0]).toHaveAttribute("aria-selected", "
|
|
122
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true")
|
|
113
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.
|
|
114
133
|
await waitFor(() =>
|
|
115
|
-
expect(options[1]).toHaveAttribute("aria-selected", "
|
|
134
|
+
expect(options[1]).toHaveAttribute("aria-selected", "true")
|
|
116
135
|
);
|
|
117
136
|
await waitFor(() =>
|
|
118
|
-
expect(options[
|
|
137
|
+
expect(options[0]).toHaveAttribute("aria-selected", "false")
|
|
119
138
|
);
|
|
120
139
|
});
|
|
121
140
|
it("only allows one selection per group in SingleSelectMenu, even if isSingleSelect is false", async () => {
|
|
122
|
-
render(
|
|
141
|
+
const { user } = render(
|
|
123
142
|
<SingleSelectMenu
|
|
124
143
|
defaultIsOpen
|
|
125
144
|
menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
|
|
@@ -140,7 +159,11 @@ describe("MenuGroup", () => {
|
|
|
140
159
|
|
|
141
160
|
// Click on each option
|
|
142
161
|
for (const option of screen.getAllByRole("option")) {
|
|
143
|
-
|
|
162
|
+
await user
|
|
163
|
+
.setup({
|
|
164
|
+
pointerEventsCheck: PointerEventsCheckLevel.Never,
|
|
165
|
+
})
|
|
166
|
+
.click(option);
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
// Expect only the last option to be selected.
|
|
@@ -34,9 +34,11 @@ export const MenuGroupTypeTest = () => (
|
|
|
34
34
|
<MenuGroup id="group" border="1px solid black" bg="white" width="150px">
|
|
35
35
|
Test
|
|
36
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" }]} />
|
|
37
41
|
|
|
38
|
-
{/* @ts-expect-error - MenuGroup requires children */}
|
|
39
|
-
<MenuGroup id="group" />
|
|
40
42
|
{/* @ts-expect-error - MenuGroup title is invalid as number */}
|
|
41
43
|
<MenuGroup id="group" title={123}>
|
|
42
44
|
Test
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
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
|
-
|
|
28
|
-
const item = (itemsMap[id] || { index: 0, id }) as TypeDefaultItemProps;
|
|
27
|
+
useMenuContext();
|
|
29
28
|
const { ...optionProps } = useOptionProps({
|
|
30
29
|
id,
|
|
31
30
|
children,
|
|
32
31
|
...props,
|
|
33
32
|
});
|
|
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 {
|
|
2
|
+
import { Label, type TypeLabelProps } from "@sproutsocial/seeds-react-label";
|
|
3
3
|
import { useMenuContext } from "./MenuContext";
|
|
4
4
|
|
|
5
|
-
export const MenuLabel = ({ children, ...rest }:
|
|
5
|
+
export const MenuLabel = ({ children, ...rest }: Partial<TypeLabelProps>) => {
|
|
6
6
|
const { getLabelProps } = useMenuContext();
|
|
7
7
|
return (
|
|
8
|
-
<
|
|
8
|
+
<Label htmlFor="fallback_menu_label" {...getLabelProps?.()} {...rest}>
|
|
9
9
|
{children}
|
|
10
|
-
</
|
|
10
|
+
</Label>
|
|
11
11
|
);
|
|
12
12
|
};
|
|
@@ -12,7 +12,8 @@ export interface TypeMenuRootOwnProps {
|
|
|
12
12
|
width?: TypePopoutProps["width"];
|
|
13
13
|
}
|
|
14
14
|
export interface TypeMenuRootProps
|
|
15
|
-
extends Partial<TypeMenuProviderProps>,
|
|
15
|
+
extends Partial<TypeMenuProviderProps["menuProps"]>,
|
|
16
|
+
Pick<TypeMenuProviderProps, "children">,
|
|
16
17
|
TypeMenuRootOwnProps {}
|
|
17
18
|
|
|
18
19
|
const CustomPopoutContent = styled(Popout.Content)`
|
|
@@ -30,7 +31,7 @@ const CustomPopoutContent = styled(Popout.Content)`
|
|
|
30
31
|
export const MenuRoot = ({
|
|
31
32
|
children,
|
|
32
33
|
menuToggleElement,
|
|
33
|
-
popoutProps,
|
|
34
|
+
popoutProps: { focusLockProps, ...popoutProps } = {},
|
|
34
35
|
width = "300px",
|
|
35
36
|
...contextProps
|
|
36
37
|
}: TypeMenuRootProps) => {
|
|
@@ -43,7 +44,7 @@ export const MenuRoot = ({
|
|
|
43
44
|
);
|
|
44
45
|
|
|
45
46
|
return (
|
|
46
|
-
<MenuProvider {
|
|
47
|
+
<MenuProvider menuProps={contextProps}>
|
|
47
48
|
<Popout
|
|
48
49
|
menuPopout
|
|
49
50
|
isOpen={!!isOpen}
|
|
@@ -55,6 +56,7 @@ export const MenuRoot = ({
|
|
|
55
56
|
// correct jerking motion when scrolling while the Popout is open
|
|
56
57
|
// returnFocus: false,
|
|
57
58
|
crossFrame: false,
|
|
59
|
+
...focusLockProps,
|
|
58
60
|
}}
|
|
59
61
|
disableWrapperAria
|
|
60
62
|
{...popoutProps}
|
|
@@ -24,11 +24,11 @@ export const SingleSelectMenu = ({
|
|
|
24
24
|
stateReducer: externalStateReducer,
|
|
25
25
|
...useSelectProps
|
|
26
26
|
}: TypeSingleSelectMenuProps) => {
|
|
27
|
-
/**
|
|
27
|
+
/** Get a list of items from the children */
|
|
28
28
|
const { allMenuItems, menuItemsMap } = useItemsFromChildren({ children });
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Destructure Downshift props and call the core useSelect with
|
|
31
|
+
* Destructure Downshift props and call the core useSelect with items and state handler
|
|
32
32
|
*/
|
|
33
33
|
const { isOpen, ...useSelectReturnProps } = useSelect({
|
|
34
34
|
...defaultUseSelectProps,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
interface TypeBook {
|
|
2
|
+
id: string;
|
|
3
|
+
author: string;
|
|
4
|
+
title: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const TEST_BOOKS: TypeBook[] = [
|
|
8
|
+
{ id: "book-1", author: "Harper Lee", title: "To Kill a Mockingbird" },
|
|
9
|
+
{ id: "book-2", author: "Leo Tolstoy", title: "War and Peace" },
|
|
10
|
+
{ id: "book-3", author: "Fyodor Dostoyevsy", title: "The Idiot" },
|
|
11
|
+
{ id: "book-4", author: "Oscar Wilde", title: "A Picture of Dorian Gray" },
|
|
12
|
+
{ id: "book-5", author: "George Orwell", title: "Nineteen Eighty Four" },
|
|
13
|
+
{ id: "book-6", author: "Jane Austen", title: "Pride and Prejudice" },
|
|
14
|
+
{ id: "book-7", author: "Marcus Aurelius", title: "Meditations" },
|
|
15
|
+
{
|
|
16
|
+
id: "book-8",
|
|
17
|
+
author: "Fyodor Dostoevsky",
|
|
18
|
+
title: "The Brothers Karamazov",
|
|
19
|
+
},
|
|
20
|
+
{ id: "book-9", author: "Lev Tolstoy", title: "Anna Karenina" },
|
|
21
|
+
{
|
|
22
|
+
id: "book-10",
|
|
23
|
+
author: "Fyodor Dostoevsky",
|
|
24
|
+
title: "Crime and Punishment",
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const EXTRA_BOOKS: TypeBook[] = [
|
|
29
|
+
{ id: "book-11", author: "F. Scott Fitzgerald", title: "The Great Gatsby" },
|
|
30
|
+
{ id: "book-12", author: "J. D. Salinger", title: "The Catcher in the Rye" },
|
|
31
|
+
{ id: "book-13", author: "J. R. R. Tolkien", title: "The Lord of the Rings" },
|
|
32
|
+
{ id: "book-14", author: "John Steinbeck", title: "The Grapes of Wrath" },
|
|
33
|
+
{ id: "book-15", author: "George Orwell", title: "Animal Farm" },
|
|
34
|
+
{ id: "book-16", author: "Jack Kerouac", title: "On the Road" },
|
|
35
|
+
{ id: "book-17", author: "Aldous Huxley", title: "Brave New World" },
|
|
36
|
+
];
|