@sproutsocial/seeds-react-menu 0.1.0 → 0.2.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 (62) hide show
  1. package/.eslintignore +6 -0
  2. package/.eslintrc.js +6 -2
  3. package/.turbo/turbo-build.log +10 -10
  4. package/CHANGELOG.md +23 -1
  5. package/dist/esm/index.js +243 -217
  6. package/dist/esm/index.js.map +1 -1
  7. package/dist/index.d.mts +104 -38
  8. package/dist/index.d.ts +104 -38
  9. package/dist/index.js +272 -243
  10. package/dist/index.js.map +1 -1
  11. package/package.json +10 -7
  12. package/src/ActionMenu/ActionMenu.feature +9 -3
  13. package/src/ActionMenu/ActionMenu.tsx +76 -42
  14. package/src/ActionMenu/__tests__/ActionMenu.test.tsx +36 -42
  15. package/src/ActionMenu/__tests__/ActionMenu.typetest.tsx +13 -13
  16. package/src/MenuContent/MenuContent.tsx +31 -1
  17. package/src/MenuContent/MenuContentTypes.ts +3 -4
  18. package/src/MenuContent/styles.tsx +10 -6
  19. package/src/MenuContext.tsx +1 -6
  20. package/src/MenuFooter/MenuFooter.feature +1 -1
  21. package/src/MenuFooter/MenuFooter.tsx +8 -0
  22. package/src/MenuFooter/__tests__/MenuFooter.test.tsx +53 -26
  23. package/src/MenuFooter/styles.tsx +1 -1
  24. package/src/MenuGroup/MenuGroup.feature +14 -26
  25. package/src/MenuGroup/MenuGroup.tsx +21 -1
  26. package/src/MenuGroup/MenuGroupTypes.ts +1 -0
  27. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +41 -6
  28. package/src/MenuGroup/styles.tsx +4 -3
  29. package/src/MenuHeader/MenuHeader.tsx +16 -5
  30. package/src/MenuHeader/__tests__/MenuHeader.test.tsx +50 -6
  31. package/src/MenuHeader/__tests__/MenuHeader.typetest.tsx +1 -0
  32. package/src/MenuHeader/styles.tsx +21 -22
  33. package/src/MenuItem/MenuItem.feature +66 -26
  34. package/src/MenuItem/MenuItem.tsx +39 -20
  35. package/src/MenuItem/MenuItemTypes.ts +13 -1
  36. package/src/MenuItem/__tests__/MenuItem.test.tsx +431 -154
  37. package/src/MenuItem/__tests__/MenuItem.typetest.tsx +57 -38
  38. package/src/MenuItem/styles.tsx +44 -3
  39. package/src/{MenuItemSelectable/MenuItemSelectable.tsx → MenuItem/useOptionProps.tsx} +32 -54
  40. package/src/MenuRoot/MenuRoot.feature +20 -10
  41. package/src/MenuRoot/MenuRoot.tsx +14 -18
  42. package/src/MenuRoot/__tests__/MenuRoot.test.tsx +168 -34
  43. package/src/MenuRoot/__tests__/MenuRoot.typetest.tsx +10 -10
  44. package/src/MultiSelectMenu/MultiSelectMenu.feature +2 -13
  45. package/src/MultiSelectMenu/MultiSelectMenu.tsx +9 -1
  46. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +154 -153
  47. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.typetest.tsx +10 -10
  48. package/src/SingleSelectMenu/SingleSelectMenu.feature +11 -0
  49. package/src/SingleSelectMenu/SingleSelectMenu.tsx +10 -0
  50. package/src/SingleSelectMenu/__tests__/SingleSelectMenu.test.tsx +11 -14
  51. package/src/SingleSelectMenu/__tests__/SingleSelectMenu.typetest.tsx +14 -14
  52. package/src/__tests__/Menu.test.tsx +20 -16
  53. package/src/hooks/useItemsFromChildren.tsx +2 -4
  54. package/src/index.ts +0 -1
  55. package/src/menuTestingUtils.tsx +7 -4
  56. package/tsconfig.json +1 -1
  57. package/src/MenuItemSelectable/MenuItemSelectable.feature +0 -99
  58. package/src/MenuItemSelectable/MenuItemSelectableTypes.ts +0 -15
  59. package/src/MenuItemSelectable/__tests__/MenuItemSelectable.test.tsx +0 -303
  60. package/src/MenuItemSelectable/__tests__/MenuItemSelectable.typetest.tsx +0 -56
  61. package/src/MenuItemSelectable/index.ts +0 -5
  62. package/src/MenuItemSelectable/styles.tsx +0 -21
