@primer/behaviors 0.0.0-2021113221730 → 0.0.0-2022017101021

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 (73) hide show
  1. package/lib/__tests__/anchored-position.test.d.ts +1 -0
  2. package/lib/__tests__/anchored-position.test.js +388 -0
  3. package/lib/__tests__/focus-trap.test.d.ts +1 -0
  4. package/lib/__tests__/focus-trap.test.js +234 -0
  5. package/lib/__tests__/focus-zone.test.d.ts +1 -0
  6. package/lib/__tests__/focus-zone.test.js +570 -0
  7. package/lib/__tests__/iterate-focusable-elements.test.d.ts +1 -0
  8. package/lib/__tests__/iterate-focusable-elements.test.js +55 -0
  9. package/lib/__tests__/scroll-into-view.test.d.ts +1 -0
  10. package/lib/__tests__/scroll-into-view.test.js +245 -0
  11. package/lib/anchored-position.d.ts +89 -0
  12. package/lib/anchored-position.js +316 -0
  13. package/lib/focus-trap.d.ts +12 -0
  14. package/lib/focus-trap.js +179 -0
  15. package/lib/focus-zone.d.ts +137 -0
  16. package/lib/focus-zone.js +578 -0
  17. package/{dist/index.js → lib/index.d.ts} +0 -0
  18. package/lib/index.js +57 -0
  19. package/lib/polyfills/event-listener-signal.d.ts +6 -0
  20. package/lib/polyfills/event-listener-signal.js +64 -0
  21. package/lib/scroll-into-view.d.ts +7 -0
  22. package/lib/scroll-into-view.js +42 -0
  23. package/{dist/utils/index.js → lib/utils/index.d.ts} +0 -0
  24. package/lib/utils/index.js +44 -0
  25. package/lib/utils/iterate-focusable-elements.d.ts +42 -0
  26. package/lib/utils/iterate-focusable-elements.js +113 -0
  27. package/lib/utils/unique-id.d.ts +1 -0
  28. package/lib/utils/unique-id.js +12 -0
  29. package/lib/utils/user-agent.d.ts +1 -0
  30. package/lib/utils/user-agent.js +15 -0
  31. package/lib-esm/__tests__/anchored-position.test.d.ts +1 -0
  32. package/lib-esm/__tests__/anchored-position.test.js +386 -0
  33. package/lib-esm/__tests__/focus-trap.test.d.ts +1 -0
  34. package/lib-esm/__tests__/focus-trap.test.js +227 -0
  35. package/lib-esm/__tests__/focus-zone.test.d.ts +1 -0
  36. package/lib-esm/__tests__/focus-zone.test.js +487 -0
  37. package/lib-esm/__tests__/iterate-focusable-elements.test.d.ts +1 -0
  38. package/lib-esm/__tests__/iterate-focusable-elements.test.js +48 -0
  39. package/lib-esm/__tests__/scroll-into-view.test.d.ts +1 -0
  40. package/lib-esm/__tests__/scroll-into-view.test.js +243 -0
  41. package/lib-esm/anchored-position.d.ts +89 -0
  42. package/lib-esm/anchored-position.js +309 -0
  43. package/lib-esm/focus-trap.d.ts +12 -0
  44. package/lib-esm/focus-trap.js +170 -0
  45. package/lib-esm/focus-zone.d.ts +137 -0
  46. package/lib-esm/focus-zone.js +559 -0
  47. package/{dist → lib-esm}/index.d.ts +0 -0
  48. package/lib-esm/index.js +4 -0
  49. package/{dist → lib-esm}/polyfills/event-listener-signal.d.ts +0 -0
  50. package/lib-esm/polyfills/event-listener-signal.js +57 -0
  51. package/{dist → lib-esm}/scroll-into-view.d.ts +0 -0
  52. package/lib-esm/scroll-into-view.js +35 -0
  53. package/{dist → lib-esm}/utils/index.d.ts +0 -0
  54. package/lib-esm/utils/index.js +3 -0
  55. package/lib-esm/utils/iterate-focusable-elements.d.ts +42 -0
  56. package/lib-esm/utils/iterate-focusable-elements.js +102 -0
  57. package/{dist → lib-esm}/utils/unique-id.d.ts +0 -0
  58. package/lib-esm/utils/unique-id.js +5 -0
  59. package/{dist → lib-esm}/utils/user-agent.d.ts +0 -0
  60. package/lib-esm/utils/user-agent.js +8 -0
  61. package/package.json +37 -5
  62. package/dist/anchored-position.d.ts +0 -15
  63. package/dist/anchored-position.js +0 -206
  64. package/dist/focus-trap.d.ts +0 -2
  65. package/dist/focus-trap.js +0 -107
  66. package/dist/focus-zone.d.ts +0 -32
  67. package/dist/focus-zone.js +0 -406
  68. package/dist/polyfills/event-listener-signal.js +0 -40
  69. package/dist/scroll-into-view.js +0 -17
  70. package/dist/utils/iterate-focusable-elements.d.ts +0 -8
  71. package/dist/utils/iterate-focusable-elements.js +0 -57
  72. package/dist/utils/unique-id.js +0 -4
  73. package/dist/utils/user-agent.js +0 -7
