refactor(paths): replace LemonDropdown with Popover in PathNodeCard#51996
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Switch from LemonDropdown to Popover for path node cards so the popover stays open when hovering over it, allowing users to interact with clickable elements like continuing/drop-off person counts. Also adds data-attr attributes to the card and its popover for analytics tracking.
2cb70a5 to
a6a35f9
Compare
|
Size Change: +816 B (0%) Total Size: 110 MB ℹ️ View Unchanged
|
| const [isCardHovered, setIsCardHovered] = useState(false) | ||
| const [isPopoverHovered, setIsPopoverHovered] = useState(false) | ||
| const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null) | ||
|
|
||
| const showPopover = isCardHovered || isPopoverHovered | ||
|
|
||
| const clearHideTimeout = (): void => { | ||
| if (hideTimeoutRef.current) { | ||
| clearTimeout(hideTimeoutRef.current) | ||
| hideTimeoutRef.current = null | ||
| } | ||
| } | ||
|
|
||
| const scheduleHide = (setter: (v: boolean) => void): void => { | ||
| hideTimeoutRef.current = setTimeout(() => setter(false), 100) | ||
| } |
There was a problem hiding this comment.
Memory leak: The timeout is never cleaned up when the component unmounts. If the component unmounts while hideTimeoutRef.current has a pending timeout, it will attempt to call setState on an unmounted component, causing a memory leak and potential errors.
Fix: Add a cleanup effect:
useEffect(() => {
return () => {
clearHideTimeout()
}
}, [])| const [isCardHovered, setIsCardHovered] = useState(false) | |
| const [isPopoverHovered, setIsPopoverHovered] = useState(false) | |
| const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null) | |
| const showPopover = isCardHovered || isPopoverHovered | |
| const clearHideTimeout = (): void => { | |
| if (hideTimeoutRef.current) { | |
| clearTimeout(hideTimeoutRef.current) | |
| hideTimeoutRef.current = null | |
| } | |
| } | |
| const scheduleHide = (setter: (v: boolean) => void): void => { | |
| hideTimeoutRef.current = setTimeout(() => setter(false), 100) | |
| } | |
| const [isCardHovered, setIsCardHovered] = useState(false) | |
| const [isPopoverHovered, setIsPopoverHovered] = useState(false) | |
| const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null) | |
| const showPopover = isCardHovered || isPopoverHovered | |
| const clearHideTimeout = (): void => { | |
| if (hideTimeoutRef.current) { | |
| clearTimeout(hideTimeoutRef.current) | |
| hideTimeoutRef.current = null | |
| } | |
| } | |
| const scheduleHide = (setter: (v: boolean) => void): void => { | |
| hideTimeoutRef.current = setTimeout(() => setter(false), 100) | |
| } | |
| useEffect(() => { | |
| return () => { | |
| clearHideTimeout() | |
| } | |
| }, []) | |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
|
🎭 Playwright report · View test results → ❌ 2 failed tests:
If your changes intentionally update screenshots: add the These issues are not necessarily caused by your changes. |

Problem
Changes
How did you test this code?
👉 Stay up-to-date with PostHog coding conventions for a smoother review.
Publish to changelog?
Docs update