@@ -1,18 +1,35 @@
1
1
  /* eslint-disable testing-library/no-unnecessary-act */
2
+ import React from "react";
2
3
  import {
3
4
  screen,
4
5
  render,
5
6
  waitFor,
6
7
  } from "@sproutsocial/seeds-react-testing-library";
7
- import { PointerEventsCheckLevel } from "@testing-library/user-event";
8
+ import {
9
+ PointerEventsCheckLevel,
10
+ type UserEvent,
11
+ } from "@testing-library/user-event";
12
+ import type { ByRoleMatcher, ByRoleOptions } from "@testing-library/react";
8
13
  import { theme } from "@sproutsocial/seeds-react-theme";
9
14
  import {
15
+ ActionMenu,
10
16
  MenuItem,
11
17
  MenuToggleButton,
12
18
  MenuContent,
13
- ActionMenu,
19
+ MultiSelectMenu,
20
+ INPUT_TYPES,
14
21
  } from "../../index";
15
- import { TestWithMenuRoot } from "../../menuTestingUtils";
22
+ import { TestWithMenuRoot, TestWithMockedMenu } from "../../menuTestingUtils";
23
+
24
+ const books = [
25
+ {
26
+ index: 0,
27
+ id: "book-1",
28
+ author: "Harper Lee",
29
+ title: "To Kill a Mockingbird",
30
+ },
31
+ { index: 1, id: "book-2", author: "Lev Tolstoy", title: "War and Peace" },
32
+ ];
16
33
 
