Skip to content

Commit f979a60

Browse files
authored
Merge pull request #932 from ReactTooltip/feat/tooltip-rendered-element-dom
fix: remove tooltip rendered element from DOM when is not showing
2 parents bfd36f4 + 44ddfd1 commit f979a60

File tree

6 files changed

+228
-150
lines changed

6 files changed

+228
-150
lines changed

docs/docs/options.mdx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,3 @@ import 'react-tooltip/dist/react-tooltip.css';
109109
| data-tooltip-show | number | false | | any `number` | tooltip show will be delayed in miliseconds by the amount of value |
110110
| data-tooltip-hide | number | false | | any `number` | tooltip hide will be delayed in miliseconds by the amount of value |
111111
| data-tooltip-float | boolean | false | `false` | `true` `false` | tooltip will follow the mouse position when it moves inside the anchor element (same as V4's `effect="float"`) |
112-
113-
#### Observations
114-
115-
- When using `data-tooltip-content` the `<br />` works by default
116-
- When using prop `content` the `<br />` doesn't work by default, use prop `html` instead of this

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
]
118118
},
119119
"dependencies": {
120-
"@floating-ui/dom": "^1.0.4",
120+
"@floating-ui/dom": "1.1.1",
121121
"classnames": "^2.3.2"
122122
}
123123
}

