@khanacademy/wonder-blocks-tooltip 1.3.1 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -977,11 +977,12 @@ class TooltipPopper extends Component {
977
977
  } = this.props;
978
978
  return /*#__PURE__*/createElement(Popper, {
979
979
  referenceElement: anchorElement,
980
+ strategy: "fixed",
980
981
  placement: placement,
981
982
  modifiers: [{
982
983
  name: "preventOverflow",
983
984
  options: {
984
- rootBoundary: "document"
985
+ rootBoundary: "viewport"
985
986
  }
986
987
  }]
987
988
  }, props => this._renderPositionedContent(props));
package/dist/index.js CHANGED
@@ -669,11 +669,12 @@ class TooltipPopper extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
669
669
  } = this.props;
670
670
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](react_popper__WEBPACK_IMPORTED_MODULE_1__["Popper"], {
671
671
  referenceElement: anchorElement,
672
+ strategy: "fixed",
672
673
  placement: placement,
673
674
  modifiers: [{
674
675
  name: "preventOverflow",
675
676
  options: {
676
- rootBoundary: "document"
677
+ rootBoundary: "viewport"
677
678
  }
678
679
  }]
679
680
  }, props => this._renderPositionedContent(props));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-tooltip",
3
- "version": "1.3.1",
3
+ "version": "1.3.5",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -15,23 +15,23 @@
15
15
  "author": "",
16
16
  "license": "MIT",
17
17
  "dependencies": {
18
- "@babel/runtime": "^7.13.10",
19
- "@khanacademy/wonder-blocks-color": "^1.1.19",
20
- "@khanacademy/wonder-blocks-core": "^3.1.5",
21
- "@khanacademy/wonder-blocks-layout": "^1.4.4",
22
- "@khanacademy/wonder-blocks-modal": "^2.1.43",
23
- "@khanacademy/wonder-blocks-spacing": "^3.0.4",
24
- "@khanacademy/wonder-blocks-typography": "^1.1.26"
18
+ "@babel/runtime": "^7.16.3",
19
+ "@khanacademy/wonder-blocks-color": "^1.1.20",
20
+ "@khanacademy/wonder-blocks-core": "^4.0.0",
21
+ "@khanacademy/wonder-blocks-layout": "^1.4.6",
22
+ "@khanacademy/wonder-blocks-modal": "^2.1.45",
23
+ "@khanacademy/wonder-blocks-spacing": "^3.0.5",
24
+ "@khanacademy/wonder-blocks-typography": "^1.1.28"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@popperjs/core": "^2.10.1",
28
28
  "aphrodite": "^1.2.5",
29
- "react": "^16.4.1",
30
- "react-dom": "^16.4.1",
29
+ "react": "16.14.0",
30
+ "react-dom": "16.14.0",
31
31
  "react-popper": "^2.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "wb-dev-build-settings": "^0.1.2"
34
+ "wb-dev-build-settings": "^0.2.0"
35
35
  },
36
- "gitHead": "9a9cc04bf2bbfb425f991a347b4f8b0d0d56e120"
36
+ "gitHead": "9ebea88533e702011165072f090a377e02fa3f0f"
37
37
  }
@@ -128,7 +128,7 @@ describe("TooltipAnchor", () => {
128
128
  });
129
129
 
130
130
  // Act
131
- const result = ref && ref.getAttribute("tabindex");
131
+ const result = ref?.getAttribute("tabindex");
132
132
 
133
133
  // Assert
134
134
  expect(result).toBe("0");
@@ -150,7 +150,7 @@ describe("TooltipAnchor", () => {
150
150
  });
151
151
 
152
152
  // Act
153
- const result = ref && ref.getAttribute("tabindex");
153
+ const result = ref?.getAttribute("tabindex");
154
154
 
155
155
  // Assert
156
156
  expect(result).toBe("-1");
@@ -174,7 +174,7 @@ describe("TooltipAnchor", () => {
174
174
  });
175
175
 
176
176
  // Act
177
- const result = ref && ref.getAttribute("tabindex");
177
+ const result = ref?.getAttribute("tabindex");
178
178
 
179
179
  // Assert
180
180
  expect(result).toBeNull();
@@ -197,11 +197,11 @@ describe("TooltipAnchor", () => {
197
197
  });
198
198
 
199
199
  // Act
200
- const tabindex = ref && ref.getAttribute("tabindex");
200
+ const tabindex = ref?.getAttribute("tabindex");
201
201
  expect(tabindex).toBe("0");
202
202
 
203
- wrapper && wrapper.setProps({force: false});
204
- const result = ref && ref.getAttribute("tabindex");
203
+ wrapper?.setProps({force: false});
204
+ const result = ref?.getAttribute("tabindex");
205
205
 
206
206
  // Assert
207
207
  expect(result).toBeNull();
@@ -224,7 +224,7 @@ describe("TooltipAnchor", () => {
224
224
  });
225
225
 
226
226
  // Act
227
- const result = ref && ref.getAttribute("tabindex");
227
+ const result = ref?.getAttribute("tabindex");
228
228
 
229
229
  // Assert
230
230
  expect(result).not.toBeNull();
@@ -237,6 +237,7 @@ describe("TooltipAnchor", () => {
237
237
  const {default: ActiveTracker} = await import(
238
238
  "../../util/active-tracker.js"
239
239
  );
240
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
240
241
  // Let's tell the tooltip it isn't stealing and therefore it should
241
242
  // be using a delay to show the tooltip.
242
243
  // Flow doesn't know this is a mock
@@ -265,10 +266,10 @@ describe("TooltipAnchor", () => {
265
266
  // whether focused directly or a child is focused). We have to
266
267
  // fake directly because there's no real browser here handling
267
268
  // focus and real events.
268
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
269
+ ref?.dispatchEvent(new FocusEvent("focusin"));
269
270
  // Check that we didn't go active before the delay
270
271
  expect(activeState).toBe(false);
271
- expect(setTimeout).toHaveBeenLastCalledWith(
272
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
272
273
  expect.any(Function),
273
274
  TooltipAppearanceDelay,
274
275
  );
@@ -310,7 +311,7 @@ describe("TooltipAnchor", () => {
310
311
  // whether focused directly or a child is focused). We have to
311
312
  // fake directly because there's no real browser here handling
312
313
  // focus and real events.
313
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
314
+ ref?.dispatchEvent(new FocusEvent("focusin"));
314
315
 
315
316
  // Assert
316
317
  expect(activeState).toBe(true);
@@ -320,6 +321,7 @@ describe("TooltipAnchor", () => {
320
321
  describe("loses keyboard focus", () => {
321
322
  test("active state was not stolen, active is set to false with delay", async () => {
322
323
  // Arrange
324
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
323
325
  let activeState = false;
324
326
  const ref = await new Promise((resolve) => {
325
327
  const nodes = (
@@ -335,8 +337,8 @@ describe("TooltipAnchor", () => {
335
337
  mount(nodes);
336
338
  });
337
339
 
338
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
339
- expect(setTimeout).toHaveBeenLastCalledWith(
340
+ ref?.dispatchEvent(new FocusEvent("focusin"));
341
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
340
342
  expect.any(Function),
341
343
  TooltipAppearanceDelay,
342
344
  );
@@ -344,9 +346,9 @@ describe("TooltipAnchor", () => {
344
346
  expect(activeState).toBe(true);
345
347
 
346
348
  // Act
347
- ref && ref.dispatchEvent(new FocusEvent("focusout"));
349
+ ref?.dispatchEvent(new FocusEvent("focusout"));
348
350
  expect(activeState).toBe(true);
349
- expect(setTimeout).toHaveBeenLastCalledWith(
351
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
350
352
  expect.any(Function),
351
353
  TooltipDisappearanceDelay,
352
354
  );
@@ -358,6 +360,7 @@ describe("TooltipAnchor", () => {
358
360
 
359
361
  test("active state was not stolen, gives up active state", async () => {
360
362
  // Arrange
363
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
361
364
  const {default: ActiveTracker} = await import(
362
365
  "../../util/active-tracker.js"
363
366
  );
@@ -380,8 +383,8 @@ describe("TooltipAnchor", () => {
380
383
  mount(nodes);
381
384
  });
382
385
 
383
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
384
- expect(setTimeout).toHaveBeenLastCalledWith(
386
+ ref?.dispatchEvent(new FocusEvent("focusin"));
387
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
385
388
  expect.any(Function),
386
389
  TooltipAppearanceDelay,
387
390
  );
@@ -389,9 +392,9 @@ describe("TooltipAnchor", () => {
389
392
  expect(activeState).toBe(true);
390
393
 
391
394
  // Act
392
- ref && ref.dispatchEvent(new FocusEvent("focusout"));
395
+ ref?.dispatchEvent(new FocusEvent("focusout"));
393
396
  expect(activeState).toBe(true);
394
- expect(setTimeout).toHaveBeenLastCalledWith(
397
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
395
398
  expect.any(Function),
396
399
  TooltipDisappearanceDelay,
397
400
  );
@@ -403,6 +406,7 @@ describe("TooltipAnchor", () => {
403
406
 
404
407
  test("active state was stolen, active is set to false immediately", async () => {
405
408
  // Arrange
409
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
406
410
  let wrapper;
407
411
  let activeState = false;
408
412
  const ref = await new Promise((resolve) => {
@@ -419,8 +423,8 @@ describe("TooltipAnchor", () => {
419
423
  wrapper = mount(nodes);
420
424
  });
421
425
 
422
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
423
- expect(setTimeout).toHaveBeenLastCalledWith(
426
+ ref?.dispatchEvent(new FocusEvent("focusin"));
427
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
424
428
  expect.any(Function),
425
429
  TooltipAppearanceDelay,
426
430
  );
@@ -428,8 +432,8 @@ describe("TooltipAnchor", () => {
428
432
  expect(activeState).toBe(true);
429
433
 
430
434
  // Act
431
- ref && ref.dispatchEvent(new FocusEvent("focusout"));
432
- wrapper && wrapper.instance().activeStateStolen();
435
+ ref?.dispatchEvent(new FocusEvent("focusout"));
436
+ wrapper?.instance().activeStateStolen();
433
437
 
434
438
  // Assert
435
439
  expect(activeState).toBe(false);
@@ -437,6 +441,7 @@ describe("TooltipAnchor", () => {
437
441
 
438
442
  test("active state was stolen, so it does not have it to give up", async () => {
439
443
  // Arrange
444
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
440
445
  const {default: ActiveTracker} = await import(
441
446
  "../../util/active-tracker.js"
442
447
  );
@@ -459,8 +464,8 @@ describe("TooltipAnchor", () => {
459
464
  );
460
465
  wrapper = mount(nodes);
461
466
  });
462
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
463
- expect(setTimeout).toHaveBeenLastCalledWith(
467
+ ref?.dispatchEvent(new FocusEvent("focusin"));
468
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
464
469
  expect.any(Function),
465
470
  TooltipAppearanceDelay,
466
471
  );
@@ -468,8 +473,8 @@ describe("TooltipAnchor", () => {
468
473
  expect(activeState).toBe(true);
469
474
 
470
475
  // Act
471
- ref && ref.dispatchEvent(new FocusEvent("focusout"));
472
- wrapper && wrapper.instance().activeStateStolen();
476
+ ref?.dispatchEvent(new FocusEvent("focusout"));
477
+ wrapper?.instance().activeStateStolen();
473
478
 
474
479
  // Assert
475
480
  expect(mockTracker.giveup).not.toHaveBeenCalled();
@@ -477,6 +482,7 @@ describe("TooltipAnchor", () => {
477
482
 
478
483
  test("if hovered, remains active", async () => {
479
484
  // Arrange
485
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
480
486
  let activeState = false;
481
487
  const ref = await new Promise((resolve) => {
482
488
  const nodes = (
@@ -491,30 +497,29 @@ describe("TooltipAnchor", () => {
491
497
  );
492
498
  mount(nodes);
493
499
  });
494
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
495
- expect(setTimeout).toHaveBeenLastCalledWith(
500
+ ref?.dispatchEvent(new FocusEvent("focusin"));
501
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
496
502
  expect.any(Function),
497
503
  TooltipAppearanceDelay,
498
504
  );
499
505
  jest.runOnlyPendingTimers();
500
- // Flow doesn't know we added jest mocks to this
501
- // $FlowFixMe[prop-missing]
502
- setTimeout.mockClear();
503
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
506
+ timeoutSpy.mockClear();
507
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
504
508
 
505
509
  // Act
506
- ref && ref.dispatchEvent(new FocusEvent("focusout"));
510
+ ref?.dispatchEvent(new FocusEvent("focusout"));
507
511
 
508
512
  // Assert
509
513
  // Make sure that we're not delay hiding as well.
510
514
  expect(activeState).toBe(true);
511
- expect(setTimeout).not.toHaveBeenCalled();
515
+ expect(timeoutSpy).not.toHaveBeenCalled();
512
516
  });
513
517
  });
514
518
 
515
519
  describe("is hovered", () => {
516
520
  test("active state was not stolen, delays set active", async () => {
517
521
  // Arrange
522
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
518
523
  const {default: ActiveTracker} = await import(
519
524
  "../../util/active-tracker.js"
520
525
  );
@@ -541,10 +546,10 @@ describe("TooltipAnchor", () => {
541
546
  });
542
547
 
543
548
  // Act
544
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
549
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
545
550
  // Check that we didn't go active before the delay
546
551
  expect(activeState).toBe(false);
547
- expect(setTimeout).toHaveBeenLastCalledWith(
552
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
548
553
  expect.any(Function),
549
554
  TooltipAppearanceDelay,
550
555
  );
@@ -582,7 +587,7 @@ describe("TooltipAnchor", () => {
582
587
  });
583
588
 
584
589
  // Act
585
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
590
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
586
591
 
587
592
  // Assert
588
593
  expect(activeState).toBe(true);
@@ -592,6 +597,7 @@ describe("TooltipAnchor", () => {
592
597
  describe("is unhovered", () => {
593
598
  test("active state was not stolen, active is set to false with delay", async () => {
594
599
  // Arrange
600
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
595
601
  let activeState = false;
596
602
  const ref = await new Promise((resolve) => {
597
603
  const nodes = (
@@ -606,8 +612,8 @@ describe("TooltipAnchor", () => {
606
612
  );
607
613
  mount(nodes);
608
614
  });
609
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
610
- expect(setTimeout).toHaveBeenLastCalledWith(
615
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
616
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
611
617
  expect.any(Function),
612
618
  TooltipAppearanceDelay,
613
619
  );
@@ -615,9 +621,9 @@ describe("TooltipAnchor", () => {
615
621
  expect(activeState).toBe(true);
616
622
 
617
623
  // Act
618
- ref && ref.dispatchEvent(new MouseEvent("mouseleave"));
624
+ ref?.dispatchEvent(new MouseEvent("mouseleave"));
619
625
  expect(activeState).toBe(true);
620
- expect(setTimeout).toHaveBeenLastCalledWith(
626
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
621
627
  expect.any(Function),
622
628
  TooltipDisappearanceDelay,
623
629
  );
@@ -629,6 +635,7 @@ describe("TooltipAnchor", () => {
629
635
 
630
636
  test("active state was not stolen, gives up active state", async () => {
631
637
  // Arrange
638
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
632
639
  const {default: ActiveTracker} = await import(
633
640
  "../../util/active-tracker.js"
634
641
  );
@@ -649,8 +656,8 @@ describe("TooltipAnchor", () => {
649
656
  );
650
657
  mount(nodes);
651
658
  });
652
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
653
- expect(setTimeout).toHaveBeenLastCalledWith(
659
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
660
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
654
661
  expect.any(Function),
655
662
  TooltipAppearanceDelay,
656
663
  );
@@ -658,9 +665,9 @@ describe("TooltipAnchor", () => {
658
665
  expect(activeState).toBe(true);
659
666
 
660
667
  // Act
661
- ref && ref.dispatchEvent(new MouseEvent("mouseleave"));
668
+ ref?.dispatchEvent(new MouseEvent("mouseleave"));
662
669
  expect(activeState).toBe(true);
663
- expect(setTimeout).toHaveBeenLastCalledWith(
670
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
664
671
  expect.any(Function),
665
672
  TooltipDisappearanceDelay,
666
673
  );
@@ -672,6 +679,7 @@ describe("TooltipAnchor", () => {
672
679
 
673
680
  test("active state was stolen, active is set to false immediately", async () => {
674
681
  // Arrange
682
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
675
683
  let wrapper;
676
684
  let activeState = false;
677
685
  const ref = await new Promise((resolve) => {
@@ -687,8 +695,8 @@ describe("TooltipAnchor", () => {
687
695
  );
688
696
  wrapper = mount(nodes);
689
697
  });
690
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
691
- expect(setTimeout).toHaveBeenLastCalledWith(
698
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
699
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
692
700
  expect.any(Function),
693
701
  TooltipAppearanceDelay,
694
702
  );
@@ -696,8 +704,8 @@ describe("TooltipAnchor", () => {
696
704
  expect(activeState).toBe(true);
697
705
 
698
706
  // Act
699
- ref && ref.dispatchEvent(new MouseEvent("mouseleave"));
700
- wrapper && wrapper.instance().activeStateStolen();
707
+ ref?.dispatchEvent(new MouseEvent("mouseleave"));
708
+ wrapper?.instance().activeStateStolen();
701
709
 
702
710
  // Assert
703
711
  expect(activeState).toBe(false);
@@ -705,6 +713,7 @@ describe("TooltipAnchor", () => {
705
713
 
706
714
  test("active state was stolen, so it does not have it to give up", async () => {
707
715
  // Arrange
716
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
708
717
  const {default: ActiveTracker} = await import(
709
718
  "../../util/active-tracker.js"
710
719
  );
@@ -727,8 +736,8 @@ describe("TooltipAnchor", () => {
727
736
  );
728
737
  wrapper = mount(nodes);
729
738
  });
730
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
731
- expect(setTimeout).toHaveBeenLastCalledWith(
739
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
740
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
732
741
  expect.any(Function),
733
742
  TooltipAppearanceDelay,
734
743
  );
@@ -736,8 +745,8 @@ describe("TooltipAnchor", () => {
736
745
  expect(activeState).toBe(true);
737
746
 
738
747
  // Act
739
- ref && ref.dispatchEvent(new MouseEvent("mouseleave"));
740
- wrapper && wrapper.instance().activeStateStolen();
748
+ ref?.dispatchEvent(new MouseEvent("mouseleave"));
749
+ wrapper?.instance().activeStateStolen();
741
750
 
742
751
  // Assert
743
752
  expect(mockTracker.giveup).not.toHaveBeenCalled();
@@ -745,6 +754,7 @@ describe("TooltipAnchor", () => {
745
754
 
746
755
  test("if focused, remains active", async () => {
747
756
  // Arrange
757
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
748
758
  let activeState = false;
749
759
  const ref = await new Promise((resolve) => {
750
760
  const nodes = (
@@ -759,30 +769,29 @@ describe("TooltipAnchor", () => {
759
769
  );
760
770
  mount(nodes);
761
771
  });
762
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
763
- expect(setTimeout).toHaveBeenLastCalledWith(
772
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
773
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
764
774
  expect.any(Function),
765
775
  TooltipAppearanceDelay,
766
776
  );
767
777
  jest.runOnlyPendingTimers();
768
- // Flow doesn't know we added jest mocks to this
769
- // $FlowFixMe[prop-missing]
770
- setTimeout.mockClear();
771
- ref && ref.dispatchEvent(new FocusEvent("focusin"));
778
+ timeoutSpy.mockClear();
779
+ ref?.dispatchEvent(new FocusEvent("focusin"));
772
780
 
773
781
  // Act
774
- ref && ref.dispatchEvent(new MouseEvent("mouseleave"));
782
+ ref?.dispatchEvent(new MouseEvent("mouseleave"));
775
783
 
776
784
  // Assert
777
785
  // Make sure that we're not delay hiding as well.
778
786
  expect(activeState).toBe(true);
779
- expect(setTimeout).not.toHaveBeenCalled();
787
+ expect(timeoutSpy).not.toHaveBeenCalled();
780
788
  });
781
789
  });
782
790
 
783
791
  describe("dismiss behavior", () => {
784
792
  test("subscribes to keydown event on active", async () => {
785
793
  // Arrange
794
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
786
795
  const spy = jest.spyOn(document, "addEventListener");
787
796
  const ref = await new Promise((resolve) => {
788
797
  const nodes = (
@@ -797,8 +806,8 @@ describe("TooltipAnchor", () => {
797
806
  });
798
807
 
799
808
  // Act
800
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
801
- expect(setTimeout).toHaveBeenLastCalledWith(
809
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
810
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
802
811
  expect.any(Function),
803
812
  TooltipAppearanceDelay,
804
813
  );
@@ -811,6 +820,7 @@ describe("TooltipAnchor", () => {
811
820
 
812
821
  test("does not subscribe to keydown event if already active", async () => {
813
822
  // Arrange
823
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
814
824
  const spy = jest.spyOn(document, "addEventListener");
815
825
  const ref = await new Promise((resolve) => {
816
826
  const nodes = (
@@ -824,8 +834,8 @@ describe("TooltipAnchor", () => {
824
834
  mount(nodes);
825
835
  });
826
836
 
827
- ref && ref.dispatchEvent(new KeyboardEvent("focusin"));
828
- expect(setTimeout).toHaveBeenLastCalledWith(
837
+ ref?.dispatchEvent(new KeyboardEvent("focusin"));
838
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
829
839
  expect.any(Function),
830
840
  TooltipAppearanceDelay,
831
841
  );
@@ -835,7 +845,7 @@ describe("TooltipAnchor", () => {
835
845
  spy.mockClear();
836
846
 
837
847
  // Act
838
- ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
848
+ ref?.dispatchEvent(new MouseEvent("mouseenter"));
839
849
 
840
850
  // Assert
841
851
  expect(spy).not.toHaveBeenCalled();
@@ -843,6 +853,7 @@ describe("TooltipAnchor", () => {
843
853
 
844
854
  test("unsubscribes from keydown event on inactive", async () => {
845
855
  // Arrange
856
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
846
857
  const spy = jest.spyOn(document, "removeEventListener");
847
858
  const ref = await new Promise((resolve) => {
848
859
  const nodes = (
@@ -856,16 +867,16 @@ describe("TooltipAnchor", () => {
856
867
  mount(nodes);
857
868
  });
858
869
 
859
- ref && ref.dispatchEvent(new KeyboardEvent("focusin"));
860
- expect(setTimeout).toHaveBeenLastCalledWith(
870
+ ref?.dispatchEvent(new KeyboardEvent("focusin"));
871
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
861
872
  expect.any(Function),
862
873
  TooltipAppearanceDelay,
863
874
  );
864
875
  jest.runOnlyPendingTimers();
865
876
 
866
877
  // Act
867
- ref && ref.dispatchEvent(new KeyboardEvent("focusout"));
868
- expect(setTimeout).toHaveBeenLastCalledWith(
878
+ ref?.dispatchEvent(new KeyboardEvent("focusout"));
879
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
869
880
  expect.any(Function),
870
881
  TooltipDisappearanceDelay,
871
882
  );
@@ -878,6 +889,7 @@ describe("TooltipAnchor", () => {
878
889
 
879
890
  test("unsubscribes from keydown event on unmount", async () => {
880
891
  // Arrange
892
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
881
893
  let wrapper;
882
894
  const spy = jest.spyOn(document, "removeEventListener");
883
895
  const ref = await new Promise((resolve) => {
@@ -892,15 +904,15 @@ describe("TooltipAnchor", () => {
892
904
  wrapper = mount(nodes);
893
905
  });
894
906
 
895
- ref && ref.dispatchEvent(new KeyboardEvent("focusin"));
896
- expect(setTimeout).toHaveBeenLastCalledWith(
907
+ ref?.dispatchEvent(new KeyboardEvent("focusin"));
908
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
897
909
  expect.any(Function),
898
910
  TooltipAppearanceDelay,
899
911
  );
900
912
  jest.runOnlyPendingTimers();
901
913
 
902
914
  // Act
903
- wrapper && wrapper.unmount();
915
+ wrapper?.unmount();
904
916
 
905
917
  // Assert
906
918
  expect(spy).toHaveBeenCalledTimes(1);
@@ -909,6 +921,7 @@ describe("TooltipAnchor", () => {
909
921
 
910
922
  test("when active, escape dismisses tooltip", async () => {
911
923
  // Arrange
924
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
912
925
  let activeState = false;
913
926
  const ref = await new Promise((resolve) => {
914
927
  const nodes = (
@@ -924,20 +937,22 @@ describe("TooltipAnchor", () => {
924
937
  mount(nodes);
925
938
  });
926
939
 
927
- ref && ref.dispatchEvent(new KeyboardEvent("focusin"));
928
- expect(setTimeout).toHaveBeenLastCalledWith(
940
+ ref?.dispatchEvent(new KeyboardEvent("focusin"));
941
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
929
942
  expect.any(Function),
930
943
  TooltipAppearanceDelay,
931
944
  );
932
945
  jest.runOnlyPendingTimers();
933
946
  const event: KeyboardEvent = (document.createEvent("Event"): any);
947
+ // $FlowIgnore[cannot-write]
934
948
  event.key = "Escape";
949
+ // $FlowIgnore[cannot-write]
935
950
  event.which = 27;
936
951
  event.initEvent("keyup", true, true);
937
952
 
938
953
  // Act
939
954
  document.dispatchEvent(event);
940
- expect(setTimeout).toHaveBeenLastCalledWith(
955
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
941
956
  expect.any(Function),
942
957
  TooltipDisappearanceDelay,
943
958
  );
@@ -949,6 +964,7 @@ describe("TooltipAnchor", () => {
949
964
 
950
965
  test("when active, escape stops event propagation", async () => {
951
966
  // Arrange
967
+ const timeoutSpy = jest.spyOn(global, "setTimeout");
952
968
  const ref = await new Promise((resolve) => {
953
969
  const nodes = (
954
970
  <TooltipAnchor
@@ -961,20 +977,21 @@ describe("TooltipAnchor", () => {
961
977
  mount(nodes);
962
978
  });
963
979
 
964
- ref && ref.dispatchEvent(new KeyboardEvent("focusin"));
965
- expect(setTimeout).toHaveBeenLastCalledWith(
980
+ ref?.dispatchEvent(new KeyboardEvent("focusin"));
981
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
966
982
  expect.any(Function),
967
983
  TooltipAppearanceDelay,
968
984
  );
969
985
  jest.runOnlyPendingTimers();
970
986
  const event: KeyboardEvent = (document.createEvent("Event"): any);
971
987
  const spyOnStopPropagation = jest.spyOn(event, "stopPropagation");
988
+ // $FlowIgnore[cannot-write]
972
989
  event.key = "Escape";
973
990
  event.initEvent("keyup", true, true);
974
991
 
975
992
  // Act
976
993
  document.dispatchEvent(event);
977
- expect(setTimeout).toHaveBeenLastCalledWith(
994
+ expect(timeoutSpy).toHaveBeenLastCalledWith(
978
995
  expect.any(Function),
979
996
  TooltipDisappearanceDelay,
980
997
  );
@@ -2,8 +2,6 @@
2
2
  import * as React from "react";
3
3
 
4
4
  import {render, screen, fireEvent} from "@testing-library/react";
5
- // eslint-disable-next-line import/no-unassigned-import
6
- import "@testing-library/jest-dom/extend-expect";
7
5
  import userEvent from "@testing-library/user-event";
8
6
 
9
7
  import Tooltip from "../tooltip.js";
@@ -56,6 +54,8 @@ describe("tooltip integration tests", () => {
56
54
  const anchor = screen.getByText("an anchor");
57
55
  userEvent.hover(anchor);
58
56
  // hover on bubble to keep it active
57
+ // Need to run the timers or we won't get the bubble wrapper to show.
58
+ jest.runAllTimers();
59
59
  const bubbleWrapper = await screen.findByRole("tooltip");
60
60
  userEvent.unhover(anchor);
61
61
 
@@ -77,7 +77,8 @@ const TRACKER = new ActiveTracker();
77
77
 
78
78
  export default class TooltipAnchor
79
79
  extends React.Component<Props, State>
80
- implements IActiveTrackerSubscriber {
80
+ implements IActiveTrackerSubscriber
81
+ {
81
82
  _weSetFocusivity: ?boolean;
82
83
  _anchorNode: ?Element;
83
84
  _focused: boolean;
@@ -84,12 +84,13 @@ export default class TooltipPopper extends React.Component<Props> {
84
84
  return (
85
85
  <Popper
86
86
  referenceElement={anchorElement}
87
+ strategy="fixed"
87
88
  placement={placement}
88
89
  modifiers={[
89
90
  {
90
91
  name: "preventOverflow",
91
92
  options: {
92
- rootBoundary: "document",
93
+ rootBoundary: "viewport",
93
94
  },
94
95
  },
95
96
  ]}
@@ -363,12 +363,8 @@ export default class TooltipTail extends React.Component<Props> {
363
363
  }
364
364
 
365
365
  _renderArrow(): React.Node {
366
- const {
367
- trimlinePoints,
368
- points,
369
- height,
370
- width,
371
- } = this._calculateDimensionsFromPlacement();
366
+ const {trimlinePoints, points, height, width} =
367
+ this._calculateDimensionsFromPlacement();
372
368
 
373
369
  const {color} = this.props;
374
370
 
@@ -9,11 +9,11 @@ import type {Placement} from "@khanacademy/wonder-blocks-tooltip";
9
9
  import type {StoryComponentType} from "@storybook/react";
10
10
 
11
11
  export default {
12
- title: "Tooltip",
12
+ title: "Floating/Tooltip",
13
13
  };
14
14
 
15
15
  const BaseTooltipExample = ({placement}: {|placement: Placement|}) => {
16
- const inputRef = React.useRef<null | HTMLElement>(null);
16
+ const inputRef = React.useRef(null);
17
17
  React.useEffect(() => {
18
18
  if (inputRef.current) {
19
19
  inputRef.current.focus();
@@ -9,12 +9,10 @@ import * as React from "react";
9
9
  import * as ReactDOM from "react-dom";
10
10
 
11
11
  import type {PopperChildrenProps} from "react-popper";
12
- import type {getRefFn} from "./types.js";
13
12
 
14
13
  type PopperRef = $PropertyType<PopperChildrenProps, "ref">;
15
14
 
16
15
  export default class RefTracker {
17
- updateRef: getRefFn;
18
16
  _lastRef: ?HTMLElement;
19
17
  _targetFn: ?(?HTMLElement) => void;
20
18