@primer/components 30.3.0-rc.2010c7d4 → 30.3.0-rc.9dbc85a9

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 (97) hide show
  1. package/CHANGELOG.md +4 -2
  2. package/dist/browser.esm.js +717 -718
  3. package/dist/browser.esm.js.map +1 -1
  4. package/dist/browser.umd.js +320 -321
  5. package/dist/browser.umd.js.map +1 -1
  6. package/docs/content/Autocomplete.mdx +627 -0
  7. package/docs/content/TextInputTokens.mdx +89 -0
  8. package/docs/src/@primer/gatsby-theme-doctocat/nav.yml +2 -0
  9. package/lib/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  10. package/lib/AnchoredOverlay/AnchoredOverlay.js +11 -3
  11. package/lib/Autocomplete/Autocomplete.d.ts +304 -0
  12. package/lib/Autocomplete/Autocomplete.js +145 -0
  13. package/lib/Autocomplete/AutocompleteContext.d.ts +17 -0
  14. package/lib/Autocomplete/AutocompleteContext.js +11 -0
  15. package/lib/Autocomplete/AutocompleteInput.d.ts +292 -0
  16. package/lib/Autocomplete/AutocompleteInput.js +157 -0
  17. package/lib/Autocomplete/AutocompleteMenu.d.ts +72 -0
  18. package/lib/Autocomplete/AutocompleteMenu.js +224 -0
  19. package/lib/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  20. package/lib/Autocomplete/AutocompleteOverlay.js +80 -0
  21. package/lib/Autocomplete/index.d.ts +2 -0
  22. package/lib/Autocomplete/index.js +15 -0
  23. package/lib/FilteredActionList/FilteredActionList.js +5 -31
  24. package/lib/Overlay.d.ts +1 -0
  25. package/lib/Overlay.js +3 -1
  26. package/lib/__tests__/Autocomplete.test.d.ts +1 -0
  27. package/lib/__tests__/Autocomplete.test.js +528 -0
  28. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  29. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.js +226 -0
  30. package/lib/behaviors/scrollIntoViewingArea.d.ts +1 -0
  31. package/lib/behaviors/scrollIntoViewingArea.js +39 -0
  32. package/lib/hooks/useOpenAndCloseFocus.d.ts +2 -1
  33. package/lib/hooks/useOpenAndCloseFocus.js +7 -2
  34. package/lib/hooks/useOverlay.d.ts +2 -1
  35. package/lib/hooks/useOverlay.js +4 -2
  36. package/lib/index.d.ts +2 -0
  37. package/lib/index.js +8 -0
  38. package/lib/stories/Autocomplete.stories.js +608 -0
  39. package/lib/utils/types/MandateProps.d.ts +3 -0
  40. package/lib/utils/types/MandateProps.js +1 -0
  41. package/lib/utils/types/index.d.ts +1 -0
  42. package/lib/utils/types/index.js +13 -0
  43. package/lib-esm/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  44. package/lib-esm/AnchoredOverlay/AnchoredOverlay.js +11 -3
  45. package/lib-esm/Autocomplete/Autocomplete.d.ts +304 -0
  46. package/lib-esm/Autocomplete/Autocomplete.js +123 -0
  47. package/lib-esm/Autocomplete/AutocompleteContext.d.ts +17 -0
  48. package/lib-esm/Autocomplete/AutocompleteContext.js +2 -0
  49. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +292 -0
  50. package/lib-esm/Autocomplete/AutocompleteInput.js +138 -0
  51. package/lib-esm/Autocomplete/AutocompleteMenu.d.ts +72 -0
  52. package/lib-esm/Autocomplete/AutocompleteMenu.js +205 -0
  53. package/lib-esm/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  54. package/lib-esm/Autocomplete/AutocompleteOverlay.js +62 -0
  55. package/lib-esm/Autocomplete/index.d.ts +2 -0
  56. package/lib-esm/Autocomplete/index.js +1 -0
  57. package/lib-esm/FilteredActionList/FilteredActionList.js +3 -31
  58. package/lib-esm/Overlay.d.ts +1 -0
  59. package/lib-esm/Overlay.js +3 -1
  60. package/lib-esm/__tests__/Autocomplete.test.d.ts +1 -0
  61. package/lib-esm/__tests__/Autocomplete.test.js +494 -0
  62. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  63. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.js +224 -0
  64. package/lib-esm/behaviors/scrollIntoViewingArea.d.ts +1 -0
  65. package/lib-esm/behaviors/scrollIntoViewingArea.js +30 -0
  66. package/lib-esm/hooks/useOpenAndCloseFocus.d.ts +2 -1
  67. package/lib-esm/hooks/useOpenAndCloseFocus.js +7 -2
  68. package/lib-esm/hooks/useOverlay.d.ts +2 -1
  69. package/lib-esm/hooks/useOverlay.js +4 -2
  70. package/lib-esm/index.d.ts +2 -0
  71. package/lib-esm/index.js +1 -0
  72. package/lib-esm/stories/Autocomplete.stories.js +549 -0
  73. package/lib-esm/utils/types/MandateProps.d.ts +3 -0
  74. package/lib-esm/utils/types/MandateProps.js +1 -0
  75. package/lib-esm/utils/types/index.d.ts +1 -0
  76. package/lib-esm/utils/types/index.js +2 -1
  77. package/package.json +1 -1
  78. package/src/AnchoredOverlay/AnchoredOverlay.tsx +14 -3
  79. package/src/Autocomplete/Autocomplete.tsx +103 -0
  80. package/src/Autocomplete/AutocompleteContext.tsx +19 -0
  81. package/src/Autocomplete/AutocompleteInput.tsx +179 -0
  82. package/src/Autocomplete/AutocompleteMenu.tsx +341 -0
  83. package/src/Autocomplete/AutocompleteOverlay.tsx +68 -0
  84. package/src/Autocomplete/index.ts +2 -0
  85. package/src/FilteredActionList/FilteredActionList.tsx +10 -25
  86. package/src/Overlay.tsx +4 -1
  87. package/src/__tests__/Autocomplete.test.tsx +444 -0
  88. package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +3414 -0
  89. package/src/__tests__/behaviors/scrollIntoViewingArea.test.ts +195 -0
  90. package/src/behaviors/scrollIntoViewingArea.ts +27 -0
  91. package/src/hooks/useOpenAndCloseFocus.ts +7 -2
  92. package/src/hooks/useOverlay.tsx +4 -2
  93. package/src/index.ts +2 -0
  94. package/src/stories/Autocomplete.stories.tsx +572 -0
  95. package/src/utils/types/MandateProps.ts +19 -0
  96. package/src/utils/types/index.ts +1 -0
  97. package/stats.html +1 -1