src/components/Tooltip/Tooltip.tsx

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,52 @@ const Tooltip = ({
4444
const [inlineStyles, setInlineStyles] = useState({})
4545
const [inlineArrowStyles, setInlineArrowStyles] = useState({})
4646
const [show, setShow] = useState(false)
47+
const [rendered, setRendered] = useState(false)
4748
const wasShowing = useRef(false)
48-
const [calculatingPosition, setCalculatingPosition] = useState(false)
4949
const lastFloatPosition = useRef<IPosition | null>(null)
5050
const { anchorRefs, setActiveAnchor: setProviderActiveAnchor } = useTooltip(id)
5151
const [activeAnchor, setActiveAnchor] = useState<React.RefObject<HTMLElement>>({ current: null })
5252
const hoveringTooltip = useRef(false)
5353

54-
const handleShow = (value: boolean) => {
55-
if (setIsOpen) {
56-
setIsOpen(value)
57-
} else if (isOpen === undefined) {
58-
setShow(value)
54+
useEffect(() => {
55+
if (!show) {
56+
setRendered(false)
5957
}
58+
}, [show])
59+
60+
const handleShow = (value: boolean) => {
61+
setRendered(true)
62+
/**
63+
* wait for the component to render and calculate position
64+
* before actually showing
65+
*/
66+
setTimeout(() => {
67+
setIsOpen?.(value)
68+
if (isOpen === undefined) {
69+
setShow(value)
70+
}
71+
}, 10)
6072
}
6173

74+
/**
75+
* this replicates the effect from `handleShow()`
76+
* when `isOpen` is changed from outside
77+
*/
78+
useEffect(() => {
79+
if (isOpen === undefined) {
80+
return () => null
81+
}
82+
if (isOpen) {
83+
setRendered(true)
84+
}
85+
const timeout = setTimeout(() => {
86+
setShow(isOpen)
87+
}, 10)
88+
return () => {
89+
clearTimeout(timeout)
90+
}
91+
}, [isOpen])
92+
6293
useEffect(() => {
6394
if (show === wasShowing.current) {
6495
return
@@ -117,7 +148,7 @@ const Tooltip = ({
117148
const handleHideTooltip = () => {
118149
if (clickable) {
119150
// allow time for the mouse to reach the tooltip, in case there's a gap
120-
handleHideTooltipDelayed(delayHide || 50)
151+
handleHideTooltipDelayed(delayHide || 100)
121152
} else if (delayHide) {
122153
handleHideTooltipDelayed()
123154
} else {
@@ -144,7 +175,6 @@ const Tooltip = ({
144175
}
145176
},
146177
} as Element
147-
setCalculatingPosition(true)
148178
computeTooltipPosition({
149179
place,
150180
offset,
@@ -154,7 +184,6 @@ const Tooltip = ({
154184
strategy: positionStrategy,
155185
middlewares,
156186
}).then((computedStylesData) => {
157-
setCalculatingPosition(false)
158187
if (Object.keys(computedStylesData.tooltipStyles).length) {
159188
setInlineStyles(computedStylesData.tooltipStyles)
160189
}
@@ -306,7 +335,11 @@ const Tooltip = ({
306335
})
307336
parentObserver.disconnect()
308337
}
309-
}, [anchorRefs, activeAnchor, closeOnEsc, anchorId, events, delayHide, delayShow])
338+
/**
339+
* rendered is also a dependency to ensure anchor observers are re-registered
340+
* since `tooltipRef` becomes stale after removing/adding the tooltip to the DOM
341+
*/
342+
}, [rendered, anchorRefs, activeAnchor, closeOnEsc, anchorId, events, delayHide, delayShow])
310343

311344
useEffect(() => {
312345
if (position) {
@@ -335,7 +368,6 @@ const Tooltip = ({
335368
// `anchorId` element takes precedence
336369
elementReference = document.querySelector(`[id='${anchorId}']`) as HTMLElement
337370
}
338-
setCalculatingPosition(true)
339371
let mounted = true
340372
computeTooltipPosition({
341373
place,
@@ -350,7 +382,6 @@ const Tooltip = ({
350382
// invalidate computed positions after remount
351383
return
352384
}
353-
setCalculatingPosition(false)
354385
if (Object.keys(computedStylesData.tooltipStyles).length) {
355386
setInlineStyles(computedStylesData.tooltipStyles)
356387
}
@@ -361,18 +392,7 @@ const Tooltip = ({
361392
return () => {
362393
mounted = false
363394
}
364-
}, [
365-
show,
366-
isOpen,
367-
anchorId,
368-
activeAnchor,
369-
content,
370-
html,
371-
place,
372-
offset,
373-
positionStrategy,
374-
position,
375-
])
395+
}, [show, anchorId, activeAnchor, content, html, place, offset, positionStrategy, position])
376396

377397
useEffect(() => {
378398
return () => {
@@ -386,13 +406,14 @@ const Tooltip = ({
386406
}, [])
387407

388408
const hasContentOrChildren = Boolean(html || content || children)
409+
const canShow = Boolean(hasContentOrChildren && show && Object.keys(inlineStyles).length > 0)
389410

390-
return (
411+
return rendered ? (
391412
<WrapperElement
392413
id={id}
393414
role="tooltip"
394415
className={classNames('react-tooltip', styles['tooltip'], styles[variant], className, {
395-
[styles['show']]: hasContentOrChildren && !calculatingPosition && (isOpen || show),
416+
[styles['show']]: canShow,
396417
[styles['fixed']]: positionStrategy === 'fixed',
397418
[styles['clickable']]: clickable,
398419
})}
@@ -409,7 +430,7 @@ const Tooltip = ({
409430
ref={tooltipArrowRef}
410431
/>
411432
</WrapperElement>
412-
)
433+
) : null
413434
}
414435

415436
export default Tooltip
Lines changed: 88 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,105 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`tooltip attributes basic tooltip component 1`] = `
4+
<span
5+
data-tooltip-content="Hello World!"
6+
id="basic-example-attr"
7+
>
8+
Lorem Ipsum
9+
</span>
10+
`;
11+
12+
exports[`tooltip attributes tooltip component - delayHide 1`] = `
13+
<span
14+
data-tooltip-content="Hello World!"
15+
data-tooltip-delay-hide={1000}
16+
id="basic-example-delay-hide-attr"
17+
>
18+
Lorem Ipsum
19+
</span>
20+
`;
21+
22+
exports[`tooltip attributes tooltip component - delayShow 1`] = `
23+
<span
24+
data-tooltip-content="Hello World!"
25+
data-tooltip-delay-show={1000}
26+
id="basic-example-delay-show-attr"
27+
>
28+
Lorem Ipsum
29+
</span>
30+
`;
31+
32+
exports[`tooltip attributes tooltip component - html 1`] = `
33+
<span
34+
data-tooltip-html="Hello World!"
35+
data-tooltip-place="top"
36+
data-tooltip-variant="info"
37+
id="basic-example-html-attr"
38+
>
39+
Lorem Ipsum
40+
</span>
41+
`;
42+
43+
exports[`tooltip attributes tooltip component - without anchorId 1`] = `
44+
<span
45+
data-tooltip-content="Hello World!"
46+
>
47+
Lorem Ipsum
48+
</span>
49+
`;
50+
351
exports[`tooltip props basic tooltip component 1`] = `
4-
[
5-
<span
6-
id="basic-example"
7-
>
8-
Lorem Ipsum
9-
</span>,
10-
<div
11-
className="react-tooltip"
12-
role="tooltip"
13-
style={{}}
14-
>
15-
Hello World!
16-
<div
17-
className="react-tooltip-arrow"
18-
style={{}}
19-
/>
20-
</div>,
21-
]
52+
<span
53+
id="basic-example"
54+
>
55+
Lorem Ipsum
56+
</span>
57+
`;
58+
59+
exports[`tooltip props tooltip component - delayHide 1`] = `
60+
<span
61+
id="basic-example-delay-hide"
62+
>
63+
Lorem Ipsum
64+
</span>
65+
`;
66+
67+
exports[`tooltip props tooltip component - delayShow 1`] = `
68+
<span
69+
id="basic-example-delay-show"
70+
>
71+
Lorem Ipsum
72+
</span>
2273
`;
2374

2475
exports[`tooltip props tooltip component - getContent 1`] = `
25-
[
26-
<span
27-
id="basic-example-get-content"
28-
>
29-
Lorem Ipsum
30-
</span>,
31-
<div
32-
className="react-tooltip"
33-
role="tooltip"
34-
style={{}}
35-
>
36-
Hello World!
37-
<div
38-
className="react-tooltip-arrow"
39-
style={{}}
40-
/>
41-
</div>,
42-
]
76+
<span
77+
id="basic-example-get-content"
78+
>
79+
Lorem Ipsum
80+
</span>
4381
`;
4482

4583
exports[`tooltip props tooltip component - html 1`] = `
46-
[
47-
<span
48-
id="basic-example-html"
49-
>
50-
Lorem Ipsum
51-
</span>,
52-
<div
53-
className="react-tooltip"
54-
role="tooltip"
55-
style={{}}
56-
>
57-
<span
58-
dangerouslySetInnerHTML={
59-
{
60-
"__html": "Hello World!",
61-
}
62-
}
63-
/>
64-
<div
65-
className="react-tooltip-arrow"
66-
style={{}}
67-
/>
68-
</div>,
69-
]
84+
<span
85+
id="basic-example-html"
86+
>
87+
Lorem Ipsum
88+
</span>
7089
`;
7190

7291
exports[`tooltip props tooltip component - position props 1`] = `
73-
[
74-
<span
75-
id="position-props"
76-
>
77-
Lorem Ipsum
78-
</span>,
79-
<div
80-
className="react-tooltip"
81-
role="tooltip"
82-
style={{}}
83-
>
84-
Hello World!
85-
<div
86-
className="react-tooltip-arrow"
87-
style={{}}
88-
/>
89-
</div>,
90-
]
92+
<span
93+
id="position-props"
94+
>
95+
Lorem Ipsum
96+
</span>
9197
`;
9298

9399
exports[`tooltip props tooltip component - without anchorId 1`] = `
94-
[
95-
<span>
96-
Lorem Ipsum
97-
</span>,
98-
<div
99-
className="react-tooltip"
100-
role="tooltip"
101-
style={{}}
102-
>
103-
Hello World!
104-
<div
105-
className="react-tooltip-arrow"
106-
style={{}}
107-
/>
108-
</div>,
109-
]
100+
<span>
101+
Lorem Ipsum
102+
</span>
110103
`;
111104

112-
exports[`tooltip props tooltip component - without element reference 1`] = `
113-
<div
114-
className="react-tooltip"
115-
role="tooltip"
116-
style={{}}
117-
>
118-
<div
119-
className="react-tooltip-arrow"
120-
style={{}}
121-
/>
122-
</div>
123-
`;
105+
exports[`tooltip props tooltip component - without element reference 1`] = `null`;

0 commit comments

Comments
 (0)