@khanacademy/wonder-blocks-tooltip 2.4.2 → 2.5.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.
@@ -1,1003 +0,0 @@
1
- /* eslint-disable max-lines */
2
- import * as React from "react";
3
- import {View} from "@khanacademy/wonder-blocks-core";
4
- import {render, screen} from "@testing-library/react";
5
- import {userEvent} from "@testing-library/user-event";
6
-
7
- import TooltipAnchor from "../tooltip-anchor";
8
- import {
9
- TooltipAppearanceDelay,
10
- TooltipDisappearanceDelay,
11
- } from "../../util/constants";
12
-
13
- jest.mock("../../util/active-tracker");
14
-
15
- describe("TooltipAnchor", () => {
16
- beforeEach(async () => {
17
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mockReset' does not exist on type '{ <K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | ... 1 more ... | undefined): void; }'.
18
- if (typeof document.addEventListener.mockReset === "function") {
19
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mockRestore' does not exist on type '{ <K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | ... 1 more ... | undefined): void; }'.
20
- document.addEventListener.mockRestore();
21
- }
22
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mockReset' does not exist on type '{ <K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions | undefined): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | ... 1 more ... | undefined): void; }'.
23
- if (typeof document.removeEventListener.mockReset === "function") {
24
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mockRestore' does not exist on type '{ <K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions | undefined): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | ... 1 more ... | undefined): void; }'.
25
- document.removeEventListener.mockRestore();
26
- }
27
- jest.clearAllTimers();
28
- jest.useFakeTimers();
29
-
30
- const {default: ActiveTracker} = await import(
31
- "../../util/active-tracker"
32
- );
33
- // We know there's one global instance of this import, so let's
34
- // reset it.
35
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
36
- const mockTracker = ActiveTracker.mock.instances[0];
37
- mockTracker.steal.mockClear();
38
- mockTracker.giveup.mockClear();
39
- });
40
-
41
- test("on mount, subscribes to focus and hover events", async () => {
42
- // Arrange
43
- const addEventListenerSpy = jest.spyOn(
44
- HTMLElement.prototype,
45
- "addEventListener",
46
- );
47
- addEventListenerSpy.mockClear();
48
-
49
- // Act
50
- render(
51
- <TooltipAnchor anchorRef={() => {}} onActiveChanged={() => {}}>
52
- Anchor text
53
- </TooltipAnchor>,
54
- );
55
-
56
- // Assert
57
- expect(addEventListenerSpy).toHaveBeenCalledWith(
58
- "focusin",
59
- expect.any(Function),
60
- );
61
- expect(addEventListenerSpy).toHaveBeenCalledWith(
62
- "focusout",
63
- expect.any(Function),
64
- );
65
- expect(addEventListenerSpy).toHaveBeenCalledWith(
66
- "mouseenter",
67
- expect.any(Function),
68
- );
69
- expect(addEventListenerSpy).toHaveBeenCalledWith(
70
- "mouseleave",
71
- expect.any(Function),
72
- );
73
- });
74
-
75
- test("on unmount, unsubscribes from focus and hover events", async () => {
76
- // Arrange
77
- const removeEventListenerSpy = jest.spyOn(
78
- HTMLElement.prototype,
79
- "removeEventListener",
80
- );
81
- removeEventListenerSpy.mockClear();
82
- const wrapper = render(
83
- <TooltipAnchor anchorRef={() => {}} onActiveChanged={() => {}}>
84
- Anchor text
85
- </TooltipAnchor>,
86
- );
87
-
88
- // Act
89
- wrapper.unmount();
90
-
91
- // Assert
92
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
93
- "focusin",
94
- expect.any(Function),
95
- );
96
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
97
- "focusout",
98
- expect.any(Function),
99
- );
100
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
101
- "mouseenter",
102
- expect.any(Function),
103
- );
104
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
105
- "mouseleave",
106
- expect.any(Function),
107
- );
108
- });
109
-
110
- test("ref is properly set", async () => {
111
- // Arrange
112
- const anchorRef = jest.fn();
113
-
114
- render(
115
- <TooltipAnchor
116
- forceAnchorFocusivity={true}
117
- anchorRef={anchorRef}
118
- onActiveChanged={() => {}}
119
- >
120
- <View id="portal">This is the anchor</View>
121
- </TooltipAnchor>,
122
- );
123
-
124
- // Act
125
- const result = await screen.findByText("This is the anchor");
126
-
127
- // Assert
128
- expect(anchorRef).toHaveBeenCalledWith(result);
129
- });
130
-
131
- describe("forceAnchorFocusivity is true", () => {
132
- test("if not set, sets tabindex on anchor target", async () => {
133
- // Arrange
134
- render(
135
- <TooltipAnchor
136
- forceAnchorFocusivity={true}
137
- anchorRef={jest.fn()}
138
- onActiveChanged={() => {}}
139
- >
140
- <View id="portal">This is the anchor</View>
141
- </TooltipAnchor>,
142
- );
143
-
144
- // Act
145
- const result = await screen.findByText("This is the anchor");
146
-
147
- // Assert
148
- expect(result).toHaveAttribute("tabindex", "0");
149
- });
150
-
151
- test("if tabindex already set, leaves it as-is", async () => {
152
- // Arrange
153
- render(
154
- <TooltipAnchor
155
- forceAnchorFocusivity={true}
156
- anchorRef={jest.fn()}
157
- onActiveChanged={() => {}}
158
- >
159
- <View tabIndex={-1}>This is the anchor</View>
160
- </TooltipAnchor>,
161
- );
162
-
163
- // Act
164
- const result = await screen.findByText("This is the anchor");
165
-
166
- // Assert
167
- expect(result).toHaveAttribute("tabindex", "-1");
168
- });
169
- });
170
-
171
- describe("forceAnchorFocusivity is false", () => {
172
- test("does not set tabindex on anchor target", async () => {
173
- // Arrange
174
- render(
175
- <TooltipAnchor
176
- forceAnchorFocusivity={false}
177
- anchorRef={jest.fn()}
178
- onActiveChanged={() => {}}
179
- >
180
- <View>This is the anchor</View>
181
- </TooltipAnchor>,
182
- );
183
-
184
- // Act
185
- const result = await screen.findByText("This is the anchor");
186
-
187
- // Assert
188
- expect(result).not.toHaveAttribute("tabindex");
189
- });
190
-
191
- test("if we had added tabindex, removes it", async () => {
192
- // Arrange
193
- const TestFixture = (props: any) => (
194
- <TooltipAnchor
195
- forceAnchorFocusivity={props.force}
196
- anchorRef={jest.fn()}
197
- onActiveChanged={() => {}}
198
- >
199
- <View>This is the anchor</View>
200
- </TooltipAnchor>
201
- );
202
- const {rerender} = render(<TestFixture force={true} />);
203
-
204
- // Act
205
- expect(
206
- await screen.findByText("This is the anchor"),
207
- ).toHaveAttribute("tabindex", "0");
208
-
209
- rerender(<TestFixture force={false} />);
210
-
211
- // Assert
212
- expect(
213
- await screen.findByText("This is the anchor"),
214
- ).not.toHaveAttribute("tabindex");
215
- });
216
-
217
- test("if we had not added tabindex, leaves it", async () => {
218
- // Arrange
219
- const TestFixture = (props: any) => (
220
- <TooltipAnchor
221
- forceAnchorFocusivity={props.force}
222
- anchorRef={jest.fn()}
223
- onActiveChanged={() => {}}
224
- >
225
- <View tabIndex={-1}>This is the anchor</View>
226
- </TooltipAnchor>
227
- );
228
-
229
- const wrapper = render(<TestFixture force={true} />);
230
-
231
- // Act
232
- wrapper.rerender(<TestFixture force={false} />);
233
-
234
- // Assert
235
- expect(
236
- await screen.findByText("This is the anchor"),
237
- ).toHaveAttribute("tabindex", "-1");
238
- });
239
- });
240
-
241
- describe("receives keyboard focus", () => {
242
- test("active state was not stolen, delays set active", async () => {
243
- // Arrange
244
- const ue = userEvent.setup({
245
- advanceTimers: jest.advanceTimersByTime,
246
- });
247
- const {default: ActiveTracker} = await import(
248
- "../../util/active-tracker"
249
- );
250
- const timeoutSpy = jest.spyOn(global, "setTimeout");
251
- // Let's tell the tooltip it isn't stealing and therefore it should
252
- // be using a delay to show the tooltip.
253
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
254
- const mockTracker = ActiveTracker.mock.instances[0];
255
- mockTracker.steal.mockImplementationOnce(() => false);
256
-
257
- let activeState = false;
258
-
259
- render(
260
- <TooltipAnchor
261
- anchorRef={jest.fn()}
262
- onActiveChanged={(active: any) => {
263
- activeState = active;
264
- }}
265
- >
266
- Anchor Text
267
- </TooltipAnchor>,
268
- );
269
-
270
- // Act
271
- // Let's focus the anchor
272
- await ue.tab();
273
- // Check that we didn't go active before the delay
274
- expect(activeState).toBe(false);
275
- expect(timeoutSpy).toHaveBeenCalledWith(
276
- expect.any(Function),
277
- TooltipAppearanceDelay,
278
- );
279
- jest.runOnlyPendingTimers();
280
-
281
- // Assert
282
- expect(activeState).toBe(true);
283
- });
284
-
285
- test("active state was stolen, set active immediately", async () => {
286
- // Arrange
287
- const ue = userEvent.setup({
288
- advanceTimers: jest.advanceTimersByTime,
289
- });
290
- const {default: ActiveTracker} = await import(
291
- "../../util/active-tracker"
292
- );
293
- // Let's tell the tooltip it is stealing and therefore it should
294
- // not be using a delay to show the tooltip.
295
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
296
- const mockTracker = ActiveTracker.mock.instances[0];
297
- mockTracker.steal.mockImplementationOnce(() => true);
298
-
299
- let activeState = false;
300
-
301
- render(
302
- <TooltipAnchor
303
- anchorRef={jest.fn()}
304
- onActiveChanged={(active: any) => {
305
- activeState = active;
306
- }}
307
- >
308
- Anchor Text
309
- </TooltipAnchor>,
310
- );
311
-
312
- // Act
313
- // Let's focus the anchor
314
- await ue.tab();
315
-
316
- // Assert
317
- expect(activeState).toBe(true);
318
- });
319
- });
320
-
321
- describe("loses keyboard focus", () => {
322
- test("active state was not stolen, active is set to false with delay", async () => {
323
- // Arrange
324
- const ue = userEvent.setup({
325
- advanceTimers: jest.advanceTimersByTime,
326
- });
327
- const timeoutSpy = jest.spyOn(global, "setTimeout");
328
- let activeState = false;
329
-
330
- render(
331
- <TooltipAnchor
332
- anchorRef={jest.fn()}
333
- onActiveChanged={(active: any) => {
334
- activeState = active;
335
- }}
336
- >
337
- Anchor Text
338
- </TooltipAnchor>,
339
- );
340
-
341
- // Let's focus the anchor
342
- await ue.tab();
343
- expect(timeoutSpy).toHaveBeenCalledWith(
344
- expect.any(Function),
345
- TooltipAppearanceDelay,
346
- );
347
- jest.runOnlyPendingTimers();
348
- expect(activeState).toBe(true);
349
-
350
- // Act
351
- // Let's blur the anchor
352
- await ue.tab();
353
- expect(timeoutSpy).toHaveBeenCalledWith(
354
- expect.any(Function),
355
- TooltipDisappearanceDelay,
356
- );
357
- jest.runOnlyPendingTimers();
358
-
359
- // Assert
360
- expect(activeState).toBe(false);
361
- });
362
-
363
- test("active state was not stolen, gives up active state", async () => {
364
- // Arrange
365
- const ue = userEvent.setup({
366
- advanceTimers: jest.advanceTimersByTime,
367
- });
368
- const timeoutSpy = jest.spyOn(global, "setTimeout");
369
- const {default: ActiveTracker} = await import(
370
- "../../util/active-tracker"
371
- );
372
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
373
- const mockTracker = ActiveTracker.mock.instances[0];
374
-
375
- let activeState = false;
376
- render(
377
- <TooltipAnchor
378
- anchorRef={jest.fn()}
379
- onActiveChanged={(active: any) => {
380
- activeState = active;
381
- }}
382
- >
383
- Anchor Text
384
- </TooltipAnchor>,
385
- );
386
-
387
- // Let's focus the anchor
388
- await ue.tab();
389
- expect(timeoutSpy).toHaveBeenCalledWith(
390
- expect.any(Function),
391
- TooltipAppearanceDelay,
392
- );
393
- jest.runOnlyPendingTimers();
394
- expect(activeState).toBe(true);
395
-
396
- // Act
397
- // Let's blur the anchor
398
- await ue.tab();
399
- expect(timeoutSpy).toHaveBeenCalledWith(
400
- expect.any(Function),
401
- TooltipDisappearanceDelay,
402
- );
403
- jest.runOnlyPendingTimers();
404
-
405
- // Assert
406
- expect(mockTracker.giveup).toHaveBeenCalledTimes(1);
407
- });
408
-
409
- test("active state was stolen, active is set to false immediately", async () => {
410
- // Arrange
411
- const ue = userEvent.setup({
412
- advanceTimers: jest.advanceTimersByTime,
413
- });
414
- const timeoutSpy = jest.spyOn(global, "setTimeout");
415
- let activeState = false;
416
-
417
- render(
418
- <TooltipAnchor
419
- anchorRef={jest.fn()}
420
- onActiveChanged={(active: any) => {
421
- activeState = active;
422
- }}
423
- >
424
- Anchor Text
425
- </TooltipAnchor>,
426
- );
427
-
428
- // Focus the anchor
429
- await ue.tab();
430
- expect(timeoutSpy).toHaveBeenCalledWith(
431
- expect.any(Function),
432
- TooltipAppearanceDelay,
433
- );
434
- jest.runOnlyPendingTimers();
435
- expect(activeState).toBe(true);
436
-
437
- // Act
438
- // Blur the anchor
439
- await ue.tab();
440
- jest.runOnlyPendingTimers();
441
-
442
- // Assert
443
- expect(activeState).toBe(false);
444
- });
445
-
446
- test("active state was stolen, so it does not have it to give up", async () => {
447
- // Arrange
448
- const ue = userEvent.setup({
449
- advanceTimers: jest.advanceTimersByTime,
450
- });
451
- const timeoutSpy = jest.spyOn(global, "setTimeout");
452
- const {default: ActiveTracker} = await import(
453
- "../../util/active-tracker"
454
- );
455
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
456
- const mockTracker = ActiveTracker.mock.instances[0];
457
- // Arrange
458
- let activeState = false;
459
- render(
460
- <TooltipAnchor
461
- anchorRef={jest.fn()}
462
- onActiveChanged={(active: any) => {
463
- activeState = active;
464
- }}
465
- >
466
- Anchor Text
467
- </TooltipAnchor>,
468
- );
469
- // Focus the anchor
470
- await ue.tab();
471
- expect(timeoutSpy).toHaveBeenCalledWith(
472
- expect.any(Function),
473
- TooltipAppearanceDelay,
474
- );
475
- jest.runOnlyPendingTimers();
476
- expect(activeState).toBe(true);
477
-
478
- // Act
479
- // Blur the anchor
480
- await ue.tab();
481
-
482
- // Assert
483
- expect(mockTracker.giveup).not.toHaveBeenCalled();
484
- });
485
-
486
- test("if hovered, remains active", async () => {
487
- // Arrange
488
- const ue = userEvent.setup({
489
- advanceTimers: jest.advanceTimersByTime,
490
- });
491
- const timeoutSpy = jest.spyOn(global, "setTimeout");
492
- let activeState = false;
493
- render(
494
- <TooltipAnchor
495
- anchorRef={jest.fn()}
496
- onActiveChanged={(active: any) => {
497
- activeState = active;
498
- }}
499
- >
500
- Anchor Text
501
- </TooltipAnchor>,
502
- );
503
-
504
- // Focus the anchor
505
- await ue.tab();
506
- expect(timeoutSpy).toHaveBeenCalledWith(
507
- expect.any(Function),
508
- TooltipAppearanceDelay,
509
- );
510
- jest.runOnlyPendingTimers();
511
- timeoutSpy.mockClear();
512
- await ue.hover(await screen.findByText("Anchor Text"));
513
-
514
- // Act
515
- // Blur the anchor
516
- await ue.tab();
517
-
518
- // Assert
519
- // Make sure that we're not delay hiding as well.
520
- expect(activeState).toBe(true);
521
- // NOTE(john): This is now being called after upgrading to
522
- // user-event v14. I'm not sure why it wasn't being called before.
523
- //expect(timeoutSpy).not.toHaveBeenCalled();
524
- });
525
- });
526
-
527
- describe("is hovered", () => {
528
- test("active state was not stolen, delays set active", async () => {
529
- // Arrange
530
- const ue = userEvent.setup({
531
- advanceTimers: jest.advanceTimersByTime,
532
- });
533
- const timeoutSpy = jest.spyOn(global, "setTimeout");
534
- const {default: ActiveTracker} = await import(
535
- "../../util/active-tracker"
536
- );
537
- // Let's tell the tooltip it isn't stealing and therefore it should
538
- // be using a delay to show the tooltip.
539
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
540
- const mockTracker = ActiveTracker.mock.instances[0];
541
- mockTracker.steal.mockImplementationOnce(() => false);
542
-
543
- let activeState = false;
544
-
545
- render(
546
- <TooltipAnchor
547
- anchorRef={jest.fn()}
548
- onActiveChanged={(active: any) => {
549
- activeState = active;
550
- }}
551
- >
552
- Anchor Text
553
- </TooltipAnchor>,
554
- );
555
-
556
- // Act
557
- await ue.hover(await screen.findByText("Anchor Text"));
558
- // Check that we didn't go active before the delay
559
- expect(activeState).toBe(false);
560
- expect(timeoutSpy).toHaveBeenCalledWith(
561
- expect.any(Function),
562
- TooltipAppearanceDelay,
563
- );
564
- jest.runOnlyPendingTimers();
565
-
566
- // Assert
567
- expect(activeState).toBe(true);
568
- });
569
-
570
- test("active state was stolen, set active immediately", async () => {
571
- // Arrange
572
- const ue = userEvent.setup({
573
- advanceTimers: jest.advanceTimersByTime,
574
- });
575
- const {default: ActiveTracker} = await import(
576
- "../../util/active-tracker"
577
- );
578
- // Let's tell the tooltip it is stealing and therefore it should
579
- // not be using a delay to show the tooltip.
580
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
581
- const mockTracker = ActiveTracker.mock.instances[0];
582
- mockTracker.steal.mockImplementationOnce(() => true);
583
-
584
- let activeState = false;
585
-
586
- render(
587
- <TooltipAnchor
588
- anchorRef={jest.fn()}
589
- onActiveChanged={(active: any) => {
590
- activeState = active;
591
- }}
592
- >
593
- Anchor Text
594
- </TooltipAnchor>,
595
- );
596
-
597
- // Act
598
- await ue.hover(await screen.findByText("Anchor Text"));
599
-
600
- // Assert
601
- expect(activeState).toBe(true);
602
- });
603
- });
604
-
605
- describe("is unhovered", () => {
606
- test("active state was not stolen, active is set to false with delay", async () => {
607
- // Arrange
608
- const ue = userEvent.setup({
609
- advanceTimers: jest.advanceTimersByTime,
610
- });
611
- const timeoutSpy = jest.spyOn(global, "setTimeout");
612
- let activeState = false;
613
-
614
- render(
615
- <TooltipAnchor
616
- anchorRef={jest.fn()}
617
- onActiveChanged={(active: any) => {
618
- activeState = active;
619
- }}
620
- >
621
- Anchor Text
622
- </TooltipAnchor>,
623
- );
624
-
625
- await ue.hover(await screen.findByText("Anchor Text"));
626
- expect(timeoutSpy).toHaveBeenCalledWith(
627
- expect.any(Function),
628
- TooltipAppearanceDelay,
629
- );
630
- jest.runOnlyPendingTimers();
631
- expect(activeState).toBe(true);
632
-
633
- // Act
634
- await ue.unhover(await screen.findByText("Anchor Text"));
635
- expect(activeState).toBe(true);
636
- expect(timeoutSpy).toHaveBeenCalledWith(
637
- expect.any(Function),
638
- TooltipDisappearanceDelay,
639
- );
640
- jest.runOnlyPendingTimers();
641
-
642
- // Assert
643
- expect(activeState).toBe(false);
644
- });
645
-
646
- test("active state was not stolen, gives up active state", async () => {
647
- // Arrange
648
- const ue = userEvent.setup({
649
- advanceTimers: jest.advanceTimersByTime,
650
- });
651
- const timeoutSpy = jest.spyOn(global, "setTimeout");
652
- const {default: ActiveTracker} = await import(
653
- "../../util/active-tracker"
654
- );
655
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
656
- const mockTracker = ActiveTracker.mock.instances[0];
657
- let activeState = false;
658
-
659
- render(
660
- <TooltipAnchor
661
- anchorRef={jest.fn()}
662
- onActiveChanged={(active: any) => {
663
- activeState = active;
664
- }}
665
- >
666
- Anchor Text
667
- </TooltipAnchor>,
668
- );
669
-
670
- await ue.hover(await screen.findByText("Anchor Text"));
671
- expect(timeoutSpy).toHaveBeenCalledWith(
672
- expect.any(Function),
673
- TooltipAppearanceDelay,
674
- );
675
- jest.runOnlyPendingTimers();
676
- expect(activeState).toBe(true);
677
-
678
- // Act
679
- await ue.unhover(await screen.findByText("Anchor Text"));
680
- expect(activeState).toBe(true);
681
- expect(timeoutSpy).toHaveBeenCalledWith(
682
- expect.any(Function),
683
- TooltipDisappearanceDelay,
684
- );
685
- jest.runOnlyPendingTimers();
686
-
687
- // Assert
688
- expect(mockTracker.giveup).toHaveBeenCalledTimes(1);
689
- });
690
-
691
- test("active state was stolen, active is set to false immediately", async () => {
692
- // Arrange
693
- const ue = userEvent.setup({
694
- advanceTimers: jest.advanceTimersByTime,
695
- });
696
- const timeoutSpy = jest.spyOn(global, "setTimeout");
697
- let activeState = false;
698
-
699
- render(
700
- <TooltipAnchor
701
- anchorRef={jest.fn()}
702
- onActiveChanged={(active: any) => {
703
- activeState = active;
704
- }}
705
- >
706
- Anchor Text
707
- </TooltipAnchor>,
708
- );
709
-
710
- await ue.hover(await screen.findByText("Anchor Text"));
711
- expect(timeoutSpy).toHaveBeenCalledWith(
712
- expect.any(Function),
713
- TooltipAppearanceDelay,
714
- );
715
- jest.runOnlyPendingTimers();
716
- expect(activeState).toBe(true);
717
-
718
- // Act
719
- await ue.unhover(await screen.findByText("Anchor Text"));
720
- jest.runOnlyPendingTimers();
721
-
722
- // Assert
723
- expect(activeState).toBe(false);
724
- });
725
-
726
- test("active state was stolen, so it does not have it to give up", async () => {
727
- // Arrange
728
- const ue = userEvent.setup({
729
- advanceTimers: jest.advanceTimersByTime,
730
- });
731
- const timeoutSpy = jest.spyOn(global, "setTimeout");
732
- const {default: ActiveTracker} = await import(
733
- "../../util/active-tracker"
734
- );
735
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'mock' does not exist on type 'typeof ActiveTracker'.
736
- const mockTracker = ActiveTracker.mock.instances[0];
737
- // Arrange
738
- let activeState = false;
739
- render(
740
- <TooltipAnchor
741
- anchorRef={jest.fn()}
742
- onActiveChanged={(active: any) => {
743
- activeState = active;
744
- }}
745
- >
746
- Anchor Text
747
- </TooltipAnchor>,
748
- );
749
- await ue.hover(await screen.findByText("Anchor Text"));
750
- expect(timeoutSpy).toHaveBeenCalledWith(
751
- expect.any(Function),
752
- TooltipAppearanceDelay,
753
- );
754
- jest.runOnlyPendingTimers();
755
- expect(activeState).toBe(true);
756
-
757
- // Act
758
- await ue.unhover(await screen.findByText("Anchor Text"));
759
-
760
- // Assert
761
- expect(mockTracker.giveup).not.toHaveBeenCalled();
762
- });
763
-
764
- test("if focused, remains active", async () => {
765
- // Arrange
766
- const ue = userEvent.setup({
767
- advanceTimers: jest.advanceTimersByTime,
768
- });
769
- const timeoutSpy = jest.spyOn(global, "setTimeout");
770
- let activeState = false;
771
- render(
772
- <TooltipAnchor
773
- anchorRef={jest.fn()}
774
- onActiveChanged={(active: any) => {
775
- activeState = active;
776
- }}
777
- >
778
- Anchor Text
779
- </TooltipAnchor>,
780
- );
781
- await ue.hover(await screen.findByText("Anchor Text"));
782
- expect(timeoutSpy).toHaveBeenCalledWith(
783
- expect.any(Function),
784
- TooltipAppearanceDelay,
785
- );
786
- jest.runOnlyPendingTimers();
787
- timeoutSpy.mockClear();
788
- // Focus the anchor
789
- await ue.tab();
790
-
791
- // Act
792
- await ue.unhover(await screen.findByText("Anchor Text"));
793
-
794
- // Assert
795
- // Make sure that we're not delay hiding as well.
796
- expect(activeState).toBe(true);
797
- // NOTE(john): This is now being called after upgrading to
798
- // user-event v14. I'm not sure why it wasn't being called before.
799
- //expect(timeoutSpy).not.toHaveBeenCalled();
800
- });
801
- });
802
-
803
- // TODO(FEI-5533): Key press events aren't working correctly with
804
- // user-event v14. We need to investigate and fix this.
805
- describe.skip("dismiss behavior", () => {
806
- test("subscribes to keydown event on active", async () => {
807
- // Arrange
808
- const ue = userEvent.setup({
809
- advanceTimers: jest.advanceTimersByTime,
810
- });
811
- const timeoutSpy = jest.spyOn(global, "setTimeout");
812
- const spy = jest.spyOn(document, "addEventListener");
813
- render(
814
- <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
815
- Anchor Text
816
- </TooltipAnchor>,
817
- );
818
-
819
- // Act
820
- await ue.hover(await screen.findByText("Anchor Text"));
821
- expect(timeoutSpy).toHaveBeenCalledWith(
822
- expect.any(Function),
823
- TooltipAppearanceDelay,
824
- );
825
- jest.runOnlyPendingTimers();
826
-
827
- // Assert
828
- expect(spy).toHaveBeenCalledTimes(1);
829
- expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
830
- });
831
-
832
- test("does not subscribe to keydown event if already active", async () => {
833
- // Arrange
834
- const ue = userEvent.setup({
835
- advanceTimers: jest.advanceTimersByTime,
836
- });
837
- const timeoutSpy = jest.spyOn(global, "setTimeout");
838
- const spy = jest.spyOn(document, "addEventListener");
839
- render(
840
- <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
841
- Anchor Text
842
- </TooltipAnchor>,
843
- );
844
-
845
- // Focus the anchor
846
- await ue.tab();
847
- expect(timeoutSpy).toHaveBeenCalledWith(
848
- expect.any(Function),
849
- TooltipAppearanceDelay,
850
- );
851
- jest.runOnlyPendingTimers();
852
- expect(spy).toHaveBeenCalledTimes(1);
853
- expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
854
- spy.mockClear();
855
-
856
- // Act
857
- await ue.hover(await screen.findByText("Anchor Text"));
858
-
859
- // Assert
860
- expect(spy).not.toHaveBeenCalled();
861
- });
862
-
863
- test("unsubscribes from keydown event on inactive", async () => {
864
- // Arrange
865
- const ue = userEvent.setup({
866
- advanceTimers: jest.advanceTimersByTime,
867
- });
868
- const timeoutSpy = jest.spyOn(global, "setTimeout");
869
- const spy = jest.spyOn(document, "removeEventListener");
870
- render(
871
- <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
872
- Anchor Text
873
- </TooltipAnchor>,
874
- );
875
-
876
- // Focus the anchor
877
- await ue.tab();
878
- expect(timeoutSpy).toHaveBeenCalledWith(
879
- expect.any(Function),
880
- TooltipAppearanceDelay,
881
- );
882
- jest.runOnlyPendingTimers();
883
-
884
- // Act
885
- // Blur the anchor
886
- await ue.tab();
887
- expect(timeoutSpy).toHaveBeenCalledWith(
888
- expect.any(Function),
889
- TooltipDisappearanceDelay,
890
- );
891
- jest.runOnlyPendingTimers();
892
-
893
- // Assert
894
- expect(spy).toHaveBeenCalledTimes(1);
895
- expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
896
- });
897
-
898
- test("unsubscribes from keydown event on unmount", async () => {
899
- // Arrange
900
- const ue = userEvent.setup({
901
- advanceTimers: jest.advanceTimersByTime,
902
- });
903
- const timeoutSpy = jest.spyOn(global, "setTimeout");
904
-
905
- const spy = jest.spyOn(document, "removeEventListener");
906
- const {unmount} = render(
907
- <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
908
- Anchor Text
909
- </TooltipAnchor>,
910
- );
911
-
912
- // Focus the anchor
913
- await ue.tab();
914
- expect(timeoutSpy).toHaveBeenCalledWith(
915
- expect.any(Function),
916
- TooltipAppearanceDelay,
917
- );
918
- jest.runOnlyPendingTimers();
919
-
920
- // Act
921
- unmount();
922
-
923
- // Assert
924
- expect(spy).toHaveBeenCalledTimes(1);
925
- expect(spy).toHaveBeenLastCalledWith("keyup", expect.any(Function));
926
- });
927
-
928
- test("when active, escape dismisses tooltip", async () => {
929
- // Arrange
930
- const ue = userEvent.setup({
931
- advanceTimers: jest.advanceTimersByTime,
932
- });
933
- const timeoutSpy = jest.spyOn(global, "setTimeout");
934
- let activeState = false;
935
- render(
936
- <TooltipAnchor
937
- anchorRef={jest.fn()}
938
- onActiveChanged={(active: any) => {
939
- activeState = active;
940
- }}
941
- >
942
- Anchor Text
943
- </TooltipAnchor>,
944
- );
945
-
946
- // Focus the anchor
947
- await ue.tab();
948
- expect(timeoutSpy).toHaveBeenCalledWith(
949
- expect.any(Function),
950
- TooltipAppearanceDelay,
951
- );
952
- jest.runOnlyPendingTimers();
953
-
954
- // Act
955
- await ue.keyboard("{esc}");
956
- expect(timeoutSpy).toHaveBeenCalledWith(
957
- expect.any(Function),
958
- TooltipDisappearanceDelay,
959
- );
960
- jest.runOnlyPendingTimers();
961
-
962
- // Assert
963
- expect(activeState).toBe(false);
964
- });
965
-
966
- test("when active, escape stops event propagation", async () => {
967
- // Arrange
968
- const ue = userEvent.setup({
969
- advanceTimers: jest.advanceTimersByTime,
970
- });
971
- const timeoutSpy = jest.spyOn(global, "setTimeout");
972
- render(
973
- <TooltipAnchor anchorRef={jest.fn()} onActiveChanged={() => {}}>
974
- Anchor Text
975
- </TooltipAnchor>,
976
- );
977
-
978
- // Focus the anchor
979
- await ue.tab();
980
- expect(timeoutSpy).toHaveBeenCalledWith(
981
- expect.any(Function),
982
- TooltipAppearanceDelay,
983
- );
984
- jest.runOnlyPendingTimers();
985
- const event: KeyboardEvent = document.createEvent("Event") as any;
986
- const spyOnStopPropagation = jest.spyOn(event, "stopPropagation");
987
- // @ts-expect-error [FEI-5019] - TS2540 - Cannot assign to 'key' because it is a read-only property.
988
- event.key = "Escape";
989
- event.initEvent("keyup", true, true);
990
-
991
- // Act
992
- document.dispatchEvent(event);
993
- expect(timeoutSpy).toHaveBeenCalledWith(
994
- expect.any(Function),
995
- TooltipDisappearanceDelay,
996
- );
997
- jest.runOnlyPendingTimers();
998
-
999
- // Assert
1000
- expect(spyOnStopPropagation).toHaveBeenCalled();
1001
- });
1002
- });
1003
- });