@@ -0,0 +1,528 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+
5
+ var _testing = require("../utils/testing");
6
+
7
+ var _react2 = require("@testing-library/react");
8
+
9
+ var _jestAxe = require("jest-axe");
10
+
11
+ require("babel-polyfill");
12
+
13
+ var _Autocomplete = _interopRequireDefault(require("../Autocomplete"));
14
+
15
+ var _index = require("../index");
16
+
17
+ var _theme = _interopRequireDefault(require("../theme"));
18
+
19
+ var _BaseStyles = _interopRequireDefault(require("../BaseStyles"));
20
+
21
+ var _ThemeProvider = require("../ThemeProvider");
22
+
23
+ var _userEvent = _interopRequireDefault(require("@testing-library/user-event"));
24
+
25
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
+
27
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
28
+
29
+ expect.extend(_jestAxe.toHaveNoViolations);
30
+ const mockItems = [{
31
+ text: 'zero',
32
+ id: 0
33
+ }, {
34
+ text: 'one',
35
+ id: 1
36
+ }, {
37
+ text: 'two',
38
+ id: 2
39
+ }, {
40
+ text: 'three',
41
+ id: 3
42
+ }, {
43
+ text: 'four',
44
+ id: 4
45
+ }, {
46
+ text: 'five',
47
+ id: 5
48
+ }, {
49
+ text: 'six',
50
+ id: 6
51
+ }, {
52
+ text: 'seven',
53
+ id: 7
54
+ }, {
55
+ text: 'twenty',
56
+ id: 20
57
+ }, {
58
+ text: 'twentyone',
59
+ id: 21
60
+ }]; // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+
62
+ const AUTOCOMPLETE_LABEL = 'Autocomplete field';
63
+
64
+ const LabelledAutocomplete = ({
65
+ inputProps = {},
66
+ menuProps
67
+ }) => {
68
+ const {
69
+ ['aria-labelledby']: ariaLabelledBy = 'autocompleteLabel',
70
+ ...menuPropsRest
71
+ } = menuProps;
72
+ const {
73
+ id = 'autocompleteInput',
74
+ ...inputPropsRest
75
+ } = inputProps;
76
+ return /*#__PURE__*/_react.default.createElement(_ThemeProvider.ThemeProvider, {
77
+ theme: _theme.default
78
+ }, /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_BaseStyles.default, null, /*#__PURE__*/_react.default.createElement("label", {
79
+ htmlFor: id,
80
+ id: ariaLabelledBy
81
+ }, "Autocomplete field"), /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
82
+ id: "autocompleteId"
83
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, _extends({
84
+ id: id
85
+ }, inputPropsRest)), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Overlay, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, _extends({
86
+ "aria-labelledby": ariaLabelledBy
87
+ }, menuPropsRest)))))));
88
+ };
89
+
90
+ LabelledAutocomplete.displayName = "LabelledAutocomplete";
91
+ describe('Autocomplete', () => {
92
+ describe('snapshots', () => {
93
+ it('renders a single select input', () => {
94
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
95
+ id: "autocompleteId"
96
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, null), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
97
+ "aria-labelledby": "labelId",
98
+ items: mockItems,
99
+ selectedItemIds: []
100
+ }))))).toMatchSnapshot();
101
+ });
102
+ it('renders a multiselect input', () => {
103
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
104
+ id: "autocompleteId"
105
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, null), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
106
+ "aria-labelledby": "labelId",
107
+ items: mockItems,
108
+ selectedItemIds: [],
109
+ selectionVariant: "multiple"
110
+ }))))).toMatchSnapshot();
111
+ });
112
+ it('renders a multiselect input with selected menu items', () => {
113
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
114
+ id: "autocompleteId"
115
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, null), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
116
+ "aria-labelledby": "labelId",
117
+ items: mockItems,
118
+ selectedItemIds: [0, 1, 2],
119
+ selectionVariant: "multiple"
120
+ }))))).toMatchSnapshot();
121
+ });
122
+ it('renders a menu that contains an item to add to the menu', () => {
123
+ const handleAddItemMock = jest.fn();
124
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
125
+ id: "autocompleteId"
126
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, null), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
127
+ "aria-labelledby": "labelId",
128
+ items: mockItems,
129
+ selectionVariant: "multiple",
130
+ selectedItemIds: [],
131
+ addNewItem: {
132
+ text: 'Add new item',
133
+ handleAddItem: handleAddItemMock
134
+ }
135
+ }))))).toMatchSnapshot();
136
+ });
137
+ it('renders a custom empty state message', () => {
138
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
139
+ id: "autocompleteId"
140
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, null), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
141
+ "aria-labelledby": "labelId",
142
+ items: [],
143
+ selectedItemIds: [],
144
+ emptyStateText: "No results"
145
+ }))))).toMatchSnapshot();
146
+ });
147
+ it('renders a loading state', () => {
148
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
149
+ id: "autocompleteId"
150
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, null), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
151
+ "aria-labelledby": "labelId",
152
+ loading: true,
153
+ items: [],
154
+ selectedItemIds: []
155
+ }))))).toMatchSnapshot();
156
+ });
157
+ it('renders with a custom text input component', () => {
158
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
159
+ id: "autocompleteId"
160
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, {
161
+ as: () => /*#__PURE__*/_react.default.createElement("input", {
162
+ type: "text",
163
+ id: "customInput"
164
+ })
165
+ }), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
166
+ "aria-labelledby": "labelId",
167
+ items: mockItems,
168
+ selectedItemIds: []
169
+ }))))).toMatchSnapshot();
170
+ });
171
+ it('renders with an input value', () => {
172
+ expect((0, _testing.render)( /*#__PURE__*/_react.default.createElement(_index.SSRProvider, null, /*#__PURE__*/_react.default.createElement(_Autocomplete.default, {
173
+ id: "autocompleteId"
174
+ }, /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Input, {
175
+ value: "test"
176
+ }), /*#__PURE__*/_react.default.createElement(_Autocomplete.default.Menu, {
177
+ "aria-labelledby": "labelId",
178
+ items: mockItems,
179
+ selectedItemIds: []
180
+ }))))).toMatchSnapshot();
181
+ });
182
+ });
183
+ describe('Autocomplete.Input', () => {
184
+ it('calls onChange', () => {
185
+ const onChangeMock = jest.fn();
186
+ const {
187
+ container
188
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
189
+ inputProps: {
190
+ onChange: onChangeMock
191
+ },
192
+ menuProps: {
193
+ items: mockItems,
194
+ selectedItemIds: []
195
+ }
196
+ }));
197
+ const inputNode = container.querySelector('#autocompleteInput');
198
+ expect(onChangeMock).not.toHaveBeenCalled();
199
+ inputNode && _userEvent.default.type(inputNode, 'z');
200
+ expect(onChangeMock).toHaveBeenCalled();
201
+ });
202
+ it('calls onFocus', () => {
203
+ const onFocusMock = jest.fn();
204
+ const {
205
+ container
206
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
207
+ inputProps: {
208
+ onFocus: onFocusMock
209
+ },
210
+ menuProps: {
211
+ items: mockItems,
212
+ selectedItemIds: []
213
+ }
214
+ }));
215
+ const inputNode = container.querySelector('#autocompleteInput');
216
+ expect(onFocusMock).not.toHaveBeenCalled();
217
+ inputNode && _react2.fireEvent.focus(inputNode);
218
+ expect(onFocusMock).toHaveBeenCalled();
219
+ });
220
+ it('calls onKeyDown', () => {
221
+ const onKeyDownMock = jest.fn();
222
+ const {
223
+ getByLabelText
224
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
225
+ inputProps: {
226
+ onKeyDown: onKeyDownMock
227
+ },
228
+ menuProps: {
229
+ items: [],
230
+ selectedItemIds: []
231
+ }
232
+ }));
233
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
234
+ expect(onKeyDownMock).not.toHaveBeenCalled();
235
+
236
+ _react2.fireEvent.keyDown(inputNode, {
237
+ key: 'Shift'
238
+ });
239
+
240
+ expect(onKeyDownMock).toHaveBeenCalled();
241
+ });
242
+ it('calls onKeyUp', () => {
243
+ const onKeyUpMock = jest.fn();
244
+ const {
245
+ getByLabelText
246
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
247
+ inputProps: {
248
+ onKeyUp: onKeyUpMock
249
+ },
250
+ menuProps: {
251
+ items: [],
252
+ selectedItemIds: []
253
+ }
254
+ }));
255
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
256
+ expect(onKeyUpMock).not.toHaveBeenCalled();
257
+
258
+ _react2.fireEvent.keyUp(inputNode, {
259
+ key: 'Shift'
260
+ });
261
+
262
+ expect(onKeyUpMock).toHaveBeenCalled();
263
+ });
264
+ it('calls onKeyPress', () => {
265
+ const onKeyPressMock = jest.fn();
266
+ const {
267
+ getByLabelText
268
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
269
+ inputProps: {
270
+ onKeyPress: onKeyPressMock
271
+ },
272
+ menuProps: {
273
+ items: [],
274
+ selectedItemIds: []
275
+ }
276
+ }));
277
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
278
+ expect(onKeyPressMock).not.toHaveBeenCalled();
279
+
280
+ _userEvent.default.type(inputNode, '{enter}');
281
+
282
+ expect(onKeyPressMock).toHaveBeenCalled();
283
+ });
284
+ it('opens the menu when the input is focused', () => {
285
+ const {
286
+ getByLabelText
287
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
288
+ menuProps: {
289
+ items: [],
290
+ selectedItemIds: []
291
+ }
292
+ }));
293
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
294
+ expect(inputNode.getAttribute('aria-expanded')).not.toBe('true');
295
+
296
+ _react2.fireEvent.focus(inputNode);
297
+
298
+ expect(inputNode.getAttribute('aria-expanded')).toBe('true');
299
+ });
300
+ it('closes the menu when the input is blurred', () => {
301
+ const {
302
+ getByLabelText
303
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
304
+ menuProps: {
305
+ items: [],
306
+ selectedItemIds: []
307
+ }
308
+ }));
309
+ const inputNode = getByLabelText(AUTOCOMPLETE_LABEL);
310
+ expect(inputNode.getAttribute('aria-expanded')).not.toBe('true');
311
+
312
+ _react2.fireEvent.focus(inputNode);
313
+
314
+ expect(inputNode.getAttribute('aria-expanded')).toBe('true'); // eslint-disable-next-line github/no-blur
315
+
316
+ _react2.fireEvent.blur(inputNode); // wait a tick for blur to finish
317
+
318
+
319
+ setTimeout(() => {
320
+ expect(inputNode.getAttribute('aria-expanded')).not.toBe('true');
321
+ }, 0);
322
+ });
323
+ it('sets the input value to the suggested item text and highlights the untyped part of the word', () => {
324
+ const {
325
+ container,
326
+ getByDisplayValue
327
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
328
+ menuProps: {
329
+ items: mockItems,
330
+ selectedItemIds: []
331
+ }
332
+ }));
333
+ const inputNode = container.querySelector('#autocompleteInput');
334
+ inputNode && _userEvent.default.type(inputNode, 'ze');
335
+ expect(getByDisplayValue('zero')).toBeDefined();
336
+ });
337
+ it('does not show or highlight suggestion text after the user hits Backspace until they hit another key', () => {
338
+ const {
339
+ container,
340
+ getByDisplayValue
341
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
342
+ menuProps: {
343
+ items: mockItems,
344
+ selectedItemIds: []
345
+ }
346
+ }));
347
+ const inputNode = container.querySelector('#autocompleteInput');
348
+ expect(inputNode.selectionStart).toBe(0);
349
+ inputNode && _userEvent.default.type(inputNode, 'ze');
350
+ expect(getByDisplayValue('zero')).toBeDefined();
351
+ expect(inputNode.selectionStart).toBe(2);
352
+ expect(inputNode.selectionEnd).toBe(4);
353
+ inputNode && _userEvent.default.type(inputNode, '{backspace}');
354
+ expect(inputNode.selectionStart).toBe(2);
355
+ expect(getByDisplayValue('ze')).toBeDefined();
356
+ inputNode && _userEvent.default.type(inputNode, 'r');
357
+ expect(inputNode.selectionStart).toBe(3);
358
+ expect(inputNode.selectionEnd).toBe(4);
359
+ expect(getByDisplayValue('zero')).toBeDefined();
360
+ });
361
+ it('clears the input value when when the user hits Escape', () => {
362
+ const {
363
+ container
364
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
365
+ menuProps: {
366
+ items: mockItems,
367
+ selectedItemIds: []
368
+ }
369
+ }));
370
+ const inputNode = container.querySelector('#autocompleteInput');
371
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
372
+ inputNode && _userEvent.default.type(inputNode, 'ze');
373
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
374
+ inputNode && _userEvent.default.type(inputNode, '{esc}');
375
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
376
+ });
377
+ });
378
+ describe('Autocomplete.Menu', () => {
379
+ it('calls a custom filter function', () => {
380
+ const filterFnMock = jest.fn();
381
+ const {
382
+ container
383
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
384
+ menuProps: {
385
+ items: mockItems,
386
+ selectedItemIds: [],
387
+ filterFn: filterFnMock
388
+ }
389
+ }));
390
+ const inputNode = container.querySelector('#autocompleteInput');
391
+ inputNode && _userEvent.default.type(inputNode, 'ze');
392
+ expect(filterFnMock).toHaveBeenCalled();
393
+ });
394
+ it('calls a custom sort function when the menu closes', () => {
395
+ const sortOnCloseFnMock = jest.fn();
396
+ const {
397
+ container
398
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
399
+ menuProps: {
400
+ items: mockItems,
401
+ selectedItemIds: [],
402
+ sortOnCloseFn: sortOnCloseFnMock
403
+ }
404
+ }));
405
+ const inputNode = container.querySelector('#autocompleteInput'); // `sortOnCloseFnMock` will be called in a `.sort()` on render to check if the
406
+ // current sort order matches the result of `sortOnCloseFnMock`
407
+
408
+ expect(sortOnCloseFnMock).toHaveBeenCalledTimes(mockItems.length - 1);
409
+
410
+ if (inputNode) {
411
+ _userEvent.default.type(inputNode, 'ze'); // eslint-disable-next-line github/no-blur
412
+
413
+
414
+ _react2.fireEvent.blur(inputNode);
415
+ } // wait a tick for blur to finish
416
+
417
+
418
+ setTimeout(() => {
419
+ expect(sortOnCloseFnMock).toHaveBeenCalledTimes(mockItems.length);
420
+ }, 0);
421
+ });
422
+ it("calls onOpenChange with the menu's open state", () => {
423
+ const onOpenChangeMock = jest.fn();
424
+ const {
425
+ container
426
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
427
+ menuProps: {
428
+ items: mockItems,
429
+ selectedItemIds: [],
430
+ onOpenChange: onOpenChangeMock
431
+ }
432
+ }));
433
+ const inputNode = container.querySelector('#autocompleteInput');
434
+ inputNode && _userEvent.default.type(inputNode, 'ze');
435
+ expect(onOpenChangeMock).toHaveBeenCalled();
436
+ });
437
+ it('calls onSelectedChange with the data for the selected items', () => {
438
+ const onSelectedChangeMock = jest.fn();
439
+ const {
440
+ container
441
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
442
+ menuProps: {
443
+ items: mockItems,
444
+ selectedItemIds: [],
445
+ onSelectedChange: onSelectedChangeMock
446
+ }
447
+ }));
448
+ const inputNode = container.querySelector('#autocompleteInput');
449
+ expect(onSelectedChangeMock).not.toHaveBeenCalled();
450
+
451
+ if (inputNode) {
452
+ _react2.fireEvent.focus(inputNode);
453
+
454
+ _userEvent.default.type(inputNode, '{enter}');
455
+ } // wait a tick for the keyboard event to be dispatched to the menu item
456
+
457
+
458
+ setTimeout(() => {
459
+ expect(onSelectedChangeMock).toHaveBeenCalledWith([mockItems[0]]);
460
+ }, 0);
461
+ });
462
+ it('does not close the menu when clicking an item in the menu if selectionVariant=multiple', () => {
463
+ const {
464
+ getByText,
465
+ container
466
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
467
+ menuProps: {
468
+ items: mockItems,
469
+ selectedItemIds: [],
470
+ selectionVariant: 'multiple'
471
+ }
472
+ }));
473
+ const inputNode = container.querySelector('#autocompleteInput');
474
+ const itemToClickNode = getByText(mockItems[1].text);
475
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
476
+ inputNode && _react2.fireEvent.focus(inputNode);
477
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
478
+
479
+ _react2.fireEvent.click(itemToClickNode);
480
+
481
+ inputNode && _userEvent.default.type(inputNode, '{enter}');
482
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
483
+ });
484
+ it('closes the menu when clicking an item in the menu if selectionVariant=single', () => {
485
+ const {
486
+ getByText,
487
+ container
488
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
489
+ menuProps: {
490
+ items: mockItems,
491
+ selectedItemIds: [],
492
+ selectionVariant: 'single'
493
+ }
494
+ }));
495
+ const inputNode = container.querySelector('#autocompleteInput');
496
+ const itemToClickNode = getByText(mockItems[1].text);
497
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
498
+ inputNode && _react2.fireEvent.focus(inputNode);
499
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).toBe('true');
500
+
501
+ _react2.fireEvent.click(itemToClickNode);
502
+
503
+ expect(inputNode === null || inputNode === void 0 ? void 0 : inputNode.getAttribute('aria-expanded')).not.toBe('true');
504
+ });
505
+ it('calls handleAddItem with new item data when passing addNewItem', () => {
506
+ const handleAddItemMock = jest.fn();
507
+ const {
508
+ getByText
509
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(LabelledAutocomplete, {
510
+ menuProps: {
511
+ items: mockItems,
512
+ selectedItemIds: [],
513
+ selectionVariant: 'multiple',
514
+ addNewItem: {
515
+ text: 'Add new item',
516
+ handleAddItem: handleAddItemMock
517
+ }
518
+ }
519
+ }));
520
+ const addNewItemNode = getByText('Add new item');
521
+ expect(handleAddItemMock).not.toHaveBeenCalled();
522
+
523
+ _react2.fireEvent.click(addNewItemNode);
524
+
525
+ expect(handleAddItemMock).toHaveBeenCalled();
526
+ });
527
+ });
528
+ });