17
34
  const menuItemProps = {
18
35
  key: "book123",
@@ -31,198 +48,458 @@ const testMenuContext = {
31
48
  getItemProps: jest.fn((props) => ({ ...props })),
32
49
  isListbox: false,
33
50
  };
34
- const testTriggerElement = <MenuToggleButton>Open</MenuToggleButton>;
35
-
36
- describe("MenuItem User Behavior", () => {
37
- describe("MenuItem is clickable", () => {
38
- it("fires onClick event when clicked and not disabled", async () => {
39
- const handleClick = jest.fn();
40
- const { user } = render(
41
- <TestWithMenuRoot
42
- triggerElement={testTriggerElement}
43
- {...testMenuContext}
44
- >
45
- <MenuContent>
46
- <MenuItem {...menuItemProps} onClick={handleClick} />
47
- </MenuContent>
48
- </TestWithMenuRoot>
49
- );
51
+ const testMenuToggleElement = <MenuToggleButton>Open</MenuToggleButton>;
50
52
 
53
+ const INTERACTION_TYPES = {
54
+ click: {
55
+ action: "clicking with mouse",
56
+ activate: async (
57
+ user: UserEvent,
58
+ role: ByRoleMatcher = "option",
59
+ options: ByRoleOptions = {}
60
+ ) => {
61
+ const firstItem = screen.getByRole(role, {
62
+ name: books[0].title,
63
+ ...options,
64
+ });
65
+ await user
66
+ .setup({
67
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
68
+ })
69
+ .click(firstItem);
70
+ },
71
+ deactivate: async (
72
+ user: UserEvent,
73
+ role: ByRoleMatcher = "option",
74
+ options: ByRoleOptions = {}
75
+ ) => {
76
+ const firstItem = screen.getByRole(role, {
77
+ name: books[0].title,
78
+ ...options,
79
+ });
51
80
  await user
52
81
  .setup({
53
82
  pointerEventsCheck: PointerEventsCheckLevel.Never,
54
83
  })
55
- .click(screen.getByRole("menuitem"));
84
+ .click(firstItem);
85
+ },
86
+ },
87
+ enter: {
88
+ action: "pressing the Enter key",
89
+ activate: async (user: UserEvent) => {
90
+ await user.keyboard("{arrowdown}{enter}");
91
+ },
92
+ deactivate: async (user: UserEvent) => {
93
+ await user.keyboard("{enter}");
94
+ },
95
+ },
96
+ space: {
97
+ action: "pressing the Space key",
98
+ activate: async (user: UserEvent) => {
99
+ await user.keyboard("{arrowdown} ");
100
+ },
101
+ deactivate: async (user: UserEvent) => {
102
+ await user.keyboard(" ");
103
+ },
104
+ },
105
+ };
56
106
 
57
- await waitFor(() => expect(handleClick).toHaveBeenCalledTimes(1));
107
+ describe("MenuItem when not isListbox", () => {
108
+ describe("User Behavior", () => {
109
+ describe("is clickable", () => {
110
+ it("fires onClick event when clicked and not disabled", async () => {
111
+ const handleClick = jest.fn();
112
+ const { user } = render(
113
+ <TestWithMenuRoot
114
+ menuToggleElement={testMenuToggleElement}
115
+ {...testMenuContext}
116
+ >
117
+ <MenuContent>
118
+ <MenuItem {...menuItemProps} onClick={handleClick} />
119
+ </MenuContent>
120
+ </TestWithMenuRoot>
121
+ );
122
+
123
+ await user
124
+ .setup({
125
+ pointerEventsCheck: PointerEventsCheckLevel.Never,
126
+ })
127
+ .click(screen.getByRole("menuitem"));
128
+
129
+ await waitFor(() => expect(handleClick).toHaveBeenCalledTimes(1));
130
+ });
58
131
  });
59
- });
60
132
 
61
- describe("MenuItem can be triggered via keyboard", () => {
62
- it(`fires onClick event when enter and not disabled`, async () => {
63
- const handleClick = jest.fn();
64
- const handleStateChange = jest.fn();
133
+ describe("MenuItem can be triggered via keyboard", () => {
134
+ it(`fires onClick event when enter and not disabled`, async () => {
135
+ const handleClick = jest.fn();
136
+ const handleStateChange = jest.fn();
137
+
138
+ const { user } = render(
139
+ <ActionMenu
140
+ menuToggleElement={testMenuToggleElement}
141
+ isOpen
142
+ onStateChange={handleStateChange}
143
+ >
144
+ <MenuContent>
145
+ <MenuItem
146
+ {...menuItemProps}
147
+ data-testid="menu-test"
148
+ key={0}
149
+ onKeyDown={handleClick}
150
+ tabIndex={0}
151
+ />
152
+ </MenuContent>
153
+ </ActionMenu>
154
+ );
155
+ const targetElem = screen.getByTestId("menu-test");
156
+
157
+ await user.tab();
158
+ expect(targetElem).toHaveFocus();
159
+
160
+ await user.keyboard("{enter}");
161
+
162
+ await waitFor(() =>
163
+ expect(handleStateChange).toHaveBeenCalledWith({
164
+ isOpen: false,
165
+ type: "__togglebutton_keydown_enter__",
166
+ })
167
+ );
168
+ expect(handleClick).toHaveBeenCalled();
169
+ });
170
+ it(`fires onClick event when space and not disabled`, async () => {
171
+ const handleClick = jest.fn();
172
+ const handleStateChange = jest.fn();
173
+
174
+ const { user } = render(
175
+ <ActionMenu
176
+ menuToggleElement={testMenuToggleElement}
177
+ isOpen
178
+ onStateChange={handleStateChange}
179
+ >
180
+ <MenuContent>
181
+ <MenuItem
182
+ {...menuItemProps}
183
+ data-testid="menu-test"
184
+ key={0}
185
+ onKeyDown={handleClick}
186
+ tabIndex={0}
187
+ />
188
+ </MenuContent>
189
+ </ActionMenu>
190
+ );
191
+ const targetElem = screen.getByTestId("menu-test");
192
+
193
+ await user.tab();
194
+ expect(targetElem).toHaveFocus();
195
+
196
+ await user.keyboard(" ");
197
+
198
+ await waitFor(() =>
199
+ expect(handleStateChange).toHaveBeenCalledWith({
200
+ isOpen: false,
201
+ type: "__togglebutton_keydown_space_button__",
202
+ })
203
+ );
204
+
205
+ expect(handleClick).toHaveBeenCalled();
206
+ });
207
+ });
208
+
209
+ test.each(Object.values(INTERACTION_TYPES))(
210
+ `does not fire onClick event when disabled and $action`,
211
+ async ({ activate }) => {
212
+ const handleClick = jest.fn();
213
+
214
+ const testItem = books[0];
215
+
216
+ const { user } = render(
217
+ <ActionMenu
218
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
219
+ isOpen
220
+ >
221
+ <MenuContent>
222
+ <MenuItem id={testItem.id} disabled={true} onClick={handleClick}>
223
+ {testItem.title}
224
+ </MenuItem>
225
+ </MenuContent>
226
+ </ActionMenu>
227
+ );
65
228
 
66
- const { user } = render(
229
+ await activate(user, "menuitem");
230
+
231
+ expect(handleClick).not.toHaveBeenCalled();
232
+ }
233
+ );
234
+ });
235
+
236
+ describe("API", () => {
237
+ it("accepts children", () => {
238
+ render(
67
239
  <ActionMenu
68
- triggerElement={testTriggerElement}
240
+ id="test-menu-item-selectable"
241
+ menuToggleElement={testMenuToggleElement}
69
242
  isOpen
70
- onStateChange={handleStateChange}
71
243
  >
72
244
  <MenuContent>
73
- <MenuItem
74
- {...menuItemProps}
75
- data-testid="menu-test"
76
- key={0}
77
- onKeyDown={handleClick}
78
- tabIndex={0}
79
- />
245
+ <MenuItem onClick={() => {}} id="test">
246
+ Hello world!
247
+ <span>testing</span>
248
+ </MenuItem>
80
249
  </MenuContent>
81
250
  </ActionMenu>
82
251
  );
83
- const targetElem = screen.getByTestId("menu-test");
84
-
85
- await user.tab();
86
- expect(targetElem).toHaveFocus();
87
252
 
88
- await user.keyboard("{enter}");
89
-
90
- await waitFor(() =>
91
- expect(handleStateChange).toHaveBeenCalledWith({
92
- isOpen: false,
93
- type: "__togglebutton_keydown_enter__",
94
- })
95
- );
96
- expect(handleClick).toHaveBeenCalled();
253
+ const menuItem = screen.getByText("Hello world!");
254
+ const span = screen.getByText("testing");
255
+ expect(menuItem).toContainElement(span);
97
256
  });
98
- it(`fires onClick event when space and not disabled`, async () => {
99
- const handleClick = jest.fn();
100
- const handleStateChange = jest.fn();
101
257
 
102
- const { user } = render(
258
+ it("accepts box & system Props", () => {
259
+ render(
103
260
  <ActionMenu
104
- triggerElement={testTriggerElement}
261
+ id="test-menu-item-selectable"
262
+ menuToggleElement={testMenuToggleElement}
105
263
  isOpen
106
- onStateChange={handleStateChange}
107
264
  >
108
265
  <MenuContent>
109
- <MenuItem
110
- {...menuItemProps}
111
- data-testid="menu-test"
112
- key={0}
113
- onKeyDown={handleClick}
114
- tabIndex={0}
115
- />
266
+ <MenuItem display="flex" p={300} onClick={() => {}} id="test">
267
+ Hello world!
268
+ <span>testing</span>
269
+ </MenuItem>
116
270
  </MenuContent>
117
271
  </ActionMenu>
118
272
  );
119
- const targetElem = screen.getByTestId("menu-test");
120
273
 
121
- await user.tab();
122
- expect(targetElem).toHaveFocus();
274
+ const menuItem = screen.getByRole("menuitem");
275
+ expect(menuItem).toHaveStyle(`padding: ${theme.space[300]}`);
276
+ expect(menuItem).toHaveStyle(`display: flex`);
277
+ });
278
+ });
279
+ });
123
280
 
124
- await user.keyboard(" ");
281
+ const testByType = {
282
+ [INPUT_TYPES.CHECKBOX]: {
283
+ isSelected: () => {
284
+ expect(
285
+ screen.getByRole("checkbox", { checked: true })
286
+ ).toBeInTheDocument();
287
+ },
288
+ isNotSelected: () => {
289
+ expect(
290
+ screen.queryByRole("checkbox", { checked: true })
291
+ ).not.toBeInTheDocument();
292
+ },
293
+ },
294
+ [INPUT_TYPES.RADIO]: {
295
+ isSelected: () => {
296
+ expect(screen.getByRole("radio", { checked: true })).toBeInTheDocument();
297
+ },
298
+ isNotSelected: () => {
299
+ expect(
300
+ screen.queryByRole("radio", { checked: true })
301
+ ).not.toBeInTheDocument();
302
+ },
303
+ },
304
+ [INPUT_TYPES.SWITCH]: {
305
+ isSelected: () => {
306
+ expect(screen.getByRole("switch", { checked: true })).toBeInTheDocument();
307
+ },
308
+ isNotSelected: () => {
309
+ expect(
310
+ screen.queryByRole("switch", { checked: true })
311
+ ).not.toBeInTheDocument();
312
+ },
313
+ },
314
+ [INPUT_TYPES.ICON]: {
315
+ isSelected: () => {
316
+ expect(
317
+ screen.getByDataQaLabel({ icon: "check-solid" })
318
+ ).toBeInTheDocument();
319
+ },
320
+ isNotSelected: () => {
321
+ expect(
322
+ screen.queryByDataQaLabel({ icon: "check-solid" })
323
+ ).not.toBeInTheDocument();
324
+ },
325
+ },
326
+ };
125
327
 
126
- await waitFor(() =>
127
- expect(handleStateChange).toHaveBeenCalledWith({
128
- isOpen: false,
129
- type: "__togglebutton_keydown_space_button__",
130
- })
131
- );
328
+ describe("MenuItem when isListbox", () => {
329
+ describe("User Behavior", () => {
330
+ describe.each([
331
+ INPUT_TYPES.CHECKBOX,
332
+ INPUT_TYPES.RADIO,
333
+ INPUT_TYPES.SWITCH,
334
+ INPUT_TYPES.ICON,
335
+ ])("when the input type is %s", (inputType) => {
336
+ test.each(Object.values(INTERACTION_TYPES))(
337
+ "can be checked and unchecked when $action",
338
+ async ({ activate, deactivate }) => {
339
+ const { user } = render(
340
+ <MultiSelectMenu
341
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
342
+ defaultIsOpen
343
+ >
344
+ <MenuContent>
345
+ {books.map((book) => {
346
+ return (
347
+ <MenuItem key={book.id} id={book.id} inputType={inputType}>
348
+ {book.title}
349
+ </MenuItem>
350
+ );
351
+ })}
352
+ </MenuContent>
353
+ </MultiSelectMenu>
354
+ );
355
+
356
+ // check that menu is visible
357
+ expect(
358
+ screen.getByRole("option", {
359
+ name: books[0].title,
360
+ selected: false,
361
+ })
362
+ ).toBeInTheDocument();
363
+
364
+ // Check that checkboxes are rendered
365
+ if (inputType !== INPUT_TYPES.ICON)
366
+ expect(screen.getAllByRole(inputType)).toHaveLength(books.length);
367
+
368
+ // Perform the action to select the item
369
+ await activate(user);
370
+
371
+ expect(
372
+ await screen.findByRole("option", {
373
+ name: books[0].title,
374
+ selected: true,
375
+ })
376
+ ).toBeInTheDocument();
132
377
 
133
- expect(handleClick).toHaveBeenCalled();
378
+ testByType[inputType].isSelected();
379
+
380
+ // Perform the action to deselect the item
381
+ deactivate(user);
382
+
383
+ expect(
384
+ await screen.findByRole("option", {
385
+ name: books[0].title,
386
+ selected: false,
387
+ })
388
+ ).toBeInTheDocument();
389
+
390
+ testByType[inputType].isNotSelected();
391
+ }
392
+ );
134
393
  });
394
+ });
135
395
 
136
- it(`does not fire onClick event when disabled`, async () => {
137
- const handleClick = jest.fn();
138
- const handleStateChange = jest.fn();
139
-
140
- const books = [
141
- {
142
- index: 0,
143
- id: "book-1",
144
- author: "Harper Lee",
145
- title: "To Kill a Mockingbird",
146
- },
147
- {
148
- index: 1,
149
- id: "book-2",
150
- author: "Lev Tolstoy",
151
- title: "War and Peace",
152
- },
153
- ];
396
+ describe("API", () => {
397
+ test.each([INPUT_TYPES.CHECKBOX, INPUT_TYPES.RADIO, INPUT_TYPES.SWITCH])(
398
+ "supports %s input types",
399
+ (inputType) => {
400
+ const testItem = books[0];
401
+ render(
402
+ <TestWithMockedMenu context={{ items: books, isListbox: true }}>
403
+ <MenuItem id={testItem.id} inputType={inputType}>
404
+ {testItem.title}
405
+ </MenuItem>
406
+ </TestWithMockedMenu>
407
+ );
154
408
 
155
- const testItem = books[0];
409
+ expect(screen.getByRole(inputType)).toBeInTheDocument();
410
+ }
411
+ );
156
412
 
157
- const { user } = render(
158
- <TestWithMenuRoot
159
- triggerElement={<MenuToggleButton>Open</MenuToggleButton>}
160
- {...{
161
- ...testMenuContext,
162
- items: books,
163
- selectedItem: testItem,
164
- }}
413
+ it("uses the check icon by default when no inputType provided", () => {
414
+ const testItem = books[0];
415
+ render(
416
+ <TestWithMockedMenu
417
+ context={{ items: books, selectedItem: testItem, isListbox: true }}
165
418
  >
166
- <MenuItem
167
- id={testItem.id}
168
- disabled={true}
169
- onClick={handleStateChange}
170
- >
171
- {testItem.title}
172
- </MenuItem>
173
- </TestWithMenuRoot>
419
+ <MenuItem id={testItem.id}>{testItem.title}</MenuItem>
420
+ </TestWithMockedMenu>
174
421
  );
175
- await user
176
- .setup({
177
- pointerEventsCheck: PointerEventsCheckLevel.Never,
178
- })
179
- .click(screen.getByRole("menuitem"));
180
422
 
181
- expect(handleClick).not.toHaveBeenCalled();
423
+ expect(
424
+ screen.getByDataQaLabel({ icon: "check-solid" })
425
+ ).toBeInTheDocument();
182
426
  });
183
- });
184
- });
185
427
 
186
- describe("MenuItem API", () => {
187
- it("accepts children", () => {
188
- render(
189
- <ActionMenu
190
- id="test-menu-item-selectable"
191
- triggerElement={testTriggerElement}
192
- isOpen
193
- >
194
- <MenuContent>
195
- <MenuItem onClick={() => {}} id="test">
196
- Hello world!
197
- <span>testing</span>
198
- </MenuItem>
199
- </MenuContent>
200
- </ActionMenu>
201
- );
428
+ describe.each([
429
+ INPUT_TYPES.CHECKBOX,
430
+ INPUT_TYPES.RADIO,
431
+ INPUT_TYPES.SWITCH,
432
+ INPUT_TYPES.ICON,
433
+ ])("when the input type is %s", (inputType) => {
434
+ it("enables it's checked state when selected in the context", () => {
435
+ const testItem = books[0];
436
+ render(
437
+ <TestWithMockedMenu
438
+ context={{ items: books, selectedItem: testItem, isListbox: true }}
439
+ >
440
+ <MenuItem id={testItem.id} inputType={inputType}>
441
+ {testItem.title}
442
+ </MenuItem>
443
+ </TestWithMockedMenu>
444
+ );
202
445
 
203
- const menuItem = screen.getByText("Hello world!");
204
- const span = screen.getByText("testing");
205
- expect(menuItem).toContainElement(span);
206
- });
446
+ screen.getByRole("option", { selected: true });
447
+ testByType[inputType].isSelected();
448
+ });
207
449
 
208
- it("accepts box & system Props", () => {
209
- render(
210
- <ActionMenu
211
- id="test-menu-item-selectable"
212
- triggerElement={testTriggerElement}
213
- isOpen
214
- >
215
- <MenuContent>
216
- <MenuItem display="flex" p={300} onClick={() => {}} id="test">
217
- Hello world!
218
- <span>testing</span>
219
- </MenuItem>
220
- </MenuContent>
221
- </ActionMenu>
222
- );
450
+ it("enables it's checked state when selected by default", () => {
451
+ const testItem = books[0];
452
+ render(
453
+ <TestWithMenuRoot
454
+ menuToggleElement={<MenuToggleButton>Open</MenuToggleButton>}
455
+ {...{
456
+ isOpen: true,
457
+ items: books,
458
+ selectedItem: testItem,
459
+ }}
460
+ >
461
+ <MenuItem id={testItem.id} inputType={inputType}>
462
+ {testItem.title}
463
+ </MenuItem>
464
+ </TestWithMenuRoot>
465
+ );
223
466
 
224
- const menuItem = screen.getByRole("menuitem");
225
- expect(menuItem).toHaveStyle(`padding: ${theme.space[300]}`);
226
- expect(menuItem).toHaveStyle(`display: flex`);
467
+ screen.getByRole("option", { selected: true });
468
+ testByType[inputType].isSelected();
469
+ });
470
+
471
+ it("allows its checked state to be deselected when using the context", async () => {
472
+ const testItem = books[0];
473
+ const TestItemWithState = () => {
474
+ const [selectedItem, setSelectedItem] = React.useState<
475
+ typeof testItem | undefined
476
+ >(testItem);
477
+ return (
478
+ <TestWithMockedMenu context={{ items: books, selectedItem }}>
479
+ <MenuItem
480
+ id={testItem.id}
481
+ inputType={inputType}
482
+ onClick={() => setSelectedItem(undefined)}
483
+ >
484
+ {testItem.title}
485
+ </MenuItem>
486
+ </TestWithMockedMenu>
487
+ );
488
+ };
489
+ const { user } = render(<TestItemWithState />);
490
+
491
+ const menuItem = screen.getByRole("option", { selected: true });
492
+ testByType[inputType].isSelected();
493
+
494
+ await user.click(menuItem);
495
+
496
+ expect(
497
+ await screen.findByRole("option", {
498
+ selected: false,
499
+ })
500
+ ).toBeInTheDocument();
501
+ testByType[inputType].isNotSelected();
502
+ });
503
+ });
227
504
  });
228
505
  });