@sproutsocial/seeds-react-menu 1.6.16 → 1.7.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 +8 -8
- package/CHANGELOG.md +23 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
- package/src/MenuToggleButton/MenuToggleButton.tsx +15 -0
- package/src/MenuToggleButton/__tests__/MenuToggleButton.test.tsx +495 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-menu",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Seeds React Menu",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
"test:watch": "jest --watch --coverage=false"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@sproutsocial/seeds-react-box": "^1.1.
|
|
21
|
-
"@sproutsocial/seeds-react-button": "^1.3.
|
|
22
|
-
"@sproutsocial/seeds-react-checkbox": "^1.3.
|
|
23
|
-
"@sproutsocial/seeds-react-icon": "^2.0.
|
|
24
|
-
"@sproutsocial/seeds-react-input": "^1.4.
|
|
25
|
-
"@sproutsocial/seeds-react-label": "^1.0.
|
|
26
|
-
"@sproutsocial/seeds-react-popout": "^2.4.
|
|
27
|
-
"@sproutsocial/seeds-react-radio": "^1.3.
|
|
28
|
-
"@sproutsocial/seeds-react-switch": "^1.2.
|
|
20
|
+
"@sproutsocial/seeds-react-box": "^1.1.7",
|
|
21
|
+
"@sproutsocial/seeds-react-button": "^1.3.6",
|
|
22
|
+
"@sproutsocial/seeds-react-checkbox": "^1.3.9",
|
|
23
|
+
"@sproutsocial/seeds-react-icon": "^2.0.1",
|
|
24
|
+
"@sproutsocial/seeds-react-input": "^1.4.12",
|
|
25
|
+
"@sproutsocial/seeds-react-label": "^1.0.6",
|
|
26
|
+
"@sproutsocial/seeds-react-popout": "^2.4.12",
|
|
27
|
+
"@sproutsocial/seeds-react-radio": "^1.3.5",
|
|
28
|
+
"@sproutsocial/seeds-react-switch": "^1.2.9",
|
|
29
29
|
"@sproutsocial/seeds-react-system-props": "^3.0.2",
|
|
30
|
-
"@sproutsocial/seeds-react-theme": "^3.2.
|
|
31
|
-
"@sproutsocial/seeds-react-token-input": "^1.4.
|
|
30
|
+
"@sproutsocial/seeds-react-theme": "^3.2.1",
|
|
31
|
+
"@sproutsocial/seeds-react-token-input": "^1.4.12",
|
|
32
32
|
"@sproutsocial/seeds-react-utilities": "^4.3.0",
|
|
33
33
|
"downshift": "9.0.4",
|
|
34
34
|
"lodash.uniqueid": "^4.0.1",
|
|
35
35
|
"motion": "^12.6.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@sproutsocial/seeds-react-tooltip": "^1.0.
|
|
38
|
+
"@sproutsocial/seeds-react-tooltip": "^1.0.11",
|
|
39
39
|
"@sproutsocial/eslint-config-seeds": "*",
|
|
40
40
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
41
41
|
"@sproutsocial/seeds-testing": "*",
|
|
@@ -42,6 +42,21 @@ export const MenuToggleButton = (props: TypeMenuToggleButtonProps) => {
|
|
|
42
42
|
...(isListbox ? {} : { role: "button", "aria-haspopup": "menu" }),
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
// Clean up default downshift aria attributes
|
|
46
|
+
// If there is not an active descendant, remove the attribute
|
|
47
|
+
if (toggleButtonProps && toggleButtonProps["aria-activedescendant"] === "") {
|
|
48
|
+
toggleButtonProps["aria-activedescendant"] = undefined;
|
|
49
|
+
}
|
|
50
|
+
// If the menu is closed, remove the aria-controls attribute
|
|
51
|
+
if (toggleButtonProps && toggleButtonProps["aria-expanded"] === false) {
|
|
52
|
+
// @ts-expect-error downshift types require aria-controls even though it is not required
|
|
53
|
+
toggleButtonProps["aria-controls"] = undefined;
|
|
54
|
+
}
|
|
55
|
+
// Remove aria-labelledby, we add our own Label component
|
|
56
|
+
if (toggleButtonProps) {
|
|
57
|
+
toggleButtonProps["aria-labelledby"] = undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
45
60
|
return (
|
|
46
61
|
<Button appearance="secondary" {...toggleButtonProps} {...restProps} />
|
|
47
62
|
);
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
render,
|
|
4
|
+
screen,
|
|
5
|
+
fireEvent,
|
|
6
|
+
} from "@sproutsocial/seeds-react-testing-library";
|
|
7
|
+
import { MenuToggleButton } from "../MenuToggleButton";
|
|
8
|
+
import { MenuToggleContext } from "../../MenuContext";
|
|
9
|
+
import type { TypeMenuToggleContext } from "../../MenuContext";
|
|
10
|
+
|
|
11
|
+
describe("MenuToggleButton", () => {
|
|
12
|
+
describe("aria attribute logic", () => {
|
|
13
|
+
it("should add role='button' and aria-haspopup='menu' when isListbox is false", () => {
|
|
14
|
+
const mockContext: TypeMenuToggleContext = {
|
|
15
|
+
items: [],
|
|
16
|
+
itemsMap: {},
|
|
17
|
+
isItemSelected: jest.fn(),
|
|
18
|
+
getInputComponentProps: jest.fn(),
|
|
19
|
+
isListbox: false,
|
|
20
|
+
// @ts-expect-error partial mock for testing
|
|
21
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
22
|
+
"aria-expanded": true,
|
|
23
|
+
...props,
|
|
24
|
+
})),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
render(
|
|
28
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
29
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
30
|
+
</MenuToggleContext.Provider>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
34
|
+
expect(button).toHaveAttribute("role", "button");
|
|
35
|
+
expect(button).toHaveAttribute("aria-haspopup", "menu");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should not add role='button' or aria-haspopup when isListbox is true", () => {
|
|
39
|
+
const mockContext: TypeMenuToggleContext = {
|
|
40
|
+
items: [],
|
|
41
|
+
itemsMap: {},
|
|
42
|
+
isItemSelected: jest.fn(),
|
|
43
|
+
getInputComponentProps: jest.fn(),
|
|
44
|
+
isListbox: true,
|
|
45
|
+
// @ts-expect-error partial mock for testing
|
|
46
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
47
|
+
"aria-expanded": true,
|
|
48
|
+
...props,
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
render(
|
|
53
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
54
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
55
|
+
</MenuToggleContext.Provider>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
59
|
+
// Button role comes from the Button component itself, not from our override
|
|
60
|
+
expect(button).not.toHaveAttribute("aria-haspopup", "menu");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should remove aria-activedescendant when it is an empty string", () => {
|
|
64
|
+
const mockContext: TypeMenuToggleContext = {
|
|
65
|
+
items: [],
|
|
66
|
+
itemsMap: {},
|
|
67
|
+
isItemSelected: jest.fn(),
|
|
68
|
+
getInputComponentProps: jest.fn(),
|
|
69
|
+
isListbox: true,
|
|
70
|
+
// @ts-expect-error partial mock for testing
|
|
71
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
72
|
+
"aria-expanded": true,
|
|
73
|
+
"aria-activedescendant": "",
|
|
74
|
+
...props,
|
|
75
|
+
})),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
render(
|
|
79
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
80
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
81
|
+
</MenuToggleContext.Provider>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
85
|
+
expect(button).not.toHaveAttribute("aria-activedescendant");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should keep aria-activedescendant when it has a value", () => {
|
|
89
|
+
const mockContext: TypeMenuToggleContext = {
|
|
90
|
+
items: [],
|
|
91
|
+
itemsMap: {},
|
|
92
|
+
isItemSelected: jest.fn(),
|
|
93
|
+
getInputComponentProps: jest.fn(),
|
|
94
|
+
isListbox: true,
|
|
95
|
+
// @ts-expect-error partial mock for testing
|
|
96
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
97
|
+
"aria-expanded": true,
|
|
98
|
+
"aria-activedescendant": "menu-item-1",
|
|
99
|
+
...props,
|
|
100
|
+
})),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
render(
|
|
104
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
105
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
106
|
+
</MenuToggleContext.Provider>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
110
|
+
expect(button).toHaveAttribute("aria-activedescendant", "menu-item-1");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should remove aria-controls when aria-expanded is false", () => {
|
|
114
|
+
const mockContext: TypeMenuToggleContext = {
|
|
115
|
+
items: [],
|
|
116
|
+
itemsMap: {},
|
|
117
|
+
isItemSelected: jest.fn(),
|
|
118
|
+
getInputComponentProps: jest.fn(),
|
|
119
|
+
isListbox: true,
|
|
120
|
+
// @ts-expect-error partial mock for testing
|
|
121
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
122
|
+
"aria-expanded": false,
|
|
123
|
+
"aria-controls": "menu-content",
|
|
124
|
+
...props,
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
render(
|
|
129
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
130
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
131
|
+
</MenuToggleContext.Provider>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
135
|
+
expect(button).toHaveAttribute("aria-expanded", "false");
|
|
136
|
+
expect(button).not.toHaveAttribute("aria-controls");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should keep aria-controls when aria-expanded is true", () => {
|
|
140
|
+
const mockContext: TypeMenuToggleContext = {
|
|
141
|
+
items: [],
|
|
142
|
+
itemsMap: {},
|
|
143
|
+
isItemSelected: jest.fn(),
|
|
144
|
+
getInputComponentProps: jest.fn(),
|
|
145
|
+
isListbox: true,
|
|
146
|
+
// @ts-expect-error partial mock for testing
|
|
147
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
148
|
+
"aria-expanded": true,
|
|
149
|
+
"aria-controls": "menu-content",
|
|
150
|
+
...props,
|
|
151
|
+
})),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
render(
|
|
155
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
156
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
157
|
+
</MenuToggleContext.Provider>
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
161
|
+
expect(button).toHaveAttribute("aria-expanded", "true");
|
|
162
|
+
expect(button).toHaveAttribute("aria-controls", "menu-content");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should always remove aria-labelledby", () => {
|
|
166
|
+
const mockContext: TypeMenuToggleContext = {
|
|
167
|
+
items: [],
|
|
168
|
+
itemsMap: {},
|
|
169
|
+
isItemSelected: jest.fn(),
|
|
170
|
+
getInputComponentProps: jest.fn(),
|
|
171
|
+
isListbox: true,
|
|
172
|
+
// @ts-expect-error partial mock for testing
|
|
173
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
174
|
+
"aria-expanded": true,
|
|
175
|
+
"aria-labelledby": "some-label-id",
|
|
176
|
+
...props,
|
|
177
|
+
})),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
render(
|
|
181
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
182
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
183
|
+
</MenuToggleContext.Provider>
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
187
|
+
expect(button).not.toHaveAttribute("aria-labelledby");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should handle all aria attribute cleanups together", () => {
|
|
191
|
+
const mockContext: TypeMenuToggleContext = {
|
|
192
|
+
items: [],
|
|
193
|
+
itemsMap: {},
|
|
194
|
+
isItemSelected: jest.fn(),
|
|
195
|
+
getInputComponentProps: jest.fn(),
|
|
196
|
+
isListbox: false,
|
|
197
|
+
// @ts-expect-error partial mock for testing
|
|
198
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
199
|
+
"aria-expanded": false,
|
|
200
|
+
"aria-controls": "menu-content",
|
|
201
|
+
"aria-activedescendant": "",
|
|
202
|
+
"aria-labelledby": "some-label-id",
|
|
203
|
+
...props,
|
|
204
|
+
})),
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
render(
|
|
208
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
209
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
210
|
+
</MenuToggleContext.Provider>
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
214
|
+
|
|
215
|
+
// Should have role and haspopup because isListbox is false
|
|
216
|
+
expect(button).toHaveAttribute("role", "button");
|
|
217
|
+
expect(button).toHaveAttribute("aria-haspopup", "menu");
|
|
218
|
+
|
|
219
|
+
// Should clean up invalid aria attributes
|
|
220
|
+
expect(button).not.toHaveAttribute("aria-controls");
|
|
221
|
+
expect(button).not.toHaveAttribute("aria-activedescendant");
|
|
222
|
+
expect(button).not.toHaveAttribute("aria-labelledby");
|
|
223
|
+
|
|
224
|
+
// Should keep aria-expanded
|
|
225
|
+
expect(button).toHaveAttribute("aria-expanded", "false");
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe("user event handlers", () => {
|
|
230
|
+
it("should call user-supplied onClick handler", () => {
|
|
231
|
+
const userOnClick = jest.fn();
|
|
232
|
+
const mockContext: TypeMenuToggleContext = {
|
|
233
|
+
items: [],
|
|
234
|
+
itemsMap: {},
|
|
235
|
+
isItemSelected: jest.fn(),
|
|
236
|
+
getInputComponentProps: jest.fn(),
|
|
237
|
+
isListbox: true,
|
|
238
|
+
// @ts-expect-error partial mock for testing
|
|
239
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
240
|
+
"aria-expanded": false,
|
|
241
|
+
onClick: (e) => {
|
|
242
|
+
// @ts-expect-error partial mock for testing
|
|
243
|
+
props.onClick?.(e);
|
|
244
|
+
},
|
|
245
|
+
...props,
|
|
246
|
+
})),
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
render(
|
|
250
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
251
|
+
<MenuToggleButton onClick={userOnClick}>Toggle Menu</MenuToggleButton>
|
|
252
|
+
</MenuToggleContext.Provider>
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
256
|
+
button.click();
|
|
257
|
+
|
|
258
|
+
expect(userOnClick).toHaveBeenCalledTimes(1);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should call user-supplied onKeyDown handler", () => {
|
|
262
|
+
const userOnKeyDown = jest.fn();
|
|
263
|
+
const mockContext: TypeMenuToggleContext = {
|
|
264
|
+
items: [],
|
|
265
|
+
itemsMap: {},
|
|
266
|
+
isItemSelected: jest.fn(),
|
|
267
|
+
getInputComponentProps: jest.fn(),
|
|
268
|
+
isListbox: true,
|
|
269
|
+
// @ts-expect-error partial mock for testing
|
|
270
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
271
|
+
"aria-expanded": false,
|
|
272
|
+
onKeyDown: (e) => {
|
|
273
|
+
// @ts-expect-error partial mock for testing
|
|
274
|
+
props.onKeyDown?.(e);
|
|
275
|
+
},
|
|
276
|
+
...props,
|
|
277
|
+
})),
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
render(
|
|
281
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
282
|
+
<MenuToggleButton onKeyDown={userOnKeyDown}>
|
|
283
|
+
Toggle Menu
|
|
284
|
+
</MenuToggleButton>
|
|
285
|
+
</MenuToggleContext.Provider>
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
289
|
+
|
|
290
|
+
// Simulate keydown using fireEvent
|
|
291
|
+
fireEvent.keyDown(button, { key: "Enter" });
|
|
292
|
+
|
|
293
|
+
expect(userOnKeyDown).toHaveBeenCalled();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("should call user-supplied onBlur handler", () => {
|
|
297
|
+
const userOnBlur = jest.fn();
|
|
298
|
+
const mockContext: TypeMenuToggleContext = {
|
|
299
|
+
items: [],
|
|
300
|
+
itemsMap: {},
|
|
301
|
+
isItemSelected: jest.fn(),
|
|
302
|
+
getInputComponentProps: jest.fn(),
|
|
303
|
+
isListbox: true,
|
|
304
|
+
// @ts-expect-error partial mock for testing
|
|
305
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
306
|
+
"aria-expanded": false,
|
|
307
|
+
onBlur: (e) => {
|
|
308
|
+
// @ts-expect-error partial mock for testing
|
|
309
|
+
props.onBlur?.(e);
|
|
310
|
+
},
|
|
311
|
+
...props,
|
|
312
|
+
})),
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
render(
|
|
316
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
317
|
+
<MenuToggleButton onBlur={userOnBlur}>Toggle Menu</MenuToggleButton>
|
|
318
|
+
</MenuToggleContext.Provider>
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
322
|
+
button.focus();
|
|
323
|
+
button.blur();
|
|
324
|
+
|
|
325
|
+
expect(userOnBlur).toHaveBeenCalled();
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe("getDropdownProps integration", () => {
|
|
330
|
+
it("should use getDropdownProps when provided", () => {
|
|
331
|
+
const mockGetDropdownProps = jest.fn((props) => ({
|
|
332
|
+
...props,
|
|
333
|
+
"data-testid": "dropdown-test",
|
|
334
|
+
}));
|
|
335
|
+
|
|
336
|
+
const mockContext: TypeMenuToggleContext = {
|
|
337
|
+
items: [],
|
|
338
|
+
itemsMap: {},
|
|
339
|
+
isItemSelected: jest.fn(),
|
|
340
|
+
getInputComponentProps: jest.fn(),
|
|
341
|
+
isListbox: true,
|
|
342
|
+
// @ts-expect-error partial mock for testing
|
|
343
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
344
|
+
"aria-expanded": false,
|
|
345
|
+
...props,
|
|
346
|
+
})),
|
|
347
|
+
getDropdownProps: mockGetDropdownProps,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
render(
|
|
351
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
352
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
353
|
+
</MenuToggleContext.Provider>
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
expect(mockGetDropdownProps).toHaveBeenCalledWith(
|
|
357
|
+
expect.objectContaining({
|
|
358
|
+
preventKeyAction: true,
|
|
359
|
+
})
|
|
360
|
+
);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should fallback to defaultDropdownProps when getDropdownProps is not provided", () => {
|
|
364
|
+
const mockGetToggleButtonProps = jest.fn((props) => ({
|
|
365
|
+
"aria-expanded": false,
|
|
366
|
+
...props,
|
|
367
|
+
}));
|
|
368
|
+
|
|
369
|
+
const mockContext: TypeMenuToggleContext = {
|
|
370
|
+
items: [],
|
|
371
|
+
itemsMap: {},
|
|
372
|
+
isItemSelected: jest.fn(),
|
|
373
|
+
getInputComponentProps: jest.fn(),
|
|
374
|
+
isListbox: true,
|
|
375
|
+
getToggleButtonProps: mockGetToggleButtonProps,
|
|
376
|
+
getDropdownProps: undefined,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
render(
|
|
380
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
381
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
382
|
+
</MenuToggleContext.Provider>
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
// Should still render successfully
|
|
386
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
387
|
+
expect(button).toBeInTheDocument();
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe("ariaProps integration", () => {
|
|
392
|
+
it("should merge ariaProps from context", () => {
|
|
393
|
+
const mockContext: TypeMenuToggleContext = {
|
|
394
|
+
items: [],
|
|
395
|
+
itemsMap: {},
|
|
396
|
+
isItemSelected: jest.fn(),
|
|
397
|
+
getInputComponentProps: jest.fn(),
|
|
398
|
+
isListbox: true,
|
|
399
|
+
// @ts-expect-error partial mock for testing
|
|
400
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
401
|
+
"aria-expanded": true,
|
|
402
|
+
...props,
|
|
403
|
+
})),
|
|
404
|
+
ariaProps: {
|
|
405
|
+
"aria-describedby": "menu-description",
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
render(
|
|
410
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
411
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
412
|
+
</MenuToggleContext.Provider>
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
416
|
+
expect(button).toHaveAttribute("aria-describedby", "menu-description");
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
describe("Button props forwarding", () => {
|
|
421
|
+
it("should forward appearance prop to Button and render successfully", () => {
|
|
422
|
+
const mockContext: TypeMenuToggleContext = {
|
|
423
|
+
items: [],
|
|
424
|
+
itemsMap: {},
|
|
425
|
+
isItemSelected: jest.fn(),
|
|
426
|
+
getInputComponentProps: jest.fn(),
|
|
427
|
+
isListbox: true,
|
|
428
|
+
// @ts-expect-error partial mock for testing
|
|
429
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
430
|
+
"aria-expanded": false,
|
|
431
|
+
...props,
|
|
432
|
+
})),
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
render(
|
|
436
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
437
|
+
<MenuToggleButton appearance="primary">Toggle Menu</MenuToggleButton>
|
|
438
|
+
</MenuToggleContext.Provider>
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
// Should render successfully with appearance prop
|
|
442
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
443
|
+
expect(button).toBeInTheDocument();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it("should render with secondary appearance by default", () => {
|
|
447
|
+
const mockContext: TypeMenuToggleContext = {
|
|
448
|
+
items: [],
|
|
449
|
+
itemsMap: {},
|
|
450
|
+
isItemSelected: jest.fn(),
|
|
451
|
+
getInputComponentProps: jest.fn(),
|
|
452
|
+
isListbox: true,
|
|
453
|
+
// @ts-expect-error partial mock for testing
|
|
454
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
455
|
+
"aria-expanded": false,
|
|
456
|
+
...props,
|
|
457
|
+
})),
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
render(
|
|
461
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
462
|
+
<MenuToggleButton>Toggle Menu</MenuToggleButton>
|
|
463
|
+
</MenuToggleContext.Provider>
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
// Should render successfully with default appearance
|
|
467
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
468
|
+
expect(button).toBeInTheDocument();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("should forward other Button props like disabled", () => {
|
|
472
|
+
const mockContext: TypeMenuToggleContext = {
|
|
473
|
+
items: [],
|
|
474
|
+
itemsMap: {},
|
|
475
|
+
isItemSelected: jest.fn(),
|
|
476
|
+
getInputComponentProps: jest.fn(),
|
|
477
|
+
isListbox: true,
|
|
478
|
+
// @ts-expect-error partial mock for testing
|
|
479
|
+
getToggleButtonProps: jest.fn((props) => ({
|
|
480
|
+
"aria-expanded": false,
|
|
481
|
+
...props,
|
|
482
|
+
})),
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
render(
|
|
486
|
+
<MenuToggleContext.Provider value={mockContext}>
|
|
487
|
+
<MenuToggleButton disabled>Toggle Menu</MenuToggleButton>
|
|
488
|
+
</MenuToggleContext.Provider>
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
const button = screen.getByRole("button", { name: "Toggle Menu" });
|
|
492
|
+
expect(button).toBeDisabled();
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
});
|