@sproutsocial/seeds-react-tooltip 1.0.16 → 1.1.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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +17 -0
- package/dist/esm/index.js +13 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +13 -7
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/Tooltip.stories.tsx +58 -0
- package/src/Tooltip.tsx +20 -7
- package/src/TooltipTypes.ts +10 -0
- package/src/__tests__/Tooltip.test.tsx +76 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -8,14 +8,14 @@ $ tsup --dts
|
|
|
8
8
|
[34mCLI[39m Cleaning output folder
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[32m6.
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[32m4.
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m6.73 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m11.16 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 73ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m4.74 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m11.05 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 76ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
21
|
-
Done in 7.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 6203ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.30 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m2.30 KB[39m
|
|
21
|
+
Done in 7.44s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-tooltip
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ade2a6f: Added exitDelay prop to Tooltip
|
|
8
|
+
- c85d800: Change mouseenter behavior to prevent the tooltip from disappearing
|
|
9
|
+
|
|
10
|
+
## 1.0.17
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [5e08137]
|
|
15
|
+
- @sproutsocial/seeds-motion@1.8.2
|
|
16
|
+
- @sproutsocial/seeds-react-theme@3.3.2
|
|
17
|
+
- @sproutsocial/seeds-react-popout@2.4.18
|
|
18
|
+
- @sproutsocial/seeds-react-box@1.1.10
|
|
19
|
+
|
|
3
20
|
## 1.0.16
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/dist/esm/index.js
CHANGED
|
@@ -24,6 +24,7 @@ var TooltipBubble = ({
|
|
|
24
24
|
children,
|
|
25
25
|
onFocus,
|
|
26
26
|
onBlur,
|
|
27
|
+
legacyMouseInteraction,
|
|
27
28
|
...rest
|
|
28
29
|
}) => {
|
|
29
30
|
const handleFocus = (e) => {
|
|
@@ -46,10 +47,12 @@ var TooltipBubble = ({
|
|
|
46
47
|
boxShadow: "medium",
|
|
47
48
|
border: 500,
|
|
48
49
|
borderColor: "container.border.base",
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
...legacyMouseInteraction && {
|
|
51
|
+
onFocus: handleFocus,
|
|
52
|
+
onBlur: handleBlur,
|
|
53
|
+
onMouseEnter: handleFocus,
|
|
54
|
+
onMouseLeave: handleBlur
|
|
55
|
+
},
|
|
53
56
|
tabIndex: 0,
|
|
54
57
|
...rest,
|
|
55
58
|
children
|
|
@@ -60,6 +63,7 @@ var Tooltip = ({
|
|
|
60
63
|
content,
|
|
61
64
|
children,
|
|
62
65
|
enterDelay = MOTION.MOTION_DURATION_FAST * 1e3,
|
|
66
|
+
exitDelay,
|
|
63
67
|
placement = "auto",
|
|
64
68
|
appearance,
|
|
65
69
|
zIndex = 7,
|
|
@@ -68,12 +72,14 @@ var Tooltip = ({
|
|
|
68
72
|
truncated = false,
|
|
69
73
|
onFocus,
|
|
70
74
|
onBlur,
|
|
75
|
+
legacyMouseInteraction,
|
|
71
76
|
...rest
|
|
72
77
|
}) => {
|
|
73
78
|
const [shouldShow, setShouldShow] = useState(false);
|
|
74
79
|
const [isOpen, setIsOpen] = useState(false);
|
|
75
80
|
const [id] = useState(`Racine-tooltip-${idCounter++}`);
|
|
76
81
|
const isInvalidContent = content === null || content === void 0;
|
|
82
|
+
const resolvedExitDelay = exitDelay !== void 0 ? exitDelay : legacyMouseInteraction ? MOTION.MOTION_DURATION_FAST * 1e3 : 0;
|
|
77
83
|
const show = (e) => {
|
|
78
84
|
onFocus?.(e);
|
|
79
85
|
setShouldShow(true);
|
|
@@ -82,7 +88,6 @@ var Tooltip = ({
|
|
|
82
88
|
onBlur?.(e);
|
|
83
89
|
setShouldShow(false);
|
|
84
90
|
};
|
|
85
|
-
const exitDelay = MOTION.MOTION_DURATION_FAST * 1e3;
|
|
86
91
|
const defaultAppearance = appearance || (typeof content === "object" ? "box" : "pill");
|
|
87
92
|
useEffect(() => {
|
|
88
93
|
const documentBody = document.body;
|
|
@@ -98,7 +103,7 @@ var Tooltip = ({
|
|
|
98
103
|
} else {
|
|
99
104
|
timeout = setTimeout(() => {
|
|
100
105
|
setIsOpen(false);
|
|
101
|
-
},
|
|
106
|
+
}, resolvedExitDelay);
|
|
102
107
|
}
|
|
103
108
|
if (isOpen) {
|
|
104
109
|
documentBody.addEventListener("keydown", onEsc, { capture: true });
|
|
@@ -107,13 +112,14 @@ var Tooltip = ({
|
|
|
107
112
|
documentBody.removeEventListener("keydown", onEsc, { capture: true });
|
|
108
113
|
clearTimeout(timeout);
|
|
109
114
|
};
|
|
110
|
-
}, [isOpen, setShouldShow, shouldShow, enterDelay,
|
|
115
|
+
}, [isOpen, setShouldShow, shouldShow, enterDelay, resolvedExitDelay]);
|
|
111
116
|
const TooltipContent = () => /* @__PURE__ */ jsx(
|
|
112
117
|
TooltipBubble,
|
|
113
118
|
{
|
|
114
119
|
appearance: defaultAppearance,
|
|
115
120
|
onFocus: show,
|
|
116
121
|
onBlur: hide,
|
|
122
|
+
legacyMouseInteraction,
|
|
117
123
|
"aria-expanded": isOpen,
|
|
118
124
|
id,
|
|
119
125
|
...rest,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Tooltip.tsx","../../src/styles.ts","../../src/TooltipTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState, useEffect } from \"react\";\nimport MOTION from \"@sproutsocial/seeds-motion/unitless\";\nimport Popout from \"@sproutsocial/seeds-react-popout\";\nimport { StyledTooltipContent } from \"./styles\";\nimport type { TypeTooltipProps, TypeTooltipContent } from \"./TooltipTypes\";\n\nlet idCounter = 0;\n\nconst hasAttribute = (child: React.ReactNode, attribute: string) => {\n return React.isValidElement(child) && child.props[attribute] !== undefined;\n};\n\n/** Tooltip Styled Popout wrapper for handling events */\nconst TooltipBubble = ({\n appearance = \"pill\",\n children,\n onFocus,\n onBlur,\n ...rest\n}: TypeTooltipContent) => {\n // @ts-ignore Will fix during refactor\n const handleFocus = (e) => {\n onFocus(e);\n };\n // @ts-ignore Will fix during refactor\n const handleBlur = (e) => {\n onBlur(e);\n };\n return (\n <StyledTooltipContent\n role=\"tooltip\"\n appearance={appearance}\n borderRadius={appearance === \"box\" ? 500 : \"5000em\"}\n px={400}\n py={appearance === \"box\" ? 400 : 200}\n m={200}\n color=\"text.body\"\n bg=\"container.background.base\"\n boxShadow=\"medium\"\n border={500}\n borderColor=\"container.border.base\"\n onFocus={handleFocus}\n onBlur={handleBlur}\n onMouseEnter={handleFocus}\n onMouseLeave={handleBlur}\n tabIndex={0}\n {...rest}\n >\n {children}\n </StyledTooltipContent>\n );\n};\n\n/** Core component */\nconst Tooltip = ({\n content,\n children,\n enterDelay = MOTION.MOTION_DURATION_FAST * 1000,\n placement = \"auto\",\n appearance,\n zIndex = 7,\n qa,\n popoutProps,\n truncated = false,\n onFocus,\n onBlur,\n ...rest\n}: TypeTooltipProps) => {\n const [shouldShow, setShouldShow] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n const [id] = useState(`Racine-tooltip-${idCounter++}`);\n const isInvalidContent = content === null || content === undefined;\n\n // @ts-ignore Will fix during refactor\n const show = (e) => {\n onFocus?.(e);\n setShouldShow(true);\n };\n // @ts-ignore Will fix during refactor\n const hide = (e) => {\n onBlur?.(e);\n setShouldShow(false);\n };\n\n const exitDelay = MOTION.MOTION_DURATION_FAST * 1000;\n const defaultAppearance =\n appearance || (typeof content === \"object\" ? \"box\" : \"pill\");\n\n /** Handles all the logic around whether to display/not display */\n useEffect(() => {\n const documentBody = document.body;\n let timeout;\n const onEsc = (e: KeyboardEvent): void => {\n // older browsers use \"Esc\"\n if ([\"Escape\", \"Esc\"].includes(e.key)) {\n setIsOpen(false);\n setShouldShow(false);\n }\n };\n\n if (shouldShow) {\n timeout = setTimeout(() => setIsOpen(true), enterDelay);\n } else {\n timeout = setTimeout(() => {\n setIsOpen(false);\n }, exitDelay);\n }\n\n // We only want listeners from the tooltip if its open in the first place\n if (isOpen) {\n documentBody.addEventListener(\"keydown\", onEsc, { capture: true });\n }\n return () => {\n documentBody.removeEventListener(\"keydown\", onEsc, { capture: true });\n clearTimeout(timeout);\n };\n }, [isOpen, setShouldShow, shouldShow, enterDelay, exitDelay]);\n\n /** The wrapped content of whats inside the Tooltip */\n const TooltipContent = () => (\n <TooltipBubble\n appearance={defaultAppearance}\n onFocus={show}\n onBlur={hide}\n aria-expanded={isOpen}\n id={id}\n {...rest}\n >\n {content}\n </TooltipBubble>\n );\n\n return (\n <Popout\n content={!isInvalidContent ? TooltipContent : undefined}\n isOpen={isOpen}\n placement={placement}\n qa={{\n \"data-qa-tooltip\": id,\n ...qa,\n }}\n id={id + \"-wrapper\"}\n focusOnContent={false}\n zIndex={zIndex}\n aria-haspopup=\"false\"\n display={truncated ? \"flex\" : undefined}\n disableWrapperAria={true} // required so that the child span doesnt take in redundant aria props\n {...popoutProps}\n >\n <span\n onBlur={hide}\n onFocus={show}\n onMouseEnter={show}\n onMouseLeave={hide}\n style={\n truncated\n ? {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : {}\n }\n >\n {React.isValidElement(children)\n ? React.cloneElement(children as React.ReactElement, {\n //** There may be cases where the Tooltip's child needs to properly describe its role as expanding a drawer, in which case that property takes priority */\n \"aria-expanded\": hasAttribute(children, \"aria-expanded\")\n ? children.props[\"aria-expanded\"]\n : undefined,\n \"aria-describedby\": isOpen ? id : undefined,\n })\n : children}\n </span>\n </Popout>\n );\n};\n\nexport default Tooltip;\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { TypeTooltipContent } from \"./TooltipTypes\";\n\nexport const StyledTooltipContent = styled(Box)<\n Pick<TypeTooltipContent, \"appearance\">\n>`\n font-family: ${(props) => props.theme.fontFamily};\n ${(props) => props.theme.typography[200]}\n text-align: ${(props) => (props.appearance === \"box\" ? \"left\" : \"center\")};\n`;\n","import * as React from \"react\";\nimport type { TypePopoutProps } from \"@sproutsocial/seeds-react-popout\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeTooltipProps\n extends Omit<\n TypeBoxProps,\n \"children\" | \"content\" | \"onMouseEnter\" | \"onMouseLeave\"\n > {\n /** The content that the tooltip should be attached to. Hovering or focusing this element will cause the tooltip to appear */\n children: React.ReactNode;\n\n /** The content to be displayed within the tooltip. If there is no content, just the children are rendered */\n content: React.ReactNode;\n\n /** The placement of the tooltip in relation to the children */\n placement?: TypePopoutProps[\"placement\"];\n\n /** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */\n enterDelay?: number;\n\n /** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */\n appearance?: \"pill\" | \"box\";\n qa?: object;\n zIndex?: number;\n\n /** Props to be spread onto the underlying Popout component */\n popoutProps?: Partial<TypePopoutProps>;\n\n /** Truncates text into a single line with ellipsis */\n truncated?: boolean;\n\n ariaProps?: Record<string, string>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface TypeTooltipContent\n extends Pick<TypeTooltipProps, \"appearance\" | \"children\"> {\n onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n}\n","import Tooltip from \"./Tooltip\";\n\nexport default Tooltip;\nexport { Tooltip };\nexport * from \"./TooltipTypes\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/Tooltip.tsx","../../src/styles.ts","../../src/TooltipTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState, useEffect } from \"react\";\nimport MOTION from \"@sproutsocial/seeds-motion/unitless\";\nimport Popout from \"@sproutsocial/seeds-react-popout\";\nimport { StyledTooltipContent } from \"./styles\";\nimport type { TypeTooltipProps, TypeTooltipContent } from \"./TooltipTypes\";\n\nlet idCounter = 0;\n\nconst hasAttribute = (child: React.ReactNode, attribute: string) => {\n return React.isValidElement(child) && child.props[attribute] !== undefined;\n};\n\n/** Tooltip Styled Popout wrapper for handling events */\nconst TooltipBubble = ({\n appearance = \"pill\",\n children,\n onFocus,\n onBlur,\n legacyMouseInteraction,\n ...rest\n}: TypeTooltipContent) => {\n // @ts-ignore Will fix during refactor\n const handleFocus = (e) => {\n onFocus(e);\n };\n // @ts-ignore Will fix during refactor\n const handleBlur = (e) => {\n onBlur(e);\n };\n return (\n <StyledTooltipContent\n role=\"tooltip\"\n appearance={appearance}\n borderRadius={appearance === \"box\" ? 500 : \"5000em\"}\n px={400}\n py={appearance === \"box\" ? 400 : 200}\n m={200}\n color=\"text.body\"\n bg=\"container.background.base\"\n boxShadow=\"medium\"\n border={500}\n borderColor=\"container.border.base\"\n {...(legacyMouseInteraction && {\n onFocus: handleFocus,\n onBlur: handleBlur,\n onMouseEnter: handleFocus,\n onMouseLeave: handleBlur,\n })}\n tabIndex={0}\n {...rest}\n >\n {children}\n </StyledTooltipContent>\n );\n};\n\n/** Core component */\nconst Tooltip = ({\n content,\n children,\n enterDelay = MOTION.MOTION_DURATION_FAST * 1000,\n exitDelay,\n placement = \"auto\",\n appearance,\n zIndex = 7,\n qa,\n popoutProps,\n truncated = false,\n onFocus,\n onBlur,\n legacyMouseInteraction,\n ...rest\n}: TypeTooltipProps) => {\n const [shouldShow, setShouldShow] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n const [id] = useState(`Racine-tooltip-${idCounter++}`);\n const isInvalidContent = content === null || content === undefined;\n\n // Compute exitDelay based on legacyMouseInteraction if not explicitly provided\n const resolvedExitDelay =\n exitDelay !== undefined\n ? exitDelay\n : legacyMouseInteraction\n ? MOTION.MOTION_DURATION_FAST * 1000\n : 0;\n\n // @ts-ignore Will fix during refactor\n const show = (e) => {\n onFocus?.(e);\n setShouldShow(true);\n };\n // @ts-ignore Will fix during refactor\n const hide = (e) => {\n onBlur?.(e);\n setShouldShow(false);\n };\n\n const defaultAppearance =\n appearance || (typeof content === \"object\" ? \"box\" : \"pill\");\n\n /** Handles all the logic around whether to display/not display */\n useEffect(() => {\n const documentBody = document.body;\n let timeout;\n const onEsc = (e: KeyboardEvent): void => {\n // older browsers use \"Esc\"\n if ([\"Escape\", \"Esc\"].includes(e.key)) {\n setIsOpen(false);\n setShouldShow(false);\n }\n };\n\n if (shouldShow) {\n timeout = setTimeout(() => setIsOpen(true), enterDelay);\n } else {\n timeout = setTimeout(() => {\n setIsOpen(false);\n }, resolvedExitDelay);\n }\n\n // We only want listeners from the tooltip if its open in the first place\n if (isOpen) {\n documentBody.addEventListener(\"keydown\", onEsc, { capture: true });\n }\n return () => {\n documentBody.removeEventListener(\"keydown\", onEsc, { capture: true });\n clearTimeout(timeout);\n };\n }, [isOpen, setShouldShow, shouldShow, enterDelay, resolvedExitDelay]);\n\n /** The wrapped content of whats inside the Tooltip */\n const TooltipContent = () => (\n <TooltipBubble\n appearance={defaultAppearance}\n onFocus={show}\n onBlur={hide}\n legacyMouseInteraction={legacyMouseInteraction}\n aria-expanded={isOpen}\n id={id}\n {...rest}\n >\n {content}\n </TooltipBubble>\n );\n\n return (\n <Popout\n content={!isInvalidContent ? TooltipContent : undefined}\n isOpen={isOpen}\n placement={placement}\n qa={{\n \"data-qa-tooltip\": id,\n ...qa,\n }}\n id={id + \"-wrapper\"}\n focusOnContent={false}\n zIndex={zIndex}\n aria-haspopup=\"false\"\n display={truncated ? \"flex\" : undefined}\n disableWrapperAria={true} // required so that the child span doesnt take in redundant aria props\n {...popoutProps}\n >\n <span\n onBlur={hide}\n onFocus={show}\n onMouseEnter={show}\n onMouseLeave={hide}\n style={\n truncated\n ? {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : {}\n }\n >\n {React.isValidElement(children)\n ? React.cloneElement(children as React.ReactElement, {\n //** There may be cases where the Tooltip's child needs to properly describe its role as expanding a drawer, in which case that property takes priority */\n \"aria-expanded\": hasAttribute(children, \"aria-expanded\")\n ? children.props[\"aria-expanded\"]\n : undefined,\n \"aria-describedby\": isOpen ? id : undefined,\n })\n : children}\n </span>\n </Popout>\n );\n};\n\nexport default Tooltip;\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { TypeTooltipContent } from \"./TooltipTypes\";\n\nexport const StyledTooltipContent = styled(Box)<\n Pick<TypeTooltipContent, \"appearance\">\n>`\n font-family: ${(props) => props.theme.fontFamily};\n ${(props) => props.theme.typography[200]}\n text-align: ${(props) => (props.appearance === \"box\" ? \"left\" : \"center\")};\n`;\n","import * as React from \"react\";\nimport type { TypePopoutProps } from \"@sproutsocial/seeds-react-popout\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeTooltipProps\n extends Omit<\n TypeBoxProps,\n \"children\" | \"content\" | \"onMouseEnter\" | \"onMouseLeave\"\n > {\n /** The content that the tooltip should be attached to. Hovering or focusing this element will cause the tooltip to appear */\n children: React.ReactNode;\n\n /** The content to be displayed within the tooltip. If there is no content, just the children are rendered */\n content: React.ReactNode;\n\n /** The placement of the tooltip in relation to the children */\n placement?: TypePopoutProps[\"placement\"];\n\n /** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */\n enterDelay?: number;\n\n /** The time (in ms) that a user has to leave hovered/focused before the tooltip will disappear */\n exitDelay?: number;\n\n /**\n * When true, enables mouse interaction on tooltip content and sets exitDelay\n * to MOTION_DURATION_FAST * 1000 by default (instead of 0).\n */\n legacyMouseInteraction?: boolean;\n\n /** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */\n appearance?: \"pill\" | \"box\";\n qa?: object;\n zIndex?: number;\n\n /** Props to be spread onto the underlying Popout component */\n popoutProps?: Partial<TypePopoutProps>;\n\n /** Truncates text into a single line with ellipsis */\n truncated?: boolean;\n\n ariaProps?: Record<string, string>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface TypeTooltipContent\n extends Pick<TypeTooltipProps, \"appearance\" | \"children\"> {\n onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n legacyMouseInteraction?: boolean;\n}\n","import Tooltip from \"./Tooltip\";\n\nexport default Tooltip;\nexport { Tooltip };\nexport * from \"./TooltipTypes\";\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -12,6 +12,13 @@ interface TypeTooltipProps extends Omit<TypeBoxProps, "children" | "content" | "
|
|
|
12
12
|
placement?: TypePopoutProps["placement"];
|
|
13
13
|
/** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */
|
|
14
14
|
enterDelay?: number;
|
|
15
|
+
/** The time (in ms) that a user has to leave hovered/focused before the tooltip will disappear */
|
|
16
|
+
exitDelay?: number;
|
|
17
|
+
/**
|
|
18
|
+
* When true, enables mouse interaction on tooltip content and sets exitDelay
|
|
19
|
+
* to MOTION_DURATION_FAST * 1000 by default (instead of 0).
|
|
20
|
+
*/
|
|
21
|
+
legacyMouseInteraction?: boolean;
|
|
15
22
|
/** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */
|
|
16
23
|
appearance?: "pill" | "box";
|
|
17
24
|
qa?: object;
|
|
@@ -25,9 +32,10 @@ interface TypeTooltipProps extends Omit<TypeBoxProps, "children" | "content" | "
|
|
|
25
32
|
interface TypeTooltipContent extends Pick<TypeTooltipProps, "appearance" | "children"> {
|
|
26
33
|
onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;
|
|
27
34
|
onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;
|
|
35
|
+
legacyMouseInteraction?: boolean;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
/** Core component */
|
|
31
|
-
declare const Tooltip: ({ content, children, enterDelay, placement, appearance, zIndex, qa, popoutProps, truncated, onFocus, onBlur, ...rest }: TypeTooltipProps) => react_jsx_runtime.JSX.Element;
|
|
39
|
+
declare const Tooltip: ({ content, children, enterDelay, exitDelay, placement, appearance, zIndex, qa, popoutProps, truncated, onFocus, onBlur, legacyMouseInteraction, ...rest }: TypeTooltipProps) => react_jsx_runtime.JSX.Element;
|
|
32
40
|
|
|
33
41
|
export { Tooltip, type TypeTooltipContent, type TypeTooltipProps, Tooltip as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,13 @@ interface TypeTooltipProps extends Omit<TypeBoxProps, "children" | "content" | "
|
|
|
12
12
|
placement?: TypePopoutProps["placement"];
|
|
13
13
|
/** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */
|
|
14
14
|
enterDelay?: number;
|
|
15
|
+
/** The time (in ms) that a user has to leave hovered/focused before the tooltip will disappear */
|
|
16
|
+
exitDelay?: number;
|
|
17
|
+
/**
|
|
18
|
+
* When true, enables mouse interaction on tooltip content and sets exitDelay
|
|
19
|
+
* to MOTION_DURATION_FAST * 1000 by default (instead of 0).
|
|
20
|
+
*/
|
|
21
|
+
legacyMouseInteraction?: boolean;
|
|
15
22
|
/** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */
|
|
16
23
|
appearance?: "pill" | "box";
|
|
17
24
|
qa?: object;
|
|
@@ -25,9 +32,10 @@ interface TypeTooltipProps extends Omit<TypeBoxProps, "children" | "content" | "
|
|
|
25
32
|
interface TypeTooltipContent extends Pick<TypeTooltipProps, "appearance" | "children"> {
|
|
26
33
|
onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;
|
|
27
34
|
onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;
|
|
35
|
+
legacyMouseInteraction?: boolean;
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
/** Core component */
|
|
31
|
-
declare const Tooltip: ({ content, children, enterDelay, placement, appearance, zIndex, qa, popoutProps, truncated, onFocus, onBlur, ...rest }: TypeTooltipProps) => react_jsx_runtime.JSX.Element;
|
|
39
|
+
declare const Tooltip: ({ content, children, enterDelay, exitDelay, placement, appearance, zIndex, qa, popoutProps, truncated, onFocus, onBlur, legacyMouseInteraction, ...rest }: TypeTooltipProps) => react_jsx_runtime.JSX.Element;
|
|
32
40
|
|
|
33
41
|
export { Tooltip, type TypeTooltipContent, type TypeTooltipProps, Tooltip as default };
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,7 @@ var TooltipBubble = ({
|
|
|
61
61
|
children,
|
|
62
62
|
onFocus,
|
|
63
63
|
onBlur,
|
|
64
|
+
legacyMouseInteraction,
|
|
64
65
|
...rest
|
|
65
66
|
}) => {
|
|
66
67
|
const handleFocus = (e) => {
|
|
@@ -83,10 +84,12 @@ var TooltipBubble = ({
|
|
|
83
84
|
boxShadow: "medium",
|
|
84
85
|
border: 500,
|
|
85
86
|
borderColor: "container.border.base",
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
...legacyMouseInteraction && {
|
|
88
|
+
onFocus: handleFocus,
|
|
89
|
+
onBlur: handleBlur,
|
|
90
|
+
onMouseEnter: handleFocus,
|
|
91
|
+
onMouseLeave: handleBlur
|
|
92
|
+
},
|
|
90
93
|
tabIndex: 0,
|
|
91
94
|
...rest,
|
|
92
95
|
children
|
|
@@ -97,6 +100,7 @@ var Tooltip = ({
|
|
|
97
100
|
content,
|
|
98
101
|
children,
|
|
99
102
|
enterDelay = import_unitless.default.MOTION_DURATION_FAST * 1e3,
|
|
103
|
+
exitDelay,
|
|
100
104
|
placement = "auto",
|
|
101
105
|
appearance,
|
|
102
106
|
zIndex = 7,
|
|
@@ -105,12 +109,14 @@ var Tooltip = ({
|
|
|
105
109
|
truncated = false,
|
|
106
110
|
onFocus,
|
|
107
111
|
onBlur,
|
|
112
|
+
legacyMouseInteraction,
|
|
108
113
|
...rest
|
|
109
114
|
}) => {
|
|
110
115
|
const [shouldShow, setShouldShow] = (0, import_react.useState)(false);
|
|
111
116
|
const [isOpen, setIsOpen] = (0, import_react.useState)(false);
|
|
112
117
|
const [id] = (0, import_react.useState)(`Racine-tooltip-${idCounter++}`);
|
|
113
118
|
const isInvalidContent = content === null || content === void 0;
|
|
119
|
+
const resolvedExitDelay = exitDelay !== void 0 ? exitDelay : legacyMouseInteraction ? import_unitless.default.MOTION_DURATION_FAST * 1e3 : 0;
|
|
114
120
|
const show = (e) => {
|
|
115
121
|
onFocus?.(e);
|
|
116
122
|
setShouldShow(true);
|
|
@@ -119,7 +125,6 @@ var Tooltip = ({
|
|
|
119
125
|
onBlur?.(e);
|
|
120
126
|
setShouldShow(false);
|
|
121
127
|
};
|
|
122
|
-
const exitDelay = import_unitless.default.MOTION_DURATION_FAST * 1e3;
|
|
123
128
|
const defaultAppearance = appearance || (typeof content === "object" ? "box" : "pill");
|
|
124
129
|
(0, import_react.useEffect)(() => {
|
|
125
130
|
const documentBody = document.body;
|
|
@@ -135,7 +140,7 @@ var Tooltip = ({
|
|
|
135
140
|
} else {
|
|
136
141
|
timeout = setTimeout(() => {
|
|
137
142
|
setIsOpen(false);
|
|
138
|
-
},
|
|
143
|
+
}, resolvedExitDelay);
|
|
139
144
|
}
|
|
140
145
|
if (isOpen) {
|
|
141
146
|
documentBody.addEventListener("keydown", onEsc, { capture: true });
|
|
@@ -144,13 +149,14 @@ var Tooltip = ({
|
|
|
144
149
|
documentBody.removeEventListener("keydown", onEsc, { capture: true });
|
|
145
150
|
clearTimeout(timeout);
|
|
146
151
|
};
|
|
147
|
-
}, [isOpen, setShouldShow, shouldShow, enterDelay,
|
|
152
|
+
}, [isOpen, setShouldShow, shouldShow, enterDelay, resolvedExitDelay]);
|
|
148
153
|
const TooltipContent = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
149
154
|
TooltipBubble,
|
|
150
155
|
{
|
|
151
156
|
appearance: defaultAppearance,
|
|
152
157
|
onFocus: show,
|
|
153
158
|
onBlur: hide,
|
|
159
|
+
legacyMouseInteraction,
|
|
154
160
|
"aria-expanded": isOpen,
|
|
155
161
|
id,
|
|
156
162
|
...rest,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Tooltip.tsx","../src/styles.ts","../src/TooltipTypes.ts"],"sourcesContent":["import Tooltip from \"./Tooltip\";\n\nexport default Tooltip;\nexport { Tooltip };\nexport * from \"./TooltipTypes\";\n","import * as React from \"react\";\nimport { useState, useEffect } from \"react\";\nimport MOTION from \"@sproutsocial/seeds-motion/unitless\";\nimport Popout from \"@sproutsocial/seeds-react-popout\";\nimport { StyledTooltipContent } from \"./styles\";\nimport type { TypeTooltipProps, TypeTooltipContent } from \"./TooltipTypes\";\n\nlet idCounter = 0;\n\nconst hasAttribute = (child: React.ReactNode, attribute: string) => {\n return React.isValidElement(child) && child.props[attribute] !== undefined;\n};\n\n/** Tooltip Styled Popout wrapper for handling events */\nconst TooltipBubble = ({\n appearance = \"pill\",\n children,\n onFocus,\n onBlur,\n ...rest\n}: TypeTooltipContent) => {\n // @ts-ignore Will fix during refactor\n const handleFocus = (e) => {\n onFocus(e);\n };\n // @ts-ignore Will fix during refactor\n const handleBlur = (e) => {\n onBlur(e);\n };\n return (\n <StyledTooltipContent\n role=\"tooltip\"\n appearance={appearance}\n borderRadius={appearance === \"box\" ? 500 : \"5000em\"}\n px={400}\n py={appearance === \"box\" ? 400 : 200}\n m={200}\n color=\"text.body\"\n bg=\"container.background.base\"\n boxShadow=\"medium\"\n border={500}\n borderColor=\"container.border.base\"\n onFocus={handleFocus}\n onBlur={handleBlur}\n onMouseEnter={handleFocus}\n onMouseLeave={handleBlur}\n tabIndex={0}\n {...rest}\n >\n {children}\n </StyledTooltipContent>\n );\n};\n\n/** Core component */\nconst Tooltip = ({\n content,\n children,\n enterDelay = MOTION.MOTION_DURATION_FAST * 1000,\n placement = \"auto\",\n appearance,\n zIndex = 7,\n qa,\n popoutProps,\n truncated = false,\n onFocus,\n onBlur,\n ...rest\n}: TypeTooltipProps) => {\n const [shouldShow, setShouldShow] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n const [id] = useState(`Racine-tooltip-${idCounter++}`);\n const isInvalidContent = content === null || content === undefined;\n\n // @ts-ignore Will fix during refactor\n const show = (e) => {\n onFocus?.(e);\n setShouldShow(true);\n };\n // @ts-ignore Will fix during refactor\n const hide = (e) => {\n onBlur?.(e);\n setShouldShow(false);\n };\n\n const exitDelay = MOTION.MOTION_DURATION_FAST * 1000;\n const defaultAppearance =\n appearance || (typeof content === \"object\" ? \"box\" : \"pill\");\n\n /** Handles all the logic around whether to display/not display */\n useEffect(() => {\n const documentBody = document.body;\n let timeout;\n const onEsc = (e: KeyboardEvent): void => {\n // older browsers use \"Esc\"\n if ([\"Escape\", \"Esc\"].includes(e.key)) {\n setIsOpen(false);\n setShouldShow(false);\n }\n };\n\n if (shouldShow) {\n timeout = setTimeout(() => setIsOpen(true), enterDelay);\n } else {\n timeout = setTimeout(() => {\n setIsOpen(false);\n }, exitDelay);\n }\n\n // We only want listeners from the tooltip if its open in the first place\n if (isOpen) {\n documentBody.addEventListener(\"keydown\", onEsc, { capture: true });\n }\n return () => {\n documentBody.removeEventListener(\"keydown\", onEsc, { capture: true });\n clearTimeout(timeout);\n };\n }, [isOpen, setShouldShow, shouldShow, enterDelay, exitDelay]);\n\n /** The wrapped content of whats inside the Tooltip */\n const TooltipContent = () => (\n <TooltipBubble\n appearance={defaultAppearance}\n onFocus={show}\n onBlur={hide}\n aria-expanded={isOpen}\n id={id}\n {...rest}\n >\n {content}\n </TooltipBubble>\n );\n\n return (\n <Popout\n content={!isInvalidContent ? TooltipContent : undefined}\n isOpen={isOpen}\n placement={placement}\n qa={{\n \"data-qa-tooltip\": id,\n ...qa,\n }}\n id={id + \"-wrapper\"}\n focusOnContent={false}\n zIndex={zIndex}\n aria-haspopup=\"false\"\n display={truncated ? \"flex\" : undefined}\n disableWrapperAria={true} // required so that the child span doesnt take in redundant aria props\n {...popoutProps}\n >\n <span\n onBlur={hide}\n onFocus={show}\n onMouseEnter={show}\n onMouseLeave={hide}\n style={\n truncated\n ? {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : {}\n }\n >\n {React.isValidElement(children)\n ? React.cloneElement(children as React.ReactElement, {\n //** There may be cases where the Tooltip's child needs to properly describe its role as expanding a drawer, in which case that property takes priority */\n \"aria-expanded\": hasAttribute(children, \"aria-expanded\")\n ? children.props[\"aria-expanded\"]\n : undefined,\n \"aria-describedby\": isOpen ? id : undefined,\n })\n : children}\n </span>\n </Popout>\n );\n};\n\nexport default Tooltip;\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { TypeTooltipContent } from \"./TooltipTypes\";\n\nexport const StyledTooltipContent = styled(Box)<\n Pick<TypeTooltipContent, \"appearance\">\n>`\n font-family: ${(props) => props.theme.fontFamily};\n ${(props) => props.theme.typography[200]}\n text-align: ${(props) => (props.appearance === \"box\" ? \"left\" : \"center\")};\n`;\n","import * as React from \"react\";\nimport type { TypePopoutProps } from \"@sproutsocial/seeds-react-popout\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeTooltipProps\n extends Omit<\n TypeBoxProps,\n \"children\" | \"content\" | \"onMouseEnter\" | \"onMouseLeave\"\n > {\n /** The content that the tooltip should be attached to. Hovering or focusing this element will cause the tooltip to appear */\n children: React.ReactNode;\n\n /** The content to be displayed within the tooltip. If there is no content, just the children are rendered */\n content: React.ReactNode;\n\n /** The placement of the tooltip in relation to the children */\n placement?: TypePopoutProps[\"placement\"];\n\n /** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */\n enterDelay?: number;\n\n /** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */\n appearance?: \"pill\" | \"box\";\n qa?: object;\n zIndex?: number;\n\n /** Props to be spread onto the underlying Popout component */\n popoutProps?: Partial<TypePopoutProps>;\n\n /** Truncates text into a single line with ellipsis */\n truncated?: boolean;\n\n ariaProps?: Record<string, string>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface TypeTooltipContent\n extends Pick<TypeTooltipProps, \"appearance\" | \"children\"> {\n onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,mBAAoC;AACpC,sBAAmB;AACnB,gCAAmB;;;ACHnB,+BAAmB;AACnB,6BAAgB;AAGT,IAAM,2BAAuB,yBAAAA,SAAO,uBAAAC,OAAG;AAAA,iBAG7B,CAAC,UAAU,MAAM,MAAM,UAAU;AAAA,IAC9C,CAAC,UAAU,MAAM,MAAM,WAAW,GAAG,CAAC;AAAA,gBAC1B,CAAC,UAAW,MAAM,eAAe,QAAQ,SAAS,QAAS;AAAA;;;ADqBvE;AAvBJ,IAAI,YAAY;AAEhB,IAAM,eAAe,CAAC,OAAwB,cAAsB;AAClE,SAAa,qBAAe,KAAK,KAAK,MAAM,MAAM,SAAS,MAAM;AACnE;AAGA,IAAM,gBAAgB,CAAC;AAAA,EACrB,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA0B;AAExB,QAAM,cAAc,CAAC,MAAM;AACzB,YAAQ,CAAC;AAAA,EACX;AAEA,QAAM,aAAa,CAAC,MAAM;AACxB,WAAO,CAAC;AAAA,EACV;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA,cAAc,eAAe,QAAQ,MAAM;AAAA,MAC3C,IAAI;AAAA,MACJ,IAAI,eAAe,QAAQ,MAAM;AAAA,MACjC,GAAG;AAAA,MACH,OAAM;AAAA,MACN,IAAG;AAAA,MACH,WAAU;AAAA,MACV,QAAQ;AAAA,MACR,aAAY;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,cAAc;AAAA,MACd,UAAU;AAAA,MACT,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAGA,IAAM,UAAU,CAAC;AAAA,EACf;AAAA,EACA;AAAA,EACA,aAAa,gBAAAC,QAAO,uBAAuB;AAAA,EAC3C,YAAY;AAAA,EACZ;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAwB;AACtB,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAC1C,QAAM,CAAC,EAAE,QAAI,uBAAS,kBAAkB,WAAW,EAAE;AACrD,QAAM,mBAAmB,YAAY,QAAQ,YAAY;AAGzD,QAAM,OAAO,CAAC,MAAM;AAClB,cAAU,CAAC;AACX,kBAAc,IAAI;AAAA,EACpB;AAEA,QAAM,OAAO,CAAC,MAAM;AAClB,aAAS,CAAC;AACV,kBAAc,KAAK;AAAA,EACrB;AAEA,QAAM,YAAY,gBAAAA,QAAO,uBAAuB;AAChD,QAAM,oBACJ,eAAe,OAAO,YAAY,WAAW,QAAQ;AAGvD,8BAAU,MAAM;AACd,UAAM,eAAe,SAAS;AAC9B,QAAI;AACJ,UAAM,QAAQ,CAAC,MAA2B;AAExC,UAAI,CAAC,UAAU,KAAK,EAAE,SAAS,EAAE,GAAG,GAAG;AACrC,kBAAU,KAAK;AACf,sBAAc,KAAK;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,YAAY;AACd,gBAAU,WAAW,MAAM,UAAU,IAAI,GAAG,UAAU;AAAA,IACxD,OAAO;AACL,gBAAU,WAAW,MAAM;AACzB,kBAAU,KAAK;AAAA,MACjB,GAAG,SAAS;AAAA,IACd;AAGA,QAAI,QAAQ;AACV,mBAAa,iBAAiB,WAAW,OAAO,EAAE,SAAS,KAAK,CAAC;AAAA,IACnE;AACA,WAAO,MAAM;AACX,mBAAa,oBAAoB,WAAW,OAAO,EAAE,SAAS,KAAK,CAAC;AACpE,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,QAAQ,eAAe,YAAY,YAAY,SAAS,CAAC;AAG7D,QAAM,iBAAiB,MACrB;AAAA,IAAC;AAAA;AAAA,MACC,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,iBAAe;AAAA,MACf;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAGF,SACE;AAAA,IAAC,0BAAAC;AAAA,IAAA;AAAA,MACC,SAAS,CAAC,mBAAmB,iBAAiB;AAAA,MAC9C;AAAA,MACA;AAAA,MACA,IAAI;AAAA,QACF,mBAAmB;AAAA,QACnB,GAAG;AAAA,MACL;AAAA,MACA,IAAI,KAAK;AAAA,MACT,gBAAgB;AAAA,MAChB;AAAA,MACA,iBAAc;AAAA,MACd,SAAS,YAAY,SAAS;AAAA,MAC9B,oBAAoB;AAAA,MACnB,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,cAAc;AAAA,UACd,cAAc;AAAA,UACd,OACE,YACI;AAAA,YACE,UAAU;AAAA,YACV,cAAc;AAAA,YACd,YAAY;AAAA,UACd,IACA,CAAC;AAAA,UAGN,UAAM,qBAAe,QAAQ,IACpB,mBAAa,UAAgC;AAAA;AAAA,YAEjD,iBAAiB,aAAa,UAAU,eAAe,IACnD,SAAS,MAAM,eAAe,IAC9B;AAAA,YACJ,oBAAoB,SAAS,KAAK;AAAA,UACpC,CAAC,IACD;AAAA;AAAA,MACN;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,kBAAQ;;;AEnLf,IAAAC,SAAuB;;;AHEvB,IAAO,gBAAQ;","names":["styled","Box","MOTION","Popout","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Tooltip.tsx","../src/styles.ts","../src/TooltipTypes.ts"],"sourcesContent":["import Tooltip from \"./Tooltip\";\n\nexport default Tooltip;\nexport { Tooltip };\nexport * from \"./TooltipTypes\";\n","import * as React from \"react\";\nimport { useState, useEffect } from \"react\";\nimport MOTION from \"@sproutsocial/seeds-motion/unitless\";\nimport Popout from \"@sproutsocial/seeds-react-popout\";\nimport { StyledTooltipContent } from \"./styles\";\nimport type { TypeTooltipProps, TypeTooltipContent } from \"./TooltipTypes\";\n\nlet idCounter = 0;\n\nconst hasAttribute = (child: React.ReactNode, attribute: string) => {\n return React.isValidElement(child) && child.props[attribute] !== undefined;\n};\n\n/** Tooltip Styled Popout wrapper for handling events */\nconst TooltipBubble = ({\n appearance = \"pill\",\n children,\n onFocus,\n onBlur,\n legacyMouseInteraction,\n ...rest\n}: TypeTooltipContent) => {\n // @ts-ignore Will fix during refactor\n const handleFocus = (e) => {\n onFocus(e);\n };\n // @ts-ignore Will fix during refactor\n const handleBlur = (e) => {\n onBlur(e);\n };\n return (\n <StyledTooltipContent\n role=\"tooltip\"\n appearance={appearance}\n borderRadius={appearance === \"box\" ? 500 : \"5000em\"}\n px={400}\n py={appearance === \"box\" ? 400 : 200}\n m={200}\n color=\"text.body\"\n bg=\"container.background.base\"\n boxShadow=\"medium\"\n border={500}\n borderColor=\"container.border.base\"\n {...(legacyMouseInteraction && {\n onFocus: handleFocus,\n onBlur: handleBlur,\n onMouseEnter: handleFocus,\n onMouseLeave: handleBlur,\n })}\n tabIndex={0}\n {...rest}\n >\n {children}\n </StyledTooltipContent>\n );\n};\n\n/** Core component */\nconst Tooltip = ({\n content,\n children,\n enterDelay = MOTION.MOTION_DURATION_FAST * 1000,\n exitDelay,\n placement = \"auto\",\n appearance,\n zIndex = 7,\n qa,\n popoutProps,\n truncated = false,\n onFocus,\n onBlur,\n legacyMouseInteraction,\n ...rest\n}: TypeTooltipProps) => {\n const [shouldShow, setShouldShow] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n const [id] = useState(`Racine-tooltip-${idCounter++}`);\n const isInvalidContent = content === null || content === undefined;\n\n // Compute exitDelay based on legacyMouseInteraction if not explicitly provided\n const resolvedExitDelay =\n exitDelay !== undefined\n ? exitDelay\n : legacyMouseInteraction\n ? MOTION.MOTION_DURATION_FAST * 1000\n : 0;\n\n // @ts-ignore Will fix during refactor\n const show = (e) => {\n onFocus?.(e);\n setShouldShow(true);\n };\n // @ts-ignore Will fix during refactor\n const hide = (e) => {\n onBlur?.(e);\n setShouldShow(false);\n };\n\n const defaultAppearance =\n appearance || (typeof content === \"object\" ? \"box\" : \"pill\");\n\n /** Handles all the logic around whether to display/not display */\n useEffect(() => {\n const documentBody = document.body;\n let timeout;\n const onEsc = (e: KeyboardEvent): void => {\n // older browsers use \"Esc\"\n if ([\"Escape\", \"Esc\"].includes(e.key)) {\n setIsOpen(false);\n setShouldShow(false);\n }\n };\n\n if (shouldShow) {\n timeout = setTimeout(() => setIsOpen(true), enterDelay);\n } else {\n timeout = setTimeout(() => {\n setIsOpen(false);\n }, resolvedExitDelay);\n }\n\n // We only want listeners from the tooltip if its open in the first place\n if (isOpen) {\n documentBody.addEventListener(\"keydown\", onEsc, { capture: true });\n }\n return () => {\n documentBody.removeEventListener(\"keydown\", onEsc, { capture: true });\n clearTimeout(timeout);\n };\n }, [isOpen, setShouldShow, shouldShow, enterDelay, resolvedExitDelay]);\n\n /** The wrapped content of whats inside the Tooltip */\n const TooltipContent = () => (\n <TooltipBubble\n appearance={defaultAppearance}\n onFocus={show}\n onBlur={hide}\n legacyMouseInteraction={legacyMouseInteraction}\n aria-expanded={isOpen}\n id={id}\n {...rest}\n >\n {content}\n </TooltipBubble>\n );\n\n return (\n <Popout\n content={!isInvalidContent ? TooltipContent : undefined}\n isOpen={isOpen}\n placement={placement}\n qa={{\n \"data-qa-tooltip\": id,\n ...qa,\n }}\n id={id + \"-wrapper\"}\n focusOnContent={false}\n zIndex={zIndex}\n aria-haspopup=\"false\"\n display={truncated ? \"flex\" : undefined}\n disableWrapperAria={true} // required so that the child span doesnt take in redundant aria props\n {...popoutProps}\n >\n <span\n onBlur={hide}\n onFocus={show}\n onMouseEnter={show}\n onMouseLeave={hide}\n style={\n truncated\n ? {\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }\n : {}\n }\n >\n {React.isValidElement(children)\n ? React.cloneElement(children as React.ReactElement, {\n //** There may be cases where the Tooltip's child needs to properly describe its role as expanding a drawer, in which case that property takes priority */\n \"aria-expanded\": hasAttribute(children, \"aria-expanded\")\n ? children.props[\"aria-expanded\"]\n : undefined,\n \"aria-describedby\": isOpen ? id : undefined,\n })\n : children}\n </span>\n </Popout>\n );\n};\n\nexport default Tooltip;\n","import styled from \"styled-components\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport type { TypeTooltipContent } from \"./TooltipTypes\";\n\nexport const StyledTooltipContent = styled(Box)<\n Pick<TypeTooltipContent, \"appearance\">\n>`\n font-family: ${(props) => props.theme.fontFamily};\n ${(props) => props.theme.typography[200]}\n text-align: ${(props) => (props.appearance === \"box\" ? \"left\" : \"center\")};\n`;\n","import * as React from \"react\";\nimport type { TypePopoutProps } from \"@sproutsocial/seeds-react-popout\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeTooltipProps\n extends Omit<\n TypeBoxProps,\n \"children\" | \"content\" | \"onMouseEnter\" | \"onMouseLeave\"\n > {\n /** The content that the tooltip should be attached to. Hovering or focusing this element will cause the tooltip to appear */\n children: React.ReactNode;\n\n /** The content to be displayed within the tooltip. If there is no content, just the children are rendered */\n content: React.ReactNode;\n\n /** The placement of the tooltip in relation to the children */\n placement?: TypePopoutProps[\"placement\"];\n\n /** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */\n enterDelay?: number;\n\n /** The time (in ms) that a user has to leave hovered/focused before the tooltip will disappear */\n exitDelay?: number;\n\n /**\n * When true, enables mouse interaction on tooltip content and sets exitDelay\n * to MOTION_DURATION_FAST * 1000 by default (instead of 0).\n */\n legacyMouseInteraction?: boolean;\n\n /** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */\n appearance?: \"pill\" | \"box\";\n qa?: object;\n zIndex?: number;\n\n /** Props to be spread onto the underlying Popout component */\n popoutProps?: Partial<TypePopoutProps>;\n\n /** Truncates text into a single line with ellipsis */\n truncated?: boolean;\n\n ariaProps?: Record<string, string>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface TypeTooltipContent\n extends Pick<TypeTooltipProps, \"appearance\" | \"children\"> {\n onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;\n legacyMouseInteraction?: boolean;\n}\n"],"mappings":"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","names":["styled","Box","MOTION","Popout","React"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-tooltip",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Seeds React Tooltip",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"test:watch": "jest --watch --coverage=false"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@sproutsocial/seeds-react-theme": "^3.3.
|
|
21
|
+
"@sproutsocial/seeds-react-theme": "^3.3.2",
|
|
22
22
|
"@sproutsocial/seeds-react-system-props": "^3.0.1",
|
|
23
|
-
"@sproutsocial/seeds-react-popout": "^2.4.
|
|
24
|
-
"@sproutsocial/seeds-react-box": "^1.1.
|
|
25
|
-
"@sproutsocial/seeds-motion": "^1.8.
|
|
23
|
+
"@sproutsocial/seeds-react-popout": "^2.4.18",
|
|
24
|
+
"@sproutsocial/seeds-react-box": "^1.1.10",
|
|
25
|
+
"@sproutsocial/seeds-motion": "^1.8.2"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/react": "^18.0.0",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"@sproutsocial/seeds-tsconfig": "*",
|
|
36
36
|
"@sproutsocial/seeds-testing": "*",
|
|
37
37
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
38
|
-
"@sproutsocial/seeds-react-icon": "^2.1.
|
|
39
|
-
"@sproutsocial/seeds-react-banner": "^1.0.
|
|
40
|
-
"@sproutsocial/seeds-react-button": "^1.3.
|
|
38
|
+
"@sproutsocial/seeds-react-icon": "^2.1.1",
|
|
39
|
+
"@sproutsocial/seeds-react-banner": "^1.0.18",
|
|
40
|
+
"@sproutsocial/seeds-react-button": "^1.3.12",
|
|
41
41
|
"@sproutsocial/seeds-react-text": "^1.3.2",
|
|
42
42
|
"@sproutsocial/seeds-react-portal": "^1.1.4"
|
|
43
43
|
},
|
package/src/Tooltip.stories.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import Banner from "@sproutsocial/seeds-react-banner";
|
|
|
3
3
|
import Box from "@sproutsocial/seeds-react-box";
|
|
4
4
|
import Button from "@sproutsocial/seeds-react-button";
|
|
5
5
|
import Icon from "@sproutsocial/seeds-react-icon";
|
|
6
|
+
import Text from "@sproutsocial/seeds-react-text";
|
|
6
7
|
import Tooltip from "./Tooltip";
|
|
7
8
|
|
|
8
9
|
const meta: Meta<typeof Tooltip> = {
|
|
@@ -127,3 +128,60 @@ export const WithFocusableContent: Story = {
|
|
|
127
128
|
</Box>
|
|
128
129
|
),
|
|
129
130
|
};
|
|
131
|
+
|
|
132
|
+
export const WithLegacyMouseInteraction: Story = {
|
|
133
|
+
render: () => (
|
|
134
|
+
<Box
|
|
135
|
+
width="100%"
|
|
136
|
+
p={600}
|
|
137
|
+
alignItems="center"
|
|
138
|
+
justifyContent="center"
|
|
139
|
+
display="flex"
|
|
140
|
+
flexDirection="column"
|
|
141
|
+
gap={400}
|
|
142
|
+
height="400px"
|
|
143
|
+
>
|
|
144
|
+
<Box display="flex" flexDirection="column" alignItems="center" gap={200}>
|
|
145
|
+
<Tooltip
|
|
146
|
+
content={
|
|
147
|
+
<Box>
|
|
148
|
+
<div>Hover into this content!</div>
|
|
149
|
+
<Button
|
|
150
|
+
appearance="secondary"
|
|
151
|
+
size="small"
|
|
152
|
+
mt={200}
|
|
153
|
+
onClick={() => alert("Clicked!")}
|
|
154
|
+
>
|
|
155
|
+
Interactive button
|
|
156
|
+
</Button>
|
|
157
|
+
</Box>
|
|
158
|
+
}
|
|
159
|
+
legacyMouseInteraction={true}
|
|
160
|
+
>
|
|
161
|
+
<Button appearance="primary">With legacyMouseInteraction</Button>
|
|
162
|
+
</Tooltip>
|
|
163
|
+
<Text fontSize={300} color="text.body.secondary">
|
|
164
|
+
Tooltip stays open when you hover into it (exitDelay: ~150ms)
|
|
165
|
+
</Text>
|
|
166
|
+
</Box>
|
|
167
|
+
|
|
168
|
+
<Box display="flex" flexDirection="column" alignItems="center" gap={200}>
|
|
169
|
+
<Tooltip
|
|
170
|
+
content={
|
|
171
|
+
<Box>
|
|
172
|
+
<div>Try to hover into this content</div>
|
|
173
|
+
<Button appearance="secondary" size="small" mt={200}>
|
|
174
|
+
Can't interact
|
|
175
|
+
</Button>
|
|
176
|
+
</Box>
|
|
177
|
+
}
|
|
178
|
+
>
|
|
179
|
+
<Button appearance="primary">Without legacyMouseInteraction</Button>
|
|
180
|
+
</Tooltip>
|
|
181
|
+
<Text fontSize={300} color="text.body.secondary">
|
|
182
|
+
Tooltip closes immediately when you leave (exitDelay: 0ms)
|
|
183
|
+
</Text>
|
|
184
|
+
</Box>
|
|
185
|
+
</Box>
|
|
186
|
+
),
|
|
187
|
+
};
|
package/src/Tooltip.tsx
CHANGED
|
@@ -17,6 +17,7 @@ const TooltipBubble = ({
|
|
|
17
17
|
children,
|
|
18
18
|
onFocus,
|
|
19
19
|
onBlur,
|
|
20
|
+
legacyMouseInteraction,
|
|
20
21
|
...rest
|
|
21
22
|
}: TypeTooltipContent) => {
|
|
22
23
|
// @ts-ignore Will fix during refactor
|
|
@@ -40,10 +41,12 @@ const TooltipBubble = ({
|
|
|
40
41
|
boxShadow="medium"
|
|
41
42
|
border={500}
|
|
42
43
|
borderColor="container.border.base"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
{...(legacyMouseInteraction && {
|
|
45
|
+
onFocus: handleFocus,
|
|
46
|
+
onBlur: handleBlur,
|
|
47
|
+
onMouseEnter: handleFocus,
|
|
48
|
+
onMouseLeave: handleBlur,
|
|
49
|
+
})}
|
|
47
50
|
tabIndex={0}
|
|
48
51
|
{...rest}
|
|
49
52
|
>
|
|
@@ -57,6 +60,7 @@ const Tooltip = ({
|
|
|
57
60
|
content,
|
|
58
61
|
children,
|
|
59
62
|
enterDelay = MOTION.MOTION_DURATION_FAST * 1000,
|
|
63
|
+
exitDelay,
|
|
60
64
|
placement = "auto",
|
|
61
65
|
appearance,
|
|
62
66
|
zIndex = 7,
|
|
@@ -65,6 +69,7 @@ const Tooltip = ({
|
|
|
65
69
|
truncated = false,
|
|
66
70
|
onFocus,
|
|
67
71
|
onBlur,
|
|
72
|
+
legacyMouseInteraction,
|
|
68
73
|
...rest
|
|
69
74
|
}: TypeTooltipProps) => {
|
|
70
75
|
const [shouldShow, setShouldShow] = useState(false);
|
|
@@ -72,6 +77,14 @@ const Tooltip = ({
|
|
|
72
77
|
const [id] = useState(`Racine-tooltip-${idCounter++}`);
|
|
73
78
|
const isInvalidContent = content === null || content === undefined;
|
|
74
79
|
|
|
80
|
+
// Compute exitDelay based on legacyMouseInteraction if not explicitly provided
|
|
81
|
+
const resolvedExitDelay =
|
|
82
|
+
exitDelay !== undefined
|
|
83
|
+
? exitDelay
|
|
84
|
+
: legacyMouseInteraction
|
|
85
|
+
? MOTION.MOTION_DURATION_FAST * 1000
|
|
86
|
+
: 0;
|
|
87
|
+
|
|
75
88
|
// @ts-ignore Will fix during refactor
|
|
76
89
|
const show = (e) => {
|
|
77
90
|
onFocus?.(e);
|
|
@@ -83,7 +96,6 @@ const Tooltip = ({
|
|
|
83
96
|
setShouldShow(false);
|
|
84
97
|
};
|
|
85
98
|
|
|
86
|
-
const exitDelay = MOTION.MOTION_DURATION_FAST * 1000;
|
|
87
99
|
const defaultAppearance =
|
|
88
100
|
appearance || (typeof content === "object" ? "box" : "pill");
|
|
89
101
|
|
|
@@ -104,7 +116,7 @@ const Tooltip = ({
|
|
|
104
116
|
} else {
|
|
105
117
|
timeout = setTimeout(() => {
|
|
106
118
|
setIsOpen(false);
|
|
107
|
-
},
|
|
119
|
+
}, resolvedExitDelay);
|
|
108
120
|
}
|
|
109
121
|
|
|
110
122
|
// We only want listeners from the tooltip if its open in the first place
|
|
@@ -115,7 +127,7 @@ const Tooltip = ({
|
|
|
115
127
|
documentBody.removeEventListener("keydown", onEsc, { capture: true });
|
|
116
128
|
clearTimeout(timeout);
|
|
117
129
|
};
|
|
118
|
-
}, [isOpen, setShouldShow, shouldShow, enterDelay,
|
|
130
|
+
}, [isOpen, setShouldShow, shouldShow, enterDelay, resolvedExitDelay]);
|
|
119
131
|
|
|
120
132
|
/** The wrapped content of whats inside the Tooltip */
|
|
121
133
|
const TooltipContent = () => (
|
|
@@ -123,6 +135,7 @@ const Tooltip = ({
|
|
|
123
135
|
appearance={defaultAppearance}
|
|
124
136
|
onFocus={show}
|
|
125
137
|
onBlur={hide}
|
|
138
|
+
legacyMouseInteraction={legacyMouseInteraction}
|
|
126
139
|
aria-expanded={isOpen}
|
|
127
140
|
id={id}
|
|
128
141
|
{...rest}
|
package/src/TooltipTypes.ts
CHANGED
|
@@ -19,6 +19,15 @@ export interface TypeTooltipProps
|
|
|
19
19
|
/** The time (in ms) that a user has to be hovered/focused before the tooltip will appear */
|
|
20
20
|
enterDelay?: number;
|
|
21
21
|
|
|
22
|
+
/** The time (in ms) that a user has to leave hovered/focused before the tooltip will disappear */
|
|
23
|
+
exitDelay?: number;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* When true, enables mouse interaction on tooltip content and sets exitDelay
|
|
27
|
+
* to MOTION_DURATION_FAST * 1000 by default (instead of 0).
|
|
28
|
+
*/
|
|
29
|
+
legacyMouseInteraction?: boolean;
|
|
30
|
+
|
|
22
31
|
/** Used to override the appearance of the Tooltip content. By default, strings will have the 'pill' appearance, and more complex content will have the 'box' appearance. You can change those defaults by setting this prop. */
|
|
23
32
|
appearance?: "pill" | "box";
|
|
24
33
|
qa?: object;
|
|
@@ -38,4 +47,5 @@ export interface TypeTooltipContent
|
|
|
38
47
|
extends Pick<TypeTooltipProps, "appearance" | "children"> {
|
|
39
48
|
onFocus: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;
|
|
40
49
|
onBlur: (e: React.FocusEvent<HTMLDivElement, FocusEvent>) => void;
|
|
50
|
+
legacyMouseInteraction?: boolean;
|
|
41
51
|
}
|
|
@@ -311,4 +311,80 @@ describe("Tooltip", () => {
|
|
|
311
311
|
expect(tooltipByRole).toContainElement(tooltipElement);
|
|
312
312
|
});
|
|
313
313
|
});
|
|
314
|
+
|
|
315
|
+
describe("legacyMouseInteraction prop", () => {
|
|
316
|
+
it("should allow hovering over tooltip content when legacyMouseInteraction is true", async () => {
|
|
317
|
+
render(
|
|
318
|
+
<Tooltip content="Tooltip content" legacyMouseInteraction>
|
|
319
|
+
<Button data-testid="trigger">Trigger</Button>
|
|
320
|
+
</Tooltip>
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const trigger = screen.getByTestId("trigger");
|
|
324
|
+
|
|
325
|
+
// Hover over trigger to open tooltip
|
|
326
|
+
act(() => {
|
|
327
|
+
fireEvent.mouseOver(trigger);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Wait for tooltip to appear
|
|
331
|
+
const tooltipContent = await screen.findByText("Tooltip content");
|
|
332
|
+
expect(tooltipContent).toBeInTheDocument();
|
|
333
|
+
|
|
334
|
+
// Move mouse away from trigger
|
|
335
|
+
act(() => {
|
|
336
|
+
fireEvent.mouseOut(trigger);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Hover over the tooltip content itself
|
|
340
|
+
act(() => {
|
|
341
|
+
fireEvent.mouseEnter(tooltipContent);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Tooltip should remain open
|
|
345
|
+
await act(async () => {
|
|
346
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
347
|
+
});
|
|
348
|
+
expect(tooltipContent).toBeInTheDocument();
|
|
349
|
+
|
|
350
|
+
// Move mouse away from tooltip content
|
|
351
|
+
act(() => {
|
|
352
|
+
fireEvent.mouseLeave(tooltipContent);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Tooltip should eventually close
|
|
356
|
+
await act(async () => {
|
|
357
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("should not have mouse handlers on tooltip content when legacyMouseInteraction is false", async () => {
|
|
362
|
+
render(
|
|
363
|
+
<Tooltip content="Tooltip content">
|
|
364
|
+
<Button data-testid="trigger">Trigger</Button>
|
|
365
|
+
</Tooltip>
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const trigger = screen.getByTestId("trigger");
|
|
369
|
+
|
|
370
|
+
// Hover over trigger to open tooltip
|
|
371
|
+
act(() => {
|
|
372
|
+
fireEvent.mouseOver(trigger);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Wait for tooltip to appear
|
|
376
|
+
const tooltipContent = await screen.findByText("Tooltip content");
|
|
377
|
+
expect(tooltipContent).toBeInTheDocument();
|
|
378
|
+
|
|
379
|
+
// Verify mouse handlers are not attached (tooltip should close immediately when leaving trigger)
|
|
380
|
+
act(() => {
|
|
381
|
+
fireEvent.mouseOut(trigger);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Without legacyMouseInteraction, tooltip closes immediately (exitDelay: 0)
|
|
385
|
+
await act(async () => {
|
|
386
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
});
|
|
314
390
|
});
|