@@ -0,0 +1,570 @@
1
+ "use strict";
2
+
3
+ var _focusZone = require("../focus-zone.js");
4
+
5
+ var _react = require("@testing-library/react");
6
+
7
+ var _react2 = _interopRequireDefault(require("react"));
8
+
9
+ var _userEvent = _interopRequireDefault(require("@testing-library/user-event"));
10
+
11
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
+
13
+ async function nextTick() {
14
+ return new Promise(resolve => setTimeout(resolve, 0));
15
+ }
16
+
17
+ const moveDown = () => _userEvent.default.type(document.activeElement, '{arrowdown}');
18
+
19
+ const moveUp = () => _userEvent.default.type(document.activeElement, '{arrowup}'); // Since we use strict `isTabbable` checks within focus trap, we need to mock these
20
+ // properties that Jest does not populate.
21
+
22
+
23
+ beforeAll(() => {
24
+ try {
25
+ Object.defineProperties(HTMLElement.prototype, {
26
+ offsetHeight: {
27
+ get: () => 42
28
+ },
29
+ offsetWidth: {
30
+ get: () => 42
31
+ },
32
+ getClientRects: {
33
+ get: () => () => [42]
34
+ }
35
+ });
36
+ } catch {// ignore
37
+ }
38
+ });
39
+ it('Should allow arrow keys to move focus', () => {
40
+ const {
41
+ container
42
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
43
+ tabIndex: 0
44
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
45
+ id: "focusZone"
46
+ }, /*#__PURE__*/_react2.default.createElement("button", {
47
+ tabIndex: 0
48
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
49
+ tabIndex: 0
50
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
51
+ tabIndex: 0
52
+ }, "Cantaloupe"))));
53
+ const focusZoneContainer = container.querySelector('#focusZone');
54
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
55
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer);
56
+ firstButton.focus();
57
+ expect(document.activeElement).toEqual(firstButton);
58
+
59
+ _userEvent.default.type(firstButton, '{arrowdown}');
60
+
61
+ expect(document.activeElement).toEqual(secondButton);
62
+ controller.abort();
63
+ });
64
+ it('Should have one tab-stop inside the focus zone when enabled', () => {
65
+ const {
66
+ container
67
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
68
+ tabIndex: 0
69
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
70
+ id: "focusZone"
71
+ }, /*#__PURE__*/_react2.default.createElement("button", {
72
+ tabIndex: 0
73
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
74
+ tabIndex: 0
75
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
76
+ tabIndex: 0
77
+ }, "Cantaloupe")), /*#__PURE__*/_react2.default.createElement("button", {
78
+ tabIndex: 0
79
+ }, "Next Apple")));
80
+ const focusZoneContainer = container.querySelector('#focusZone'); // eslint-disable-next-line @typescript-eslint/no-unused-vars
81
+
82
+ const [one, two, three, four, five] = container.querySelectorAll('button');
83
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer);
84
+ one.focus();
85
+
86
+ _userEvent.default.tab();
87
+
88
+ _userEvent.default.tab();
89
+
90
+ expect(document.activeElement).toEqual(five);
91
+ controller.abort();
92
+ one.focus();
93
+
94
+ _userEvent.default.tab();
95
+
96
+ _userEvent.default.tab();
97
+
98
+ expect(document.activeElement).toEqual(three);
99
+ controller.abort();
100
+ });
101
+ it('Should prevent moving focus outside the zone', () => {
102
+ const {
103
+ container
104
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
105
+ tabIndex: 0
106
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
107
+ id: "focusZone"
108
+ }, /*#__PURE__*/_react2.default.createElement("button", {
109
+ tabIndex: 0
110
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
111
+ tabIndex: 0
112
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
113
+ tabIndex: 0
114
+ }, "Cantaloupe"))));
115
+ const focusZoneContainer = container.querySelector('#focusZone');
116
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
117
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer);
118
+ firstButton.focus();
119
+ expect(document.activeElement).toEqual(firstButton);
120
+
121
+ _userEvent.default.type(firstButton, '{arrowup}');
122
+
123
+ expect(document.activeElement).toEqual(firstButton);
124
+
125
+ _userEvent.default.type(firstButton, '{arrowdown}');
126
+
127
+ expect(document.activeElement).toEqual(secondButton);
128
+
129
+ _userEvent.default.type(secondButton, '{arrowdown}');
130
+
131
+ expect(document.activeElement).toEqual(thirdButton);
132
+
133
+ _userEvent.default.type(thirdButton, '{arrowdown}');
134
+
135
+ expect(document.activeElement).toEqual(thirdButton);
136
+ controller.abort();
137
+ });
138
+ it('Should do focus wrapping correctly', () => {
139
+ const {
140
+ container
141
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
142
+ tabIndex: 0
143
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
144
+ id: "focusZone"
145
+ }, /*#__PURE__*/_react2.default.createElement("button", {
146
+ tabIndex: 0
147
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
148
+ tabIndex: 0
149
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
150
+ tabIndex: 0
151
+ }, "Cantaloupe"))));
152
+ const focusZoneContainer = container.querySelector('#focusZone');
153
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
154
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
155
+ focusOutBehavior: 'wrap'
156
+ });
157
+ firstButton.focus();
158
+ expect(document.activeElement).toEqual(firstButton);
159
+
160
+ _userEvent.default.type(firstButton, '{arrowup}');
161
+
162
+ expect(document.activeElement).toEqual(thirdButton);
163
+
164
+ _userEvent.default.type(thirdButton, '{arrowup}');
165
+
166
+ expect(document.activeElement).toEqual(secondButton);
167
+
168
+ _userEvent.default.type(secondButton, '{arrowdown}');
169
+
170
+ expect(document.activeElement).toEqual(thirdButton);
171
+
172
+ _userEvent.default.type(thirdButton, '{arrowdown}');
173
+
174
+ expect(document.activeElement).toEqual(firstButton);
175
+ controller.abort();
176
+ });
177
+ it('Should call custom getNextFocusable callback', () => {
178
+ const {
179
+ container
180
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
181
+ tabIndex: 0
182
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
183
+ id: "focusZone"
184
+ }, /*#__PURE__*/_react2.default.createElement("button", {
185
+ tabIndex: 0
186
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
187
+ tabIndex: 0
188
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
189
+ tabIndex: 0
190
+ }, "Cantaloupe"))));
191
+ const focusZoneContainer = container.querySelector('#focusZone');
192
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
193
+ const getNextFocusableCallback = jest.fn();
194
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
195
+ getNextFocusable: getNextFocusableCallback
196
+ });
197
+ firstButton.focus();
198
+ expect(document.activeElement).toEqual(firstButton);
199
+
200
+ _userEvent.default.type(firstButton, '{arrowdown}');
201
+
202
+ expect(getNextFocusableCallback).toHaveBeenCalledWith('next', firstButton, expect.anything());
203
+
204
+ _userEvent.default.type(secondButton, '{home}');
205
+
206
+ expect(getNextFocusableCallback).toHaveBeenCalledWith('start', secondButton, expect.anything());
207
+ controller.abort();
208
+ });
209
+ it('Should focus-in to the most recently-focused element', () => {
210
+ const {
211
+ container
212
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
213
+ tabIndex: 0,
214
+ id: "outside"
215
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
216
+ id: "focusZone"
217
+ }, /*#__PURE__*/_react2.default.createElement("button", {
218
+ tabIndex: 0
219
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
220
+ tabIndex: 0
221
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
222
+ tabIndex: 0
223
+ }, "Cantaloupe"))));
224
+ const focusZoneContainer = container.querySelector('#focusZone');
225
+ const outsideButton = container.querySelector('#outside');
226
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
227
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer);
228
+ firstButton.focus();
229
+ expect(document.activeElement).toEqual(firstButton);
230
+
231
+ _userEvent.default.type(firstButton, '{arrowdown}');
232
+
233
+ expect(document.activeElement).toEqual(secondButton);
234
+ outsideButton.focus();
235
+
236
+ _userEvent.default.tab();
237
+
238
+ expect(document.activeElement).toEqual(secondButton);
239
+ controller.abort();
240
+ });
241
+ it('Should focus-in to the first element when focusInStrategy is "first"', () => {
242
+ const {
243
+ container
244
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
245
+ tabIndex: 0,
246
+ id: "outside"
247
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
248
+ id: "focusZone"
249
+ }, /*#__PURE__*/_react2.default.createElement("button", {
250
+ tabIndex: 0
251
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
252
+ tabIndex: 0
253
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
254
+ tabIndex: 0
255
+ }, "Cantaloupe"))));
256
+ const focusZoneContainer = container.querySelector('#focusZone');
257
+ const outsideButton = container.querySelector('#outside');
258
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
259
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
260
+ focusInStrategy: 'first'
261
+ });
262
+ firstButton.focus();
263
+ expect(document.activeElement).toEqual(firstButton);
264
+
265
+ _userEvent.default.type(firstButton, '{arrowdown}');
266
+
267
+ expect(document.activeElement).toEqual(secondButton);
268
+ outsideButton.focus();
269
+
270
+ _userEvent.default.tab();
271
+
272
+ expect(document.activeElement).toEqual(firstButton);
273
+ controller.abort();
274
+ });
275
+ it('Should focus-in to the closest element when focusInStrategy is "closest"', () => {
276
+ const {
277
+ container
278
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
279
+ tabIndex: 0,
280
+ id: "outsideBefore"
281
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
282
+ id: "focusZone"
283
+ }, /*#__PURE__*/_react2.default.createElement("button", {
284
+ id: "apple",
285
+ tabIndex: 0
286
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
287
+ id: "banana",
288
+ tabIndex: 0
289
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
290
+ id: "cantaloupe",
291
+ tabIndex: 0
292
+ }, "Cantaloupe")), /*#__PURE__*/_react2.default.createElement("button", {
293
+ tabIndex: 0,
294
+ id: "outsideAfter"
295
+ }, "Good Apple")));
296
+ const focusZoneContainer = container.querySelector('#focusZone');
297
+ const outsideBefore = container.querySelector('#outsideBefore');
298
+ const outsideAfter = container.querySelector('#outsideAfter');
299
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
300
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
301
+ focusInStrategy: 'closest'
302
+ });
303
+ firstButton.focus();
304
+ expect(document.activeElement).toEqual(firstButton);
305
+
306
+ _userEvent.default.type(firstButton, '{arrowdown}');
307
+
308
+ expect(document.activeElement).toEqual(secondButton);
309
+ outsideBefore.focus();
310
+
311
+ _userEvent.default.tab();
312
+
313
+ expect(document.activeElement).toEqual(firstButton);
314
+ outsideAfter.focus();
315
+
316
+ _userEvent.default.tab({
317
+ shift: true
318
+ });
319
+
320
+ expect(document.activeElement).toEqual(thirdButton);
321
+ controller.abort();
322
+ });
323
+ it('Should call the custom focusInStrategy callback', () => {
324
+ const {
325
+ container
326
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
327
+ tabIndex: 0,
328
+ id: "outside"
329
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
330
+ id: "focusZone"
331
+ }, /*#__PURE__*/_react2.default.createElement("button", {
332
+ tabIndex: 0
333
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
334
+ tabIndex: 0
335
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
336
+ tabIndex: 0
337
+ }, "Cantaloupe"))));
338
+ const focusZoneContainer = container.querySelector('#focusZone');
339
+ const outsideButton = container.querySelector('#outside');
340
+ const [, secondButton] = focusZoneContainer.querySelectorAll('button');
341
+ const focusInCallback = jest.fn().mockReturnValue(secondButton);
342
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
343
+ focusInStrategy: focusInCallback
344
+ });
345
+ outsideButton.focus();
346
+
347
+ _userEvent.default.tab();
348
+
349
+ expect(focusInCallback).toHaveBeenCalledWith(outsideButton);
350
+ expect(document.activeElement).toEqual(secondButton);
351
+ controller.abort();
352
+ });
353
+ it('Should respect inputs by not moving focus if key would have some other effect', () => {
354
+ const {
355
+ container
356
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
357
+ tabIndex: 0,
358
+ id: "outside"
359
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
360
+ id: "focusZone"
361
+ }, /*#__PURE__*/_react2.default.createElement("button", {
362
+ tabIndex: 0
363
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("input", {
364
+ type: "text",
365
+ defaultValue: "Banana",
366
+ tabIndex: 0
367
+ }), /*#__PURE__*/_react2.default.createElement("button", {
368
+ tabIndex: 0
369
+ }, "Cantaloupe"))));
370
+ const focusZoneContainer = container.querySelector('#focusZone');
371
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
372
+ const input = focusZoneContainer.querySelector('input');
373
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
374
+ bindKeys: _focusZone.FocusKeys.ArrowHorizontal | _focusZone.FocusKeys.HomeAndEnd
375
+ });
376
+ firstButton.focus();
377
+
378
+ _userEvent.default.type(firstButton, '{arrowright}');
379
+
380
+ expect(document.activeElement).toEqual(input);
381
+
382
+ _userEvent.default.type(input, '{arrowleft}');
383
+
384
+ expect(document.activeElement).toEqual(input);
385
+
386
+ _userEvent.default.type(input, '{arrowright}');
387
+
388
+ expect(document.activeElement).toEqual(input);
389
+
390
+ _userEvent.default.type(input, '{arrowright}');
391
+
392
+ expect(document.activeElement).toEqual(secondButton);
393
+ controller.abort();
394
+ });
395
+ it('Should focus-in to the first element if the last-focused element is removed', async () => {
396
+ const {
397
+ container
398
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
399
+ tabIndex: 0,
400
+ id: "outside"
401
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("div", {
402
+ id: "focusZone"
403
+ }, /*#__PURE__*/_react2.default.createElement("button", {
404
+ tabIndex: 0
405
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
406
+ tabIndex: 0
407
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
408
+ tabIndex: 0
409
+ }, "Cantaloupe"))));
410
+ const focusZoneContainer = container.querySelector('#focusZone');
411
+ const [firstButton, secondButton, thirdButton] = focusZoneContainer.querySelectorAll('button');
412
+ const outsideButton = container.querySelector('#outside');
413
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer);
414
+ firstButton.focus();
415
+
416
+ _userEvent.default.type(firstButton, '{arrowdown}');
417
+
418
+ expect(document.activeElement).toEqual(secondButton);
419
+ outsideButton.focus();
420
+ focusZoneContainer.removeChild(secondButton); // The mutation observer fires asynchronously
421
+
422
+ await nextTick();
423
+
424
+ _userEvent.default.tab();
425
+
426
+ expect(document.activeElement).toEqual(firstButton);
427
+
428
+ _userEvent.default.type(firstButton, '{arrowdown}');
429
+
430
+ expect(document.activeElement).toEqual(thirdButton);
431
+ controller.abort();
432
+ });
433
+ it('Should call onActiveDescendantChanged properly', () => {
434
+ const {
435
+ container
436
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
437
+ tabIndex: 0,
438
+ id: "outside"
439
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("input", {
440
+ id: "control",
441
+ defaultValue: "control input",
442
+ tabIndex: 0
443
+ }), /*#__PURE__*/_react2.default.createElement("div", {
444
+ id: "focusZone"
445
+ }, /*#__PURE__*/_react2.default.createElement("button", {
446
+ tabIndex: 0
447
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
448
+ tabIndex: 0
449
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
450
+ tabIndex: 0
451
+ }, "Cantaloupe"))));
452
+ const focusZoneContainer = container.querySelector('#focusZone');
453
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
454
+ const control = container.querySelector('#control');
455
+ const activeDescendantChangedCallback = jest.fn();
456
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
457
+ activeDescendantControl: control,
458
+ onActiveDescendantChanged: activeDescendantChangedCallback
459
+ });
460
+ control.focus();
461
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, undefined, false);
462
+
463
+ _userEvent.default.type(control, '{arrowdown}');
464
+
465
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(secondButton, firstButton, true);
466
+
467
+ _userEvent.default.type(control, '{arrowup}');
468
+
469
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, secondButton, true);
470
+
471
+ _react.fireEvent.mouseMove(secondButton);
472
+
473
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(secondButton, firstButton, false);
474
+
475
+ _userEvent.default.type(control, '{arrowup}');
476
+
477
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, secondButton, true);
478
+
479
+ _userEvent.default.type(control, '{arrowUp}');
480
+
481
+ expect(activeDescendantChangedCallback).toHaveBeenLastCalledWith(firstButton, firstButton, true);
482
+ activeDescendantChangedCallback.mockReset();
483
+
484
+ _react.fireEvent.mouseMove(firstButton);
485
+
486
+ expect(activeDescendantChangedCallback).not.toBeCalled();
487
+ controller.abort();
488
+ });
489
+ it('Should set aria-activedescendant correctly', () => {
490
+ const {
491
+ container
492
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("button", {
493
+ tabIndex: 0,
494
+ id: "outside"
495
+ }, "Bad Apple"), /*#__PURE__*/_react2.default.createElement("input", {
496
+ id: "control",
497
+ defaultValue: "control input",
498
+ tabIndex: 0
499
+ }), /*#__PURE__*/_react2.default.createElement("div", {
500
+ id: "focusZone"
501
+ }, /*#__PURE__*/_react2.default.createElement("button", {
502
+ tabIndex: 0
503
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
504
+ tabIndex: 0
505
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
506
+ tabIndex: 0
507
+ }, "Cantaloupe"))));
508
+ const focusZoneContainer = container.querySelector('#focusZone');
509
+ const [firstButton, secondButton] = focusZoneContainer.querySelectorAll('button');
510
+ const outsideButton = container.querySelector('#outside');
511
+ const control = container.querySelector('#control');
512
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer, {
513
+ activeDescendantControl: control
514
+ });
515
+ control.focus();
516
+ expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
517
+
518
+ _userEvent.default.type(control, '{arrowdown}');
519
+
520
+ expect(control.getAttribute('aria-activedescendant')).toEqual(secondButton.id);
521
+
522
+ _userEvent.default.type(control, '{arrowup}');
523
+
524
+ expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
525
+ expect(document.activeElement).toEqual(control);
526
+
527
+ _userEvent.default.type(control, '{arrowup}');
528
+
529
+ expect(control.getAttribute('aria-activedescendant')).toEqual(firstButton.id);
530
+ outsideButton.focus();
531
+ expect(control.hasAttribute('aria-activedescendant')).toBeFalsy();
532
+ controller.abort();
533
+ });
534
+ it('Should handle elements being reordered', async () => {
535
+ const {
536
+ container
537
+ } = (0, _react.render)( /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("div", {
538
+ id: "focusZone"
539
+ }, /*#__PURE__*/_react2.default.createElement("button", {
540
+ tabIndex: 0
541
+ }, "Apple"), /*#__PURE__*/_react2.default.createElement("button", {
542
+ tabIndex: 0
543
+ }, "Banana"), /*#__PURE__*/_react2.default.createElement("button", {
544
+ tabIndex: 0
545
+ }, "Cantaloupe"), /*#__PURE__*/_react2.default.createElement("button", {
546
+ tabIndex: 0
547
+ }, "Durian"))));
548
+ const focusZoneContainer = container.querySelector('#focusZone');
549
+ const [firstButton, secondButton, thirdButton, fourthButton] = focusZoneContainer.querySelectorAll('button');
550
+ const controller = (0, _focusZone.focusZone)(focusZoneContainer);
551
+ firstButton.focus();
552
+ expect(document.activeElement).toEqual(firstButton);
553
+ moveDown();
554
+ expect(document.activeElement).toEqual(secondButton);
555
+ moveUp();
556
+ expect(document.activeElement).toEqual(firstButton); // move secondButton and thirdButton to the end of the zone, in reverse order
557
+
558
+ focusZoneContainer.appendChild(thirdButton);
559
+ focusZoneContainer.appendChild(secondButton); // The mutation observer fires asynchronously
560
+
561
+ await nextTick();
562
+ expect(document.activeElement).toEqual(firstButton);
563
+ moveDown();
564
+ expect(document.activeElement).toEqual(fourthButton);
565
+ moveDown();
566
+ expect(document.activeElement).toEqual(thirdButton);
567
+ moveDown();
568
+ expect(document.activeElement).toEqual(secondButton);
569
+ controller.abort();
570
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+
5
+ var _iterateFocusableElements = require("../utils/iterate-focusable-elements.js");
6
+
7
+ var _react2 = require("@testing-library/react");
8
+
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+
11
+ it('Should iterate through focusable elements only', () => {
12
+ const {
13
+ container
14
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("textarea", null)), /*#__PURE__*/_react.default.createElement("input", null), /*#__PURE__*/_react.default.createElement("button", null, "Hello"), /*#__PURE__*/_react.default.createElement("p", null, "Not focusable"), /*#__PURE__*/_react.default.createElement("div", {
15
+ tabIndex: 0
16
+ }, /*#__PURE__*/_react.default.createElement("a", {
17
+ tabIndex: -1,
18
+ href: "#boo"
19
+ }, "Not focusable"), /*#__PURE__*/_react.default.createElement("a", {
20
+ href: "#yah"
21
+ }, "Focusable"))));
22
+ const focusable = Array.from((0, _iterateFocusableElements.iterateFocusableElements)(container, {
23
+ onlyTabbable: true
24
+ }));
25
+ expect(focusable.length).toEqual(5);
26
+ expect(focusable[0].tagName.toLowerCase()).toEqual('textarea');
27
+ expect(focusable[1].tagName.toLowerCase()).toEqual('input');
28
+ expect(focusable[2].tagName.toLowerCase()).toEqual('button');
29
+ expect(focusable[3].tagName.toLowerCase()).toEqual('div');
30
+ expect(focusable[4].tagName.toLowerCase()).toEqual('a');
31
+ expect(focusable[4].getAttribute('href')).toEqual('#yah');
32
+ });
33
+ it('Should iterate through focusable elements in reverse', () => {
34
+ const {
35
+ container
36
+ } = (0, _react2.render)( /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("textarea", null)), /*#__PURE__*/_react.default.createElement("input", null), /*#__PURE__*/_react.default.createElement("button", null, "Hello"), /*#__PURE__*/_react.default.createElement("p", null, "Not focusable"), /*#__PURE__*/_react.default.createElement("div", {
37
+ tabIndex: 0
38
+ }, /*#__PURE__*/_react.default.createElement("a", {
39
+ tabIndex: -1,
40
+ href: "#boo"
41
+ }, "Not focusable"), /*#__PURE__*/_react.default.createElement("a", {
42
+ href: "#yah"
43
+ }, "Focusable"))));
44
+ const focusable = Array.from((0, _iterateFocusableElements.iterateFocusableElements)(container, {
45
+ reverse: true,
46
+ onlyTabbable: true
47
+ }));
48
+ expect(focusable.length).toEqual(5);
49
+ expect(focusable[0].tagName.toLowerCase()).toEqual('a');
50
+ expect(focusable[0].getAttribute('href')).toEqual('#yah');
51
+ expect(focusable[1].tagName.toLowerCase()).toEqual('div');
52
+ expect(focusable[2].tagName.toLowerCase()).toEqual('button');
53
+ expect(focusable[3].tagName.toLowerCase()).toEqual('input');
54
+ expect(focusable[4].tagName.toLowerCase()).toEqual('textarea');
55
+ });
@@ -0,0 +1 @@
1
+ export {};