-
Notifications
You must be signed in to change notification settings - Fork 68
LG-4580: tooltip pinning #2837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
stephl3
wants to merge
11
commits into
main
Choose a base branch
from
steph/ch-tooltip-pinning
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
LG-4580: tooltip pinning #2837
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
ded57d4
Update WithTooltipAndZoom story
stephl3 464efe8
Add and install @leafygreen-ui/icon-button and polished to charts cor…
stephl3 aaa4c24
Assert presence of echartsInstance in callbacks wrapped withInstanceC…
stephl3 439d9e8
Refactor ChartTooltip to use TOOLTIP_ID constant
stephl3 20a777f
Add tooltip pinning logic and refactor tooltip visibility into useToo…
stephl3 62b2407
Hide tooltips when hovering mark lines
stephl3 243a52d
Changeset
stephl3 af1b307
Rename addCb to addUnpinCallbackToCloseButton, fix deps, fix ChartToo…
stephl3 5796f15
Fix colors and add click to pin note
stephl3 649c496
Refactor tooltip sub-components to include max-height with scroll whe…
stephl3 a892fcd
Use requestAnimationFrame to hide tooltip visibility after state updates
stephl3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@lg-charts/core': minor | ||
--- | ||
|
||
Adds tooltip pinning logic and refactors tooltip visibility |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
export const TOOLBOX_ID = 'toolbox'; | ||
export const TOOLTIP_CLOSE_BTN_ID = 'chart_tooltip-close_button'; | ||
export const TOOLTIP_ID = 'tooltip'; | ||
export const X_AXIS_ID = 'x-axis'; | ||
export const Y_AXIS_ID = 'y-axis'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import { EChartEvents } from '../../Echart'; | |
import { getDefaultChartOptions } from '../config'; | ||
|
||
import type { ChartHookProps, ChartInstance } from './useChart.types'; | ||
import { useTooltipVisibility } from './useTooltipVisibility'; | ||
|
||
export function useChart({ | ||
onChartReady = () => {}, | ||
|
@@ -23,6 +24,7 @@ export function useChart({ | |
* element only gets populated after render. | ||
*/ | ||
const [container, setContainer] = useState<HTMLDivElement | null>(null); | ||
|
||
const echart = useEchart({ | ||
container, | ||
initialOptions, | ||
|
@@ -32,7 +34,6 @@ export function useChart({ | |
const { | ||
addToGroup, | ||
enableZoom, | ||
hideTooltip, | ||
off, | ||
on, | ||
ready, | ||
|
@@ -91,24 +92,7 @@ export function useChart({ | |
} | ||
}, [ready, onZoomSelect, on]); | ||
|
||
// We want to hide the tooltip when it's hovered over any `EventMarkerPoint` | ||
useEffect(() => { | ||
if (ready) { | ||
on('mouseover', e => { | ||
if (e.componentType === 'markPoint') { | ||
hideTooltip(); | ||
on('mousemove', hideTooltip); | ||
} | ||
}); | ||
|
||
// Stop hiding once the mouse leaves the `EventMarkerPoint` | ||
on('mouseout', e => { | ||
if (e.componentType === 'markPoint') { | ||
off('mousemove', hideTooltip); | ||
} | ||
}); | ||
} | ||
}, [echart, hideTooltip, off, on, ready]); | ||
const { tooltipPinned, setTooltipMounted } = useTooltipVisibility({ echart }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. abstracted all tooltip visibility logic into |
||
|
||
const initialRenderRef = useRef(true); | ||
|
||
|
@@ -160,6 +144,8 @@ export function useChart({ | |
return { | ||
...echart, | ||
ref: setContainer, | ||
setTooltipMounted, | ||
state, | ||
tooltipPinned, | ||
}; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import { useCallback, useEffect, useState } from 'react'; | ||
|
||
import { EChartEvents, EChartsInstance } from '../../Echart'; | ||
import { TOOLTIP_CLOSE_BTN_ID } from '../constants'; | ||
|
||
import { UseTooltipVisibilityReturnObj } from './useTooltipVisibility.types'; | ||
|
||
/** | ||
* Hook to manage the visibility of the tooltip in the chart including pinning behavior. | ||
*/ | ||
export const useTooltipVisibility = ({ | ||
echart, | ||
}: { | ||
echart: EChartsInstance; | ||
}): UseTooltipVisibilityReturnObj => { | ||
const [tooltipMounted, setTooltipMounted] = useState(false); | ||
const [tooltipPinned, setTooltipPinned] = useState(false); | ||
|
||
const { hideTooltip, off, on, ready, showTooltip } = echart; | ||
|
||
/** | ||
* Event listener callback added to the chart that is called on mouse move | ||
* to show the tooltip. | ||
*/ | ||
const showTooltipOnMouseMove = useCallback( | ||
(params: any) => { | ||
if (!tooltipMounted || tooltipPinned) { | ||
return; | ||
} | ||
|
||
const x = params.offsetX; | ||
const y = params.offsetY; | ||
showTooltip(x, y); | ||
}, | ||
[showTooltip, tooltipMounted, tooltipPinned], | ||
); | ||
|
||
/** | ||
* Event listener callback added to the close button in the tooltip. When called, | ||
* it hides the tooltip and sets the `tooltipPinned` state to false. | ||
*/ | ||
const unpinTooltip = useCallback(() => { | ||
setTooltipPinned(false); | ||
|
||
/** | ||
* We need to use requestAnimationFrame to ensure that the tooltip is hidden | ||
* after the `tooltipPinned` state updates. | ||
*/ | ||
requestAnimationFrame(() => { | ||
hideTooltip(); | ||
}); | ||
}, [hideTooltip]); | ||
|
||
/** | ||
* Helper method to add the `unpinTooltip` event listener to the close button | ||
* in the tooltip. The echarts tooltip `formatter` cannot pass the `onClick` | ||
* event to the button, so we have to add it manually. | ||
*/ | ||
const addUnpinCallbackToCloseButton = useCallback(() => { | ||
const btn = document.getElementById(TOOLTIP_CLOSE_BTN_ID); | ||
|
||
if (btn && !btn.dataset.bound) { | ||
btn.addEventListener('click', unpinTooltip); | ||
btn.dataset.bound = 'true'; // prevents duplicate listeners | ||
} | ||
}, [unpinTooltip]); | ||
|
||
/** | ||
* Event listener callback added to the chart that is called on click | ||
* to show/pin the tooltip and set the `tooltipPinned` state to true. | ||
*/ | ||
const pinTooltipOnClick = useCallback( | ||
(params: any) => { | ||
if (!tooltipMounted || tooltipPinned) { | ||
return; | ||
} | ||
|
||
/** | ||
* Remove the mouse move and click event listeners to prevent the tooltip | ||
* from moving when it is pinned. User can unpin it by clicking the close | ||
* button in the tooltip which will turn the listeners back on. | ||
*/ | ||
off(EChartEvents.MouseMove, showTooltipOnMouseMove, { | ||
useCanvasAsTrigger: true, | ||
}); | ||
off(EChartEvents.Click, pinTooltipOnClick, { useCanvasAsTrigger: true }); | ||
|
||
const x = params.offsetX; | ||
const y = params.offsetY; | ||
|
||
setTooltipPinned(true); | ||
|
||
/** | ||
* We need to use requestAnimationFrame to ensure that the tooltip is shown | ||
* after the `tooltipPinned` state updates. | ||
*/ | ||
requestAnimationFrame(() => { | ||
showTooltip(x, y); | ||
addUnpinCallbackToCloseButton(); | ||
}); | ||
}, | ||
[ | ||
addUnpinCallbackToCloseButton, | ||
showTooltipOnMouseMove, | ||
off, | ||
showTooltip, | ||
tooltipMounted, | ||
tooltipPinned, | ||
], | ||
); | ||
|
||
/** | ||
* Event listener callback that is called when mousing over a mark point. | ||
* It hides the tooltip and disables the chart click event listener. | ||
*/ | ||
const hideTooltipOnMouseOverMark = useCallback( | ||
(params: any) => { | ||
if (!tooltipMounted) { | ||
return; | ||
} | ||
|
||
if ( | ||
params.componentType === 'markPoint' || | ||
params.componentType === 'markLine' | ||
) { | ||
hideTooltip(); | ||
on(EChartEvents.MouseMove, hideTooltip); | ||
off(EChartEvents.Click, pinTooltipOnClick, { | ||
useCanvasAsTrigger: true, | ||
}); | ||
} | ||
Comment on lines
+122
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. proactively updated this to apply to mark lines as well |
||
}, | ||
[hideTooltip, off, on, pinTooltipOnClick, tooltipMounted], | ||
); | ||
|
||
/** | ||
* Event listener callback that is called when mousing out of a mark point. | ||
* It stops hiding the tooltip and re-enables the chart click event listener. | ||
*/ | ||
const stopHideTooltipOnMouseOutMark = useCallback( | ||
(params: any) => { | ||
if (!tooltipMounted) { | ||
return; | ||
} | ||
|
||
if ( | ||
params.componentType === 'markPoint' || | ||
params.componentType === 'markLine' | ||
) { | ||
off(EChartEvents.MouseMove, hideTooltip); | ||
on(EChartEvents.Click, pinTooltipOnClick, { | ||
useCanvasAsTrigger: true, | ||
}); | ||
} | ||
}, | ||
[hideTooltip, off, on, pinTooltipOnClick, tooltipMounted], | ||
); | ||
|
||
/** | ||
* Effect to turn on the tooltip event listeners when the chart is ready and tooltip | ||
* is not already pinned. | ||
*/ | ||
useEffect(() => { | ||
if (!ready) { | ||
return; | ||
} | ||
|
||
if (tooltipPinned) { | ||
return; | ||
} | ||
|
||
on(EChartEvents.MouseMove, showTooltipOnMouseMove, { | ||
useCanvasAsTrigger: true, | ||
}); | ||
on(EChartEvents.Click, pinTooltipOnClick, { | ||
useCanvasAsTrigger: true, | ||
}); | ||
}, [on, pinTooltipOnClick, ready, showTooltipOnMouseMove, tooltipPinned]); | ||
|
||
/** | ||
* Effect to add the event listeners to hide the tooltip when hovering a mark point. | ||
*/ | ||
useEffect(() => { | ||
if (!ready) { | ||
return; | ||
} | ||
|
||
on(EChartEvents.MouseOver, hideTooltipOnMouseOverMark); | ||
on(EChartEvents.MouseOut, stopHideTooltipOnMouseOutMark); | ||
}, [ | ||
pinTooltipOnClick, | ||
showTooltipOnMouseMove, | ||
hideTooltipOnMouseOverMark, | ||
stopHideTooltipOnMouseOutMark, | ||
hideTooltip, | ||
off, | ||
on, | ||
ready, | ||
tooltipPinned, | ||
]); | ||
|
||
return { | ||
setTooltipMounted, | ||
tooltipPinned, | ||
}; | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed debounce in previous PR but forgot to drop this dep