@khanacademy/wonder-blocks-tooltip 1.3.4 → 1.3.8
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/CHANGELOG.md +23 -0
- package/dist/es/index.js +44 -45
- package/package.json +10 -11
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +2 -2
- package/src/components/__tests__/tooltip-anchor.test.js +97 -79
- package/src/components/__tests__/tooltip-bubble.test.js +1 -0
- package/src/components/__tests__/tooltip-popper.test.js +1 -0
- package/src/components/__tests__/tooltip.integration.test.js +2 -2
- package/src/components/__tests__/tooltip.test.js +1 -0
- package/src/components/tooltip-anchor.js +2 -1
- package/src/components/tooltip-tail.js +2 -6
- package/src/components/tooltip.stories.js +39 -4
- package/src/util/__tests__/ref-tracker.test.js +1 -0
- package/src/util/ref-tracker.js +0 -2
- package/LICENSE +0 -21
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @khanacademy/wonder-blocks-tooltip
|
|
2
|
+
|
|
3
|
+
## 1.3.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @khanacademy/wonder-blocks-modal@2.2.2
|
|
8
|
+
|
|
9
|
+
## 1.3.7
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- @khanacademy/wonder-blocks-core@4.2.1
|
|
14
|
+
- @khanacademy/wonder-blocks-layout@1.4.7
|
|
15
|
+
- @khanacademy/wonder-blocks-modal@2.2.1
|
|
16
|
+
- @khanacademy/wonder-blocks-typography@1.1.29
|
|
17
|
+
|
|
18
|
+
## 1.3.6
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [e7bbf149]
|
|
23
|
+
- @khanacademy/wonder-blocks-modal@2.2.0
|
package/dist/es/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as ReactDOM from 'react-dom';
|
|
3
3
|
import { Text as Text$1, View, UniqueIDProvider } from '@khanacademy/wonder-blocks-core';
|
|
4
4
|
import { maybeGetPortalMountedModalHostElement } from '@khanacademy/wonder-blocks-modal';
|
|
5
|
-
import {
|
|
5
|
+
import { StyleSheet, css } from 'aphrodite';
|
|
6
6
|
import Colors from '@khanacademy/wonder-blocks-color';
|
|
7
7
|
import Spacing from '@khanacademy/wonder-blocks-spacing';
|
|
8
8
|
import _extends from '@babel/runtime/helpers/extends';
|
|
@@ -118,7 +118,7 @@ const TooltipDisappearanceDelay = 75;
|
|
|
118
118
|
* positioning and displaying tooltips.
|
|
119
119
|
*/
|
|
120
120
|
const TRACKER = new ActiveTracker();
|
|
121
|
-
class TooltipAnchor extends Component {
|
|
121
|
+
class TooltipAnchor extends React.Component {
|
|
122
122
|
constructor(props) {
|
|
123
123
|
super(props);
|
|
124
124
|
|
|
@@ -182,7 +182,7 @@ class TooltipAnchor extends Component {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
componentDidMount() {
|
|
185
|
-
const anchorNode = findDOMNode(this); // This should never happen, but we have this check here to make flow
|
|
185
|
+
const anchorNode = ReactDOM.findDOMNode(this); // This should never happen, but we have this check here to make flow
|
|
186
186
|
// happy and ensure that if this does happen, we'll know about it.
|
|
187
187
|
|
|
188
188
|
if (anchorNode instanceof Text) {
|
|
@@ -327,13 +327,13 @@ class TooltipAnchor extends Component {
|
|
|
327
327
|
const {
|
|
328
328
|
children
|
|
329
329
|
} = this.props;
|
|
330
|
-
return typeof children === "string" ? /*#__PURE__*/createElement(Text$1, null, children) : children;
|
|
330
|
+
return typeof children === "string" ? /*#__PURE__*/React.createElement(Text$1, null, children) : children;
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
_renderAccessibleChildren(ids) {
|
|
334
334
|
const anchorableChildren = this._renderAnchorableChildren();
|
|
335
335
|
|
|
336
|
-
return /*#__PURE__*/cloneElement(anchorableChildren, {
|
|
336
|
+
return /*#__PURE__*/React.cloneElement(anchorableChildren, {
|
|
337
337
|
"aria-describedby": ids.get(TooltipAnchor.ariaContentId)
|
|
338
338
|
});
|
|
339
339
|
}
|
|
@@ -359,7 +359,7 @@ TooltipAnchor.ariaContentId = "aria-content";
|
|
|
359
359
|
// TODO(somewhatabstract): Replace this really basic unique ID work with
|
|
360
360
|
// something SSR-friendly and more robust.
|
|
361
361
|
let tempIdCounter = 0;
|
|
362
|
-
class TooltipTail extends Component {
|
|
362
|
+
class TooltipTail extends React.Component {
|
|
363
363
|
_calculateDimensionsFromPlacement() {
|
|
364
364
|
const {
|
|
365
365
|
placement
|
|
@@ -476,7 +476,7 @@ class TooltipTail extends Component {
|
|
|
476
476
|
offsetShadowX
|
|
477
477
|
} = position;
|
|
478
478
|
const dropShadowFilterId = `tooltip-dropshadow-${placement}-${tempIdCounter++}`;
|
|
479
|
-
return [/*#__PURE__*/createElement("filter", {
|
|
479
|
+
return [/*#__PURE__*/React.createElement("filter", {
|
|
480
480
|
key: "filter",
|
|
481
481
|
id: dropShadowFilterId // Height and width tell the filter how big of a canvas to
|
|
482
482
|
// draw based on its parent size. i.e. 2 times bigger.
|
|
@@ -491,10 +491,10 @@ class TooltipTail extends Component {
|
|
|
491
491
|
,
|
|
492
492
|
x: x,
|
|
493
493
|
y: y
|
|
494
|
-
}, /*#__PURE__*/createElement("feGaussianBlur", {
|
|
494
|
+
}, /*#__PURE__*/React.createElement("feGaussianBlur", {
|
|
495
495
|
in: "SourceAlpha",
|
|
496
496
|
stdDeviation: Spacing.xxSmall_6 / 2
|
|
497
|
-
}), /*#__PURE__*/createElement("feComponentTransfer", null, /*#__PURE__*/createElement("feFuncA", {
|
|
497
|
+
}), /*#__PURE__*/React.createElement("feComponentTransfer", null, /*#__PURE__*/React.createElement("feFuncA", {
|
|
498
498
|
type: "linear",
|
|
499
499
|
slope: "0.3"
|
|
500
500
|
}))),
|
|
@@ -512,10 +512,10 @@ class TooltipTail extends Component {
|
|
|
512
512
|
*
|
|
513
513
|
* See styles below for why we offset the arrow.
|
|
514
514
|
*/
|
|
515
|
-
createElement("g", {
|
|
515
|
+
React.createElement("g", {
|
|
516
516
|
key: "dropshadow",
|
|
517
517
|
transform: `translate(${offsetShadowX},5.5)`
|
|
518
|
-
}, /*#__PURE__*/createElement("polyline", {
|
|
518
|
+
}, /*#__PURE__*/React.createElement("polyline", {
|
|
519
519
|
fill: Colors.offBlack16,
|
|
520
520
|
points: points.join(" "),
|
|
521
521
|
stroke: Colors.offBlack32,
|
|
@@ -656,23 +656,23 @@ class TooltipTail extends Component {
|
|
|
656
656
|
const {
|
|
657
657
|
color
|
|
658
658
|
} = this.props;
|
|
659
|
-
return /*#__PURE__*/createElement("svg", {
|
|
660
|
-
className: css(styles.arrow),
|
|
659
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
660
|
+
className: css(styles$2.arrow),
|
|
661
661
|
style: this._getArrowStyle(),
|
|
662
662
|
width: width,
|
|
663
663
|
height: height
|
|
664
|
-
}, this._maybeRenderDropshadow(points), /*#__PURE__*/createElement("polyline", {
|
|
664
|
+
}, this._maybeRenderDropshadow(points), /*#__PURE__*/React.createElement("polyline", {
|
|
665
665
|
fill: Colors[color],
|
|
666
666
|
stroke: Colors[color],
|
|
667
667
|
points: points.join(" ")
|
|
668
|
-
}), /*#__PURE__*/createElement("polyline", {
|
|
668
|
+
}), /*#__PURE__*/React.createElement("polyline", {
|
|
669
669
|
// Redraw the stroke on top of the background color,
|
|
670
670
|
// so that the ends aren't extra dark where they meet
|
|
671
671
|
// the border of the tooltip.
|
|
672
672
|
fill: Colors[color],
|
|
673
673
|
points: points.join(" "),
|
|
674
674
|
stroke: Colors.offBlack16
|
|
675
|
-
}), /*#__PURE__*/createElement("polyline", {
|
|
675
|
+
}), /*#__PURE__*/React.createElement("polyline", {
|
|
676
676
|
stroke: Colors[color],
|
|
677
677
|
points: trimlinePoints.join(" ")
|
|
678
678
|
}));
|
|
@@ -684,8 +684,8 @@ class TooltipTail extends Component {
|
|
|
684
684
|
placement,
|
|
685
685
|
updateRef
|
|
686
686
|
} = this.props;
|
|
687
|
-
return /*#__PURE__*/createElement(View, {
|
|
688
|
-
style: [styles.tailContainer, _extends({}, offset), this._getContainerStyle()],
|
|
687
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
688
|
+
style: [styles$2.tailContainer, _extends({}, offset), this._getContainerStyle()],
|
|
689
689
|
"data-placement": placement,
|
|
690
690
|
ref: updateRef
|
|
691
691
|
}, this._renderArrow());
|
|
@@ -706,7 +706,7 @@ const DISTANCE_FROM_ANCHOR = Spacing.xSmall_8;
|
|
|
706
706
|
const MIN_DISTANCE_FROM_CORNERS = Spacing.xSmall_8;
|
|
707
707
|
const ARROW_WIDTH = Spacing.large_24;
|
|
708
708
|
const ARROW_HEIGHT = Spacing.small_12;
|
|
709
|
-
const styles = StyleSheet.create({
|
|
709
|
+
const styles$2 = StyleSheet.create({
|
|
710
710
|
/**
|
|
711
711
|
* Container
|
|
712
712
|
*/
|
|
@@ -724,7 +724,7 @@ const styles = StyleSheet.create({
|
|
|
724
724
|
}
|
|
725
725
|
});
|
|
726
726
|
|
|
727
|
-
class TooltipBubble extends Component {
|
|
727
|
+
class TooltipBubble extends React.Component {
|
|
728
728
|
constructor(...args) {
|
|
729
729
|
super(...args);
|
|
730
730
|
this.state = {
|
|
@@ -758,7 +758,7 @@ class TooltipBubble extends Component {
|
|
|
758
758
|
updateTailRef,
|
|
759
759
|
tailOffset
|
|
760
760
|
} = this.props;
|
|
761
|
-
return /*#__PURE__*/createElement(View, {
|
|
761
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
762
762
|
id: id,
|
|
763
763
|
role: "tooltip",
|
|
764
764
|
"data-placement": placement,
|
|
@@ -766,9 +766,9 @@ class TooltipBubble extends Component {
|
|
|
766
766
|
onMouseLeave: this.handleMouseLeave,
|
|
767
767
|
ref: updateBubbleRef,
|
|
768
768
|
style: [isReferenceHidden && styles$1.hide, styles$1.bubble, styles$1[`content-${placement}`], style]
|
|
769
|
-
}, /*#__PURE__*/createElement(View, {
|
|
769
|
+
}, /*#__PURE__*/React.createElement(View, {
|
|
770
770
|
style: styles$1.content
|
|
771
|
-
}, children), /*#__PURE__*/createElement(TooltipTail, {
|
|
771
|
+
}, children), /*#__PURE__*/React.createElement(TooltipTail, {
|
|
772
772
|
updateRef: updateTailRef,
|
|
773
773
|
placement: placement,
|
|
774
774
|
offset: tailOffset
|
|
@@ -823,7 +823,7 @@ const styles$1 = StyleSheet.create({
|
|
|
823
823
|
* This component is used to provide the content that is to be rendered in the
|
|
824
824
|
* tooltip bubble.
|
|
825
825
|
*/
|
|
826
|
-
class TooltipContent extends Component {
|
|
826
|
+
class TooltipContent extends React.Component {
|
|
827
827
|
_renderTitle() {
|
|
828
828
|
const {
|
|
829
829
|
title
|
|
@@ -831,7 +831,7 @@ class TooltipContent extends Component {
|
|
|
831
831
|
|
|
832
832
|
if (title) {
|
|
833
833
|
if (typeof title === "string") {
|
|
834
|
-
return /*#__PURE__*/createElement(HeadingSmall, null, title);
|
|
834
|
+
return /*#__PURE__*/React.createElement(HeadingSmall, null, title);
|
|
835
835
|
} else {
|
|
836
836
|
return title;
|
|
837
837
|
}
|
|
@@ -846,7 +846,7 @@ class TooltipContent extends Component {
|
|
|
846
846
|
} = this.props;
|
|
847
847
|
|
|
848
848
|
if (typeof children === "string") {
|
|
849
|
-
return /*#__PURE__*/createElement(LabelMedium, null, children);
|
|
849
|
+
return /*#__PURE__*/React.createElement(LabelMedium, null, children);
|
|
850
850
|
} else {
|
|
851
851
|
return children;
|
|
852
852
|
}
|
|
@@ -857,16 +857,16 @@ class TooltipContent extends Component {
|
|
|
857
857
|
|
|
858
858
|
const children = this._renderChildren();
|
|
859
859
|
|
|
860
|
-
const containerStyle = title ? styles
|
|
861
|
-
return /*#__PURE__*/createElement(View, {
|
|
860
|
+
const containerStyle = title ? styles.withTitle : styles.withoutTitle;
|
|
861
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
862
862
|
style: containerStyle
|
|
863
|
-
}, title, title && children && /*#__PURE__*/createElement(Strut, {
|
|
863
|
+
}, title, title && children && /*#__PURE__*/React.createElement(Strut, {
|
|
864
864
|
size: Spacing.xxxSmall_4
|
|
865
865
|
}), children);
|
|
866
866
|
}
|
|
867
867
|
|
|
868
868
|
}
|
|
869
|
-
const styles
|
|
869
|
+
const styles = StyleSheet.create({
|
|
870
870
|
withoutTitle: {
|
|
871
871
|
padding: `10px ${Spacing.medium_16}px`
|
|
872
872
|
},
|
|
@@ -888,7 +888,7 @@ class RefTracker {
|
|
|
888
888
|
// We only want to update the reference if it is
|
|
889
889
|
// actually changed. Otherwise, we can trigger another render that
|
|
890
890
|
// would then update the reference again and just keep looping.
|
|
891
|
-
const domNode = findDOMNode(ref);
|
|
891
|
+
const domNode = ReactDOM.findDOMNode(ref);
|
|
892
892
|
|
|
893
893
|
if (domNode instanceof HTMLElement && domNode !== this._lastRef) {
|
|
894
894
|
this._lastRef = domNode;
|
|
@@ -918,7 +918,7 @@ class RefTracker {
|
|
|
918
918
|
* This component is a light wrapper for react-popper, allowing us to position
|
|
919
919
|
* and control the tooltip bubble location and visibility as we need.
|
|
920
920
|
*/
|
|
921
|
-
class TooltipPopper extends Component {
|
|
921
|
+
class TooltipPopper extends React.Component {
|
|
922
922
|
constructor(...args) {
|
|
923
923
|
super(...args);
|
|
924
924
|
this._bubbleRefTracker = new RefTracker();
|
|
@@ -975,7 +975,7 @@ class TooltipPopper extends Component {
|
|
|
975
975
|
anchorElement,
|
|
976
976
|
placement
|
|
977
977
|
} = this.props;
|
|
978
|
-
return /*#__PURE__*/createElement(Popper, {
|
|
978
|
+
return /*#__PURE__*/React.createElement(Popper, {
|
|
979
979
|
referenceElement: anchorElement,
|
|
980
980
|
strategy: "fixed",
|
|
981
981
|
placement: placement,
|
|
@@ -1009,7 +1009,7 @@ class TooltipPopper extends Component {
|
|
|
1009
1009
|
* - TooltipTail (renders the callout tail and shadow that points from the
|
|
1010
1010
|
* callout to the anchor content)
|
|
1011
1011
|
*/
|
|
1012
|
-
class Tooltip extends Component {
|
|
1012
|
+
class Tooltip extends React.Component {
|
|
1013
1013
|
constructor(...args) {
|
|
1014
1014
|
super(...args);
|
|
1015
1015
|
this.state = {
|
|
@@ -1034,11 +1034,11 @@ class Tooltip extends Component {
|
|
|
1034
1034
|
} = this.props;
|
|
1035
1035
|
|
|
1036
1036
|
if (typeof content === "string") {
|
|
1037
|
-
return /*#__PURE__*/createElement(TooltipContent, {
|
|
1037
|
+
return /*#__PURE__*/React.createElement(TooltipContent, {
|
|
1038
1038
|
title: title
|
|
1039
1039
|
}, content);
|
|
1040
1040
|
} else if (title) {
|
|
1041
|
-
return /*#__PURE__*/cloneElement(content, {
|
|
1041
|
+
return /*#__PURE__*/React.cloneElement(content, {
|
|
1042
1042
|
title
|
|
1043
1043
|
});
|
|
1044
1044
|
} else {
|
|
@@ -1059,10 +1059,10 @@ class Tooltip extends Component {
|
|
|
1059
1059
|
const {
|
|
1060
1060
|
placement
|
|
1061
1061
|
} = this.props;
|
|
1062
|
-
return /*#__PURE__*/createElement(TooltipPopper, {
|
|
1062
|
+
return /*#__PURE__*/React.createElement(TooltipPopper, {
|
|
1063
1063
|
anchorElement: this.state.anchorElement,
|
|
1064
1064
|
placement: placement
|
|
1065
|
-
}, props => /*#__PURE__*/createElement(TooltipBubble, {
|
|
1065
|
+
}, props => /*#__PURE__*/React.createElement(TooltipBubble, {
|
|
1066
1066
|
id: bubbleId,
|
|
1067
1067
|
style: props.style,
|
|
1068
1068
|
tailOffset: props.tailOffset,
|
|
@@ -1096,14 +1096,14 @@ class Tooltip extends Component {
|
|
|
1096
1096
|
const popperHost = this._getHost(); // TODO(kevinb): update to use ReactPopper's React 16-friendly syntax
|
|
1097
1097
|
|
|
1098
1098
|
|
|
1099
|
-
return /*#__PURE__*/createElement(Fragment, null, /*#__PURE__*/createElement(TooltipAnchor, {
|
|
1099
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TooltipAnchor, {
|
|
1100
1100
|
forceAnchorFocusivity: forceAnchorFocusivity,
|
|
1101
1101
|
anchorRef: r => this._updateAnchorElement(r),
|
|
1102
1102
|
onActiveChanged: active => this.setState({
|
|
1103
1103
|
active
|
|
1104
1104
|
}),
|
|
1105
1105
|
ids: ids
|
|
1106
|
-
}, children), popperHost && (active || activeBubble) && /*#__PURE__*/createPortal(this._renderPopper(ids), popperHost));
|
|
1106
|
+
}, children), popperHost && (active || activeBubble) && /*#__PURE__*/ReactDOM.createPortal(this._renderPopper(ids), popperHost));
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
1109
|
render() {
|
|
@@ -1116,7 +1116,7 @@ class Tooltip extends Component {
|
|
|
1116
1116
|
// need it.
|
|
1117
1117
|
return this._renderTooltipAnchor();
|
|
1118
1118
|
} else {
|
|
1119
|
-
return /*#__PURE__*/createElement(UniqueIDProvider, {
|
|
1119
|
+
return /*#__PURE__*/React.createElement(UniqueIDProvider, {
|
|
1120
1120
|
scope: "tooltip",
|
|
1121
1121
|
mockOnFirstRender: true
|
|
1122
1122
|
}, ids => this._renderTooltipAnchor(ids));
|
|
@@ -1130,5 +1130,4 @@ Tooltip.defaultProps = {
|
|
|
1130
1130
|
};
|
|
1131
1131
|
Tooltip.ariaContentId = "aria-content";
|
|
1132
1132
|
|
|
1133
|
-
export default
|
|
1134
|
-
export { TooltipContent, TooltipPopper, TooltipTail };
|
|
1133
|
+
export { TooltipContent, TooltipPopper, TooltipTail, Tooltip as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-tooltip",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.8",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"author": "",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@babel/runtime": "^7.
|
|
19
|
-
"@khanacademy/wonder-blocks-color": "^1.1.
|
|
20
|
-
"@khanacademy/wonder-blocks-core": "^
|
|
21
|
-
"@khanacademy/wonder-blocks-layout": "^1.4.
|
|
22
|
-
"@khanacademy/wonder-blocks-modal": "^2.
|
|
23
|
-
"@khanacademy/wonder-blocks-spacing": "^3.0.
|
|
24
|
-
"@khanacademy/wonder-blocks-typography": "^1.1.
|
|
18
|
+
"@babel/runtime": "^7.16.3",
|
|
19
|
+
"@khanacademy/wonder-blocks-color": "^1.1.20",
|
|
20
|
+
"@khanacademy/wonder-blocks-core": "^4.2.1",
|
|
21
|
+
"@khanacademy/wonder-blocks-layout": "^1.4.7",
|
|
22
|
+
"@khanacademy/wonder-blocks-modal": "^2.2.2",
|
|
23
|
+
"@khanacademy/wonder-blocks-spacing": "^3.0.5",
|
|
24
|
+
"@khanacademy/wonder-blocks-typography": "^1.1.29"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@popperjs/core": "^2.10.1",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"react-popper": "^2.0.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"wb-dev-build-settings": "^0.
|
|
35
|
-
}
|
|
36
|
-
"gitHead": "b6193f70c73e70fbaf76bc688dc69a47fb1d0ef3"
|
|
34
|
+
"wb-dev-build-settings": "^0.3.0"
|
|
35
|
+
}
|
|
37
36
|
}
|
|
@@ -142,8 +142,8 @@ exports[`wonder-blocks-tooltip example 3 1`] = `
|
|
|
142
142
|
|
|
143
143
|
exports[`wonder-blocks-tooltip example 4 1`] = `
|
|
144
144
|
<button
|
|
145
|
+
aria-disabled={false}
|
|
145
146
|
className=""
|
|
146
|
-
disabled={false}
|
|
147
147
|
onBlur={[Function]}
|
|
148
148
|
onClick={[Function]}
|
|
149
149
|
onDragStart={[Function]}
|
|
@@ -400,8 +400,8 @@ exports[`wonder-blocks-tooltip example 6 1`] = `
|
|
|
400
400
|
>
|
|
401
401
|
<button
|
|
402
402
|
aria-describedby="uid-tooltip-6-aria-content"
|
|
403
|
+
aria-disabled={false}
|
|
403
404
|
className=""
|
|
404
|
-
disabled={false}
|
|
405
405
|
onBlur={[Function]}
|
|
406
406
|
onClick={[Function]}
|
|
407
407
|
onDragStart={[Function]}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
6
6
|
import {mount} from "enzyme";
|
|
7
|
+
import "jest-enzyme";
|
|
7
8
|
|
|
8
9
|
import TooltipAnchor from "../tooltip-anchor.js";
|
|
9
10
|
import {
|
|
@@ -128,7 +129,7 @@ describe("TooltipAnchor", () => {
|
|
|
128
129
|
});
|
|
129
130
|
|
|
130
131
|
// Act
|
|
131
|
-
const result = ref
|
|
132
|
+
const result = ref?.getAttribute("tabindex");
|
|
132
133
|
|
|
133
134
|
// Assert
|
|
134
135
|
expect(result).toBe("0");
|
|
@@ -150,7 +151,7 @@ describe("TooltipAnchor", () => {
|
|
|
150
151
|
});
|
|
151
152
|
|
|
152
153
|
// Act
|
|
153
|
-
const result = ref
|
|
154
|
+
const result = ref?.getAttribute("tabindex");
|
|
154
155
|
|
|
155
156
|
// Assert
|
|
156
157
|
expect(result).toBe("-1");
|
|
@@ -174,7 +175,7 @@ describe("TooltipAnchor", () => {
|
|
|
174
175
|
});
|
|
175
176
|
|
|
176
177
|
// Act
|
|
177
|
-
const result = ref
|
|
178
|
+
const result = ref?.getAttribute("tabindex");
|
|
178
179
|
|
|
179
180
|
// Assert
|
|
180
181
|
expect(result).toBeNull();
|
|
@@ -197,11 +198,11 @@ describe("TooltipAnchor", () => {
|
|
|
197
198
|
});
|
|
198
199
|
|
|
199
200
|
// Act
|
|
200
|
-
const tabindex = ref
|
|
201
|
+
const tabindex = ref?.getAttribute("tabindex");
|
|
201
202
|
expect(tabindex).toBe("0");
|
|
202
203
|
|
|
203
|
-
wrapper
|
|
204
|
-
const result = ref
|
|
204
|
+
wrapper?.setProps({force: false});
|
|
205
|
+
const result = ref?.getAttribute("tabindex");
|
|
205
206
|
|
|
206
207
|
// Assert
|
|
207
208
|
expect(result).toBeNull();
|
|
@@ -224,7 +225,7 @@ describe("TooltipAnchor", () => {
|
|
|
224
225
|
});
|
|
225
226
|
|
|
226
227
|
// Act
|
|
227
|
-
const result = ref
|
|
228
|
+
const result = ref?.getAttribute("tabindex");
|
|
228
229
|
|
|
229
230
|
// Assert
|
|
230
231
|
expect(result).not.toBeNull();
|
|
@@ -237,6 +238,7 @@ describe("TooltipAnchor", () => {
|
|
|
237
238
|
const {default: ActiveTracker} = await import(
|
|
238
239
|
"../../util/active-tracker.js"
|
|
239
240
|
);
|
|
241
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
240
242
|
// Let's tell the tooltip it isn't stealing and therefore it should
|
|
241
243
|
// be using a delay to show the tooltip.
|
|
242
244
|
// Flow doesn't know this is a mock
|
|
@@ -265,10 +267,10 @@ describe("TooltipAnchor", () => {
|
|
|
265
267
|
// whether focused directly or a child is focused). We have to
|
|
266
268
|
// fake directly because there's no real browser here handling
|
|
267
269
|
// focus and real events.
|
|
268
|
-
ref
|
|
270
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
269
271
|
// Check that we didn't go active before the delay
|
|
270
272
|
expect(activeState).toBe(false);
|
|
271
|
-
expect(
|
|
273
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
272
274
|
expect.any(Function),
|
|
273
275
|
TooltipAppearanceDelay,
|
|
274
276
|
);
|
|
@@ -310,7 +312,7 @@ describe("TooltipAnchor", () => {
|
|
|
310
312
|
// whether focused directly or a child is focused). We have to
|
|
311
313
|
// fake directly because there's no real browser here handling
|
|
312
314
|
// focus and real events.
|
|
313
|
-
ref
|
|
315
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
314
316
|
|
|
315
317
|
// Assert
|
|
316
318
|
expect(activeState).toBe(true);
|
|
@@ -320,6 +322,7 @@ describe("TooltipAnchor", () => {
|
|
|
320
322
|
describe("loses keyboard focus", () => {
|
|
321
323
|
test("active state was not stolen, active is set to false with delay", async () => {
|
|
322
324
|
// Arrange
|
|
325
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
323
326
|
let activeState = false;
|
|
324
327
|
const ref = await new Promise((resolve) => {
|
|
325
328
|
const nodes = (
|
|
@@ -335,8 +338,8 @@ describe("TooltipAnchor", () => {
|
|
|
335
338
|
mount(nodes);
|
|
336
339
|
});
|
|
337
340
|
|
|
338
|
-
ref
|
|
339
|
-
expect(
|
|
341
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
342
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
340
343
|
expect.any(Function),
|
|
341
344
|
TooltipAppearanceDelay,
|
|
342
345
|
);
|
|
@@ -344,9 +347,9 @@ describe("TooltipAnchor", () => {
|
|
|
344
347
|
expect(activeState).toBe(true);
|
|
345
348
|
|
|
346
349
|
// Act
|
|
347
|
-
ref
|
|
350
|
+
ref?.dispatchEvent(new FocusEvent("focusout"));
|
|
348
351
|
expect(activeState).toBe(true);
|
|
349
|
-
expect(
|
|
352
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
350
353
|
expect.any(Function),
|
|
351
354
|
TooltipDisappearanceDelay,
|
|
352
355
|
);
|
|
@@ -358,6 +361,7 @@ describe("TooltipAnchor", () => {
|
|
|
358
361
|
|
|
359
362
|
test("active state was not stolen, gives up active state", async () => {
|
|
360
363
|
// Arrange
|
|
364
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
361
365
|
const {default: ActiveTracker} = await import(
|
|
362
366
|
"../../util/active-tracker.js"
|
|
363
367
|
);
|
|
@@ -380,8 +384,8 @@ describe("TooltipAnchor", () => {
|
|
|
380
384
|
mount(nodes);
|
|
381
385
|
});
|
|
382
386
|
|
|
383
|
-
ref
|
|
384
|
-
expect(
|
|
387
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
388
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
385
389
|
expect.any(Function),
|
|
386
390
|
TooltipAppearanceDelay,
|
|
387
391
|
);
|
|
@@ -389,9 +393,9 @@ describe("TooltipAnchor", () => {
|
|
|
389
393
|
expect(activeState).toBe(true);
|
|
390
394
|
|
|
391
395
|
// Act
|
|
392
|
-
ref
|
|
396
|
+
ref?.dispatchEvent(new FocusEvent("focusout"));
|
|
393
397
|
expect(activeState).toBe(true);
|
|
394
|
-
expect(
|
|
398
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
395
399
|
expect.any(Function),
|
|
396
400
|
TooltipDisappearanceDelay,
|
|
397
401
|
);
|
|
@@ -403,6 +407,7 @@ describe("TooltipAnchor", () => {
|
|
|
403
407
|
|
|
404
408
|
test("active state was stolen, active is set to false immediately", async () => {
|
|
405
409
|
// Arrange
|
|
410
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
406
411
|
let wrapper;
|
|
407
412
|
let activeState = false;
|
|
408
413
|
const ref = await new Promise((resolve) => {
|
|
@@ -419,8 +424,8 @@ describe("TooltipAnchor", () => {
|
|
|
419
424
|
wrapper = mount(nodes);
|
|
420
425
|
});
|
|
421
426
|
|
|
422
|
-
ref
|
|
423
|
-
expect(
|
|
427
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
428
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
424
429
|
expect.any(Function),
|
|
425
430
|
TooltipAppearanceDelay,
|
|
426
431
|
);
|
|
@@ -428,8 +433,8 @@ describe("TooltipAnchor", () => {
|
|
|
428
433
|
expect(activeState).toBe(true);
|
|
429
434
|
|
|
430
435
|
// Act
|
|
431
|
-
ref
|
|
432
|
-
wrapper
|
|
436
|
+
ref?.dispatchEvent(new FocusEvent("focusout"));
|
|
437
|
+
wrapper?.instance().activeStateStolen();
|
|
433
438
|
|
|
434
439
|
// Assert
|
|
435
440
|
expect(activeState).toBe(false);
|
|
@@ -437,6 +442,7 @@ describe("TooltipAnchor", () => {
|
|
|
437
442
|
|
|
438
443
|
test("active state was stolen, so it does not have it to give up", async () => {
|
|
439
444
|
// Arrange
|
|
445
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
440
446
|
const {default: ActiveTracker} = await import(
|
|
441
447
|
"../../util/active-tracker.js"
|
|
442
448
|
);
|
|
@@ -459,8 +465,8 @@ describe("TooltipAnchor", () => {
|
|
|
459
465
|
);
|
|
460
466
|
wrapper = mount(nodes);
|
|
461
467
|
});
|
|
462
|
-
ref
|
|
463
|
-
expect(
|
|
468
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
469
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
464
470
|
expect.any(Function),
|
|
465
471
|
TooltipAppearanceDelay,
|
|
466
472
|
);
|
|
@@ -468,8 +474,8 @@ describe("TooltipAnchor", () => {
|
|
|
468
474
|
expect(activeState).toBe(true);
|
|
469
475
|
|
|
470
476
|
// Act
|
|
471
|
-
ref
|
|
472
|
-
wrapper
|
|
477
|
+
ref?.dispatchEvent(new FocusEvent("focusout"));
|
|
478
|
+
wrapper?.instance().activeStateStolen();
|
|
473
479
|
|
|
474
480
|
// Assert
|
|
475
481
|
expect(mockTracker.giveup).not.toHaveBeenCalled();
|
|
@@ -477,6 +483,7 @@ describe("TooltipAnchor", () => {
|
|
|
477
483
|
|
|
478
484
|
test("if hovered, remains active", async () => {
|
|
479
485
|
// Arrange
|
|
486
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
480
487
|
let activeState = false;
|
|
481
488
|
const ref = await new Promise((resolve) => {
|
|
482
489
|
const nodes = (
|
|
@@ -491,30 +498,29 @@ describe("TooltipAnchor", () => {
|
|
|
491
498
|
);
|
|
492
499
|
mount(nodes);
|
|
493
500
|
});
|
|
494
|
-
ref
|
|
495
|
-
expect(
|
|
501
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
502
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
496
503
|
expect.any(Function),
|
|
497
504
|
TooltipAppearanceDelay,
|
|
498
505
|
);
|
|
499
506
|
jest.runOnlyPendingTimers();
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
setTimeout.mockClear();
|
|
503
|
-
ref && ref.dispatchEvent(new MouseEvent("mouseenter"));
|
|
507
|
+
timeoutSpy.mockClear();
|
|
508
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
504
509
|
|
|
505
510
|
// Act
|
|
506
|
-
ref
|
|
511
|
+
ref?.dispatchEvent(new FocusEvent("focusout"));
|
|
507
512
|
|
|
508
513
|
// Assert
|
|
509
514
|
// Make sure that we're not delay hiding as well.
|
|
510
515
|
expect(activeState).toBe(true);
|
|
511
|
-
expect(
|
|
516
|
+
expect(timeoutSpy).not.toHaveBeenCalled();
|
|
512
517
|
});
|
|
513
518
|
});
|
|
514
519
|
|
|
515
520
|
describe("is hovered", () => {
|
|
516
521
|
test("active state was not stolen, delays set active", async () => {
|
|
517
522
|
// Arrange
|
|
523
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
518
524
|
const {default: ActiveTracker} = await import(
|
|
519
525
|
"../../util/active-tracker.js"
|
|
520
526
|
);
|
|
@@ -541,10 +547,10 @@ describe("TooltipAnchor", () => {
|
|
|
541
547
|
});
|
|
542
548
|
|
|
543
549
|
// Act
|
|
544
|
-
ref
|
|
550
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
545
551
|
// Check that we didn't go active before the delay
|
|
546
552
|
expect(activeState).toBe(false);
|
|
547
|
-
expect(
|
|
553
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
548
554
|
expect.any(Function),
|
|
549
555
|
TooltipAppearanceDelay,
|
|
550
556
|
);
|
|
@@ -582,7 +588,7 @@ describe("TooltipAnchor", () => {
|
|
|
582
588
|
});
|
|
583
589
|
|
|
584
590
|
// Act
|
|
585
|
-
ref
|
|
591
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
586
592
|
|
|
587
593
|
// Assert
|
|
588
594
|
expect(activeState).toBe(true);
|
|
@@ -592,6 +598,7 @@ describe("TooltipAnchor", () => {
|
|
|
592
598
|
describe("is unhovered", () => {
|
|
593
599
|
test("active state was not stolen, active is set to false with delay", async () => {
|
|
594
600
|
// Arrange
|
|
601
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
595
602
|
let activeState = false;
|
|
596
603
|
const ref = await new Promise((resolve) => {
|
|
597
604
|
const nodes = (
|
|
@@ -606,8 +613,8 @@ describe("TooltipAnchor", () => {
|
|
|
606
613
|
);
|
|
607
614
|
mount(nodes);
|
|
608
615
|
});
|
|
609
|
-
ref
|
|
610
|
-
expect(
|
|
616
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
617
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
611
618
|
expect.any(Function),
|
|
612
619
|
TooltipAppearanceDelay,
|
|
613
620
|
);
|
|
@@ -615,9 +622,9 @@ describe("TooltipAnchor", () => {
|
|
|
615
622
|
expect(activeState).toBe(true);
|
|
616
623
|
|
|
617
624
|
// Act
|
|
618
|
-
ref
|
|
625
|
+
ref?.dispatchEvent(new MouseEvent("mouseleave"));
|
|
619
626
|
expect(activeState).toBe(true);
|
|
620
|
-
expect(
|
|
627
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
621
628
|
expect.any(Function),
|
|
622
629
|
TooltipDisappearanceDelay,
|
|
623
630
|
);
|
|
@@ -629,6 +636,7 @@ describe("TooltipAnchor", () => {
|
|
|
629
636
|
|
|
630
637
|
test("active state was not stolen, gives up active state", async () => {
|
|
631
638
|
// Arrange
|
|
639
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
632
640
|
const {default: ActiveTracker} = await import(
|
|
633
641
|
"../../util/active-tracker.js"
|
|
634
642
|
);
|
|
@@ -649,8 +657,8 @@ describe("TooltipAnchor", () => {
|
|
|
649
657
|
);
|
|
650
658
|
mount(nodes);
|
|
651
659
|
});
|
|
652
|
-
ref
|
|
653
|
-
expect(
|
|
660
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
661
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
654
662
|
expect.any(Function),
|
|
655
663
|
TooltipAppearanceDelay,
|
|
656
664
|
);
|
|
@@ -658,9 +666,9 @@ describe("TooltipAnchor", () => {
|
|
|
658
666
|
expect(activeState).toBe(true);
|
|
659
667
|
|
|
660
668
|
// Act
|
|
661
|
-
ref
|
|
669
|
+
ref?.dispatchEvent(new MouseEvent("mouseleave"));
|
|
662
670
|
expect(activeState).toBe(true);
|
|
663
|
-
expect(
|
|
671
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
664
672
|
expect.any(Function),
|
|
665
673
|
TooltipDisappearanceDelay,
|
|
666
674
|
);
|
|
@@ -672,6 +680,7 @@ describe("TooltipAnchor", () => {
|
|
|
672
680
|
|
|
673
681
|
test("active state was stolen, active is set to false immediately", async () => {
|
|
674
682
|
// Arrange
|
|
683
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
675
684
|
let wrapper;
|
|
676
685
|
let activeState = false;
|
|
677
686
|
const ref = await new Promise((resolve) => {
|
|
@@ -687,8 +696,8 @@ describe("TooltipAnchor", () => {
|
|
|
687
696
|
);
|
|
688
697
|
wrapper = mount(nodes);
|
|
689
698
|
});
|
|
690
|
-
ref
|
|
691
|
-
expect(
|
|
699
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
700
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
692
701
|
expect.any(Function),
|
|
693
702
|
TooltipAppearanceDelay,
|
|
694
703
|
);
|
|
@@ -696,8 +705,8 @@ describe("TooltipAnchor", () => {
|
|
|
696
705
|
expect(activeState).toBe(true);
|
|
697
706
|
|
|
698
707
|
// Act
|
|
699
|
-
ref
|
|
700
|
-
wrapper
|
|
708
|
+
ref?.dispatchEvent(new MouseEvent("mouseleave"));
|
|
709
|
+
wrapper?.instance().activeStateStolen();
|
|
701
710
|
|
|
702
711
|
// Assert
|
|
703
712
|
expect(activeState).toBe(false);
|
|
@@ -705,6 +714,7 @@ describe("TooltipAnchor", () => {
|
|
|
705
714
|
|
|
706
715
|
test("active state was stolen, so it does not have it to give up", async () => {
|
|
707
716
|
// Arrange
|
|
717
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
708
718
|
const {default: ActiveTracker} = await import(
|
|
709
719
|
"../../util/active-tracker.js"
|
|
710
720
|
);
|
|
@@ -727,8 +737,8 @@ describe("TooltipAnchor", () => {
|
|
|
727
737
|
);
|
|
728
738
|
wrapper = mount(nodes);
|
|
729
739
|
});
|
|
730
|
-
ref
|
|
731
|
-
expect(
|
|
740
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
741
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
732
742
|
expect.any(Function),
|
|
733
743
|
TooltipAppearanceDelay,
|
|
734
744
|
);
|
|
@@ -736,8 +746,8 @@ describe("TooltipAnchor", () => {
|
|
|
736
746
|
expect(activeState).toBe(true);
|
|
737
747
|
|
|
738
748
|
// Act
|
|
739
|
-
ref
|
|
740
|
-
wrapper
|
|
749
|
+
ref?.dispatchEvent(new MouseEvent("mouseleave"));
|
|
750
|
+
wrapper?.instance().activeStateStolen();
|
|
741
751
|
|
|
742
752
|
// Assert
|
|
743
753
|
expect(mockTracker.giveup).not.toHaveBeenCalled();
|
|
@@ -745,6 +755,7 @@ describe("TooltipAnchor", () => {
|
|
|
745
755
|
|
|
746
756
|
test("if focused, remains active", async () => {
|
|
747
757
|
// Arrange
|
|
758
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
748
759
|
let activeState = false;
|
|
749
760
|
const ref = await new Promise((resolve) => {
|
|
750
761
|
const nodes = (
|
|
@@ -759,30 +770,29 @@ describe("TooltipAnchor", () => {
|
|
|
759
770
|
);
|
|
760
771
|
mount(nodes);
|
|
761
772
|
});
|
|
762
|
-
ref
|
|
763
|
-
expect(
|
|
773
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
774
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
764
775
|
expect.any(Function),
|
|
765
776
|
TooltipAppearanceDelay,
|
|
766
777
|
);
|
|
767
778
|
jest.runOnlyPendingTimers();
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
setTimeout.mockClear();
|
|
771
|
-
ref && ref.dispatchEvent(new FocusEvent("focusin"));
|
|
779
|
+
timeoutSpy.mockClear();
|
|
780
|
+
ref?.dispatchEvent(new FocusEvent("focusin"));
|
|
772
781
|
|
|
773
782
|
// Act
|
|
774
|
-
ref
|
|
783
|
+
ref?.dispatchEvent(new MouseEvent("mouseleave"));
|
|
775
784
|
|
|
776
785
|
// Assert
|
|
777
786
|
// Make sure that we're not delay hiding as well.
|
|
778
787
|
expect(activeState).toBe(true);
|
|
779
|
-
expect(
|
|
788
|
+
expect(timeoutSpy).not.toHaveBeenCalled();
|
|
780
789
|
});
|
|
781
790
|
});
|
|
782
791
|
|
|
783
792
|
describe("dismiss behavior", () => {
|
|
784
793
|
test("subscribes to keydown event on active", async () => {
|
|
785
794
|
// Arrange
|
|
795
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
786
796
|
const spy = jest.spyOn(document, "addEventListener");
|
|
787
797
|
const ref = await new Promise((resolve) => {
|
|
788
798
|
const nodes = (
|
|
@@ -797,8 +807,8 @@ describe("TooltipAnchor", () => {
|
|
|
797
807
|
});
|
|
798
808
|
|
|
799
809
|
// Act
|
|
800
|
-
ref
|
|
801
|
-
expect(
|
|
810
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
811
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
802
812
|
expect.any(Function),
|
|
803
813
|
TooltipAppearanceDelay,
|
|
804
814
|
);
|
|
@@ -811,6 +821,7 @@ describe("TooltipAnchor", () => {
|
|
|
811
821
|
|
|
812
822
|
test("does not subscribe to keydown event if already active", async () => {
|
|
813
823
|
// Arrange
|
|
824
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
814
825
|
const spy = jest.spyOn(document, "addEventListener");
|
|
815
826
|
const ref = await new Promise((resolve) => {
|
|
816
827
|
const nodes = (
|
|
@@ -824,8 +835,8 @@ describe("TooltipAnchor", () => {
|
|
|
824
835
|
mount(nodes);
|
|
825
836
|
});
|
|
826
837
|
|
|
827
|
-
ref
|
|
828
|
-
expect(
|
|
838
|
+
ref?.dispatchEvent(new KeyboardEvent("focusin"));
|
|
839
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
829
840
|
expect.any(Function),
|
|
830
841
|
TooltipAppearanceDelay,
|
|
831
842
|
);
|
|
@@ -835,7 +846,7 @@ describe("TooltipAnchor", () => {
|
|
|
835
846
|
spy.mockClear();
|
|
836
847
|
|
|
837
848
|
// Act
|
|
838
|
-
ref
|
|
849
|
+
ref?.dispatchEvent(new MouseEvent("mouseenter"));
|
|
839
850
|
|
|
840
851
|
// Assert
|
|
841
852
|
expect(spy).not.toHaveBeenCalled();
|
|
@@ -843,6 +854,7 @@ describe("TooltipAnchor", () => {
|
|
|
843
854
|
|
|
844
855
|
test("unsubscribes from keydown event on inactive", async () => {
|
|
845
856
|
// Arrange
|
|
857
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
846
858
|
const spy = jest.spyOn(document, "removeEventListener");
|
|
847
859
|
const ref = await new Promise((resolve) => {
|
|
848
860
|
const nodes = (
|
|
@@ -856,16 +868,16 @@ describe("TooltipAnchor", () => {
|
|
|
856
868
|
mount(nodes);
|
|
857
869
|
});
|
|
858
870
|
|
|
859
|
-
ref
|
|
860
|
-
expect(
|
|
871
|
+
ref?.dispatchEvent(new KeyboardEvent("focusin"));
|
|
872
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
861
873
|
expect.any(Function),
|
|
862
874
|
TooltipAppearanceDelay,
|
|
863
875
|
);
|
|
864
876
|
jest.runOnlyPendingTimers();
|
|
865
877
|
|
|
866
878
|
// Act
|
|
867
|
-
ref
|
|
868
|
-
expect(
|
|
879
|
+
ref?.dispatchEvent(new KeyboardEvent("focusout"));
|
|
880
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
869
881
|
expect.any(Function),
|
|
870
882
|
TooltipDisappearanceDelay,
|
|
871
883
|
);
|
|
@@ -878,6 +890,7 @@ describe("TooltipAnchor", () => {
|
|
|
878
890
|
|
|
879
891
|
test("unsubscribes from keydown event on unmount", async () => {
|
|
880
892
|
// Arrange
|
|
893
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
881
894
|
let wrapper;
|
|
882
895
|
const spy = jest.spyOn(document, "removeEventListener");
|
|
883
896
|
const ref = await new Promise((resolve) => {
|
|
@@ -892,15 +905,15 @@ describe("TooltipAnchor", () => {
|
|
|
892
905
|
wrapper = mount(nodes);
|
|
893
906
|
});
|
|
894
907
|
|
|
895
|
-
ref
|
|
896
|
-
expect(
|
|
908
|
+
ref?.dispatchEvent(new KeyboardEvent("focusin"));
|
|
909
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
897
910
|
expect.any(Function),
|
|
898
911
|
TooltipAppearanceDelay,
|
|
899
912
|
);
|
|
900
913
|
jest.runOnlyPendingTimers();
|
|
901
914
|
|
|
902
915
|
// Act
|
|
903
|
-
wrapper
|
|
916
|
+
wrapper?.unmount();
|
|
904
917
|
|
|
905
918
|
// Assert
|
|
906
919
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
@@ -909,6 +922,7 @@ describe("TooltipAnchor", () => {
|
|
|
909
922
|
|
|
910
923
|
test("when active, escape dismisses tooltip", async () => {
|
|
911
924
|
// Arrange
|
|
925
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
912
926
|
let activeState = false;
|
|
913
927
|
const ref = await new Promise((resolve) => {
|
|
914
928
|
const nodes = (
|
|
@@ -924,20 +938,22 @@ describe("TooltipAnchor", () => {
|
|
|
924
938
|
mount(nodes);
|
|
925
939
|
});
|
|
926
940
|
|
|
927
|
-
ref
|
|
928
|
-
expect(
|
|
941
|
+
ref?.dispatchEvent(new KeyboardEvent("focusin"));
|
|
942
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
929
943
|
expect.any(Function),
|
|
930
944
|
TooltipAppearanceDelay,
|
|
931
945
|
);
|
|
932
946
|
jest.runOnlyPendingTimers();
|
|
933
947
|
const event: KeyboardEvent = (document.createEvent("Event"): any);
|
|
948
|
+
// $FlowIgnore[cannot-write]
|
|
934
949
|
event.key = "Escape";
|
|
950
|
+
// $FlowIgnore[cannot-write]
|
|
935
951
|
event.which = 27;
|
|
936
952
|
event.initEvent("keyup", true, true);
|
|
937
953
|
|
|
938
954
|
// Act
|
|
939
955
|
document.dispatchEvent(event);
|
|
940
|
-
expect(
|
|
956
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
941
957
|
expect.any(Function),
|
|
942
958
|
TooltipDisappearanceDelay,
|
|
943
959
|
);
|
|
@@ -949,6 +965,7 @@ describe("TooltipAnchor", () => {
|
|
|
949
965
|
|
|
950
966
|
test("when active, escape stops event propagation", async () => {
|
|
951
967
|
// Arrange
|
|
968
|
+
const timeoutSpy = jest.spyOn(global, "setTimeout");
|
|
952
969
|
const ref = await new Promise((resolve) => {
|
|
953
970
|
const nodes = (
|
|
954
971
|
<TooltipAnchor
|
|
@@ -961,20 +978,21 @@ describe("TooltipAnchor", () => {
|
|
|
961
978
|
mount(nodes);
|
|
962
979
|
});
|
|
963
980
|
|
|
964
|
-
ref
|
|
965
|
-
expect(
|
|
981
|
+
ref?.dispatchEvent(new KeyboardEvent("focusin"));
|
|
982
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
966
983
|
expect.any(Function),
|
|
967
984
|
TooltipAppearanceDelay,
|
|
968
985
|
);
|
|
969
986
|
jest.runOnlyPendingTimers();
|
|
970
987
|
const event: KeyboardEvent = (document.createEvent("Event"): any);
|
|
971
988
|
const spyOnStopPropagation = jest.spyOn(event, "stopPropagation");
|
|
989
|
+
// $FlowIgnore[cannot-write]
|
|
972
990
|
event.key = "Escape";
|
|
973
991
|
event.initEvent("keyup", true, true);
|
|
974
992
|
|
|
975
993
|
// Act
|
|
976
994
|
document.dispatchEvent(event);
|
|
977
|
-
expect(
|
|
995
|
+
expect(timeoutSpy).toHaveBeenLastCalledWith(
|
|
978
996
|
expect.any(Function),
|
|
979
997
|
TooltipDisappearanceDelay,
|
|
980
998
|
);
|
|
@@ -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;
|
|
@@ -363,12 +363,8 @@ export default class TooltipTail extends React.Component<Props> {
|
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
_renderArrow(): React.Node {
|
|
366
|
-
const {
|
|
367
|
-
|
|
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
|
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import {StyleSheet} from "aphrodite";
|
|
4
4
|
import {View} from "@khanacademy/wonder-blocks-core";
|
|
5
|
+
import Button from "@khanacademy/wonder-blocks-button";
|
|
5
6
|
import {TextField} from "@khanacademy/wonder-blocks-form";
|
|
7
|
+
import {Strut} from "@khanacademy/wonder-blocks-layout";
|
|
8
|
+
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
6
9
|
import Tooltip from "@khanacademy/wonder-blocks-tooltip";
|
|
7
10
|
|
|
8
11
|
import type {Placement} from "@khanacademy/wonder-blocks-tooltip";
|
|
@@ -10,6 +13,13 @@ import type {StoryComponentType} from "@storybook/react";
|
|
|
10
13
|
|
|
11
14
|
export default {
|
|
12
15
|
title: "Floating/Tooltip",
|
|
16
|
+
parameters: {
|
|
17
|
+
// TODO(WB-1170): Reassess this after investigating more about Chromatic
|
|
18
|
+
// flakyness.
|
|
19
|
+
chromatic: {
|
|
20
|
+
disableSnapshot: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
13
23
|
};
|
|
14
24
|
|
|
15
25
|
const BaseTooltipExample = ({placement}: {|placement: Placement|}) => {
|
|
@@ -41,19 +51,44 @@ const BaseTooltipExample = ({placement}: {|placement: Placement|}) => {
|
|
|
41
51
|
);
|
|
42
52
|
};
|
|
43
53
|
|
|
44
|
-
export const
|
|
54
|
+
export const TooltipOnButtons: StoryComponentType = () => {
|
|
55
|
+
return (
|
|
56
|
+
<View style={styles.centered}>
|
|
57
|
+
<View>
|
|
58
|
+
<Tooltip content={"This is a tooltip on a button."}>
|
|
59
|
+
<Button disabled={false}>Example 1</Button>
|
|
60
|
+
</Tooltip>
|
|
61
|
+
<Strut size={Spacing.medium_16} />
|
|
62
|
+
<Tooltip
|
|
63
|
+
content="This is a tooltip on a disabled button."
|
|
64
|
+
placement="bottom"
|
|
65
|
+
>
|
|
66
|
+
<Button disabled={true}>Example 2</Button>
|
|
67
|
+
</Tooltip>
|
|
68
|
+
</View>
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
TooltipOnButtons.parameters = {
|
|
74
|
+
chromatic: {
|
|
75
|
+
disableSnapshot: true,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const TooltipRight: StoryComponentType = () => (
|
|
45
80
|
<BaseTooltipExample placement="right" />
|
|
46
81
|
);
|
|
47
82
|
|
|
48
|
-
export const
|
|
83
|
+
export const TooltipLeft: StoryComponentType = () => (
|
|
49
84
|
<BaseTooltipExample placement="left" />
|
|
50
85
|
);
|
|
51
86
|
|
|
52
|
-
export const
|
|
87
|
+
export const TooltipTop: StoryComponentType = () => (
|
|
53
88
|
<BaseTooltipExample placement="top" />
|
|
54
89
|
);
|
|
55
90
|
|
|
56
|
-
export const
|
|
91
|
+
export const TooltipBottom: StoryComponentType = () => (
|
|
57
92
|
<BaseTooltipExample placement="bottom" />
|
|
58
93
|
);
|
|
59
94
|
|
package/src/util/ref-tracker.js
CHANGED
|
@@ -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
|
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2018 Khan Academy
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|