vx tooltip
npm install @vx/tooltipThe @vx/tooltip package provides utilities for making it easy to add Tooltips to a visualization
and includes hooks, higher-order component (HOC) enhancers, and Tooltip components.
```
npm install --save @vx/tooltip
This package provides two ways to add tooltip state logic to your chart components:
- a hook: useTooltip()withTooltip()
- a higher order component (HOC):
The useTooltip hook is the recommended way to add tooltip state logic to your components, but canwithTooltip
only be used in functional components. The HOC can be used with both functional and
class components, and is the recommended way to add tooltip state logic to class components.
Both useTooltip and withTooltip expose the same values and functions for use in your component:
| Name | Type | Description |
| :------------ | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| showTooltip | func | Call this function with the signature func({ tooltipData, tooltipLeft, tooltipTop }) to set the tooltip state to the specified values. |showTooltip
| hideTooltip | func | Call this function to close a tooltip, i.e., set the state to false. |tooltipLeft
| tooltipOpen | bool | Whether the tooltip state is open or closed |
| tooltipLeft | number | The position passed to the showTooltip func, intended to be used for tooltip positioning |tooltipTop
| tooltipTop | number | The position passed to the showTooltip func, intended to be used for tooltip positioning |tooltipData
| tooltipData | any | The value passed to the showTooltip func, intended to be used for any data that your tooltip might need to render |func({ tooltipOpen, tooltipLeft, tooltipTop, tooltipData })
| updateTooltip | func | Call this function with the signature to set the tooltip state to the specified values. |
In the case of useTooltip, these will be returned from the useTooltip() call in your component.withTooltip
In the case of , they will be passed as props to your wrapped component. Refer to the
Examples section for a basic demo of each approach.
#### useTooltip()
If you would like to add tooltip state logic to a functional component, you may use the
useTooltip() hook which will return an object with several properties that you can use to managediv
the tooltip state of your component. **For correct tooltip positioning, it is important to wrap your
component in an element (e.g., ) with relative positioning**. This is handled for you by thewithTooltip HOC, but not with the useTooltip() hook.
#### withTooltip(BaseComponent [, containerProps [, renderContainer]])
If you would like to add tooltip state logic to a class component, you may wrap it in
withTooltip(BaseComponent [, containerProps [, renderContainer]).
The HOC will wrap your component in a div with relative positioning by default and handle stateBaseComponent
for tooltip positioning, visibility, and content by injecting the following props into your:
You may override the container by specifying containerProps as the second argument towithTooltip, or by specifying renderContainer as the third argument to withTooltip.
Tooltip components render tooltip state and can be used in conjunction with useTooltip andwithTooltip above.
#### Tooltip
This is a simple Tooltip container component meant to be used to actually render a Tooltip. It
accepts the following props, and will spread any additional props on the tooltip container div
(i.e., ...restProps):
| Name | Type | Default | Description |
| :-------- | :--------------- | :------ | :---------------------------------------------------------------------------- |
| left | number or string | -- | Sets style.left of the tooltip container |
| top | number or string | -- | Sets style.top of the tooltip container |
| className | string | -- | Adds a class (in addition to vx-tooltip-portal) to the tooltip container |
| style | object | -- | Sets / overrides any styles on the tooltip container (including top and left) |
| children | node | -- | Sets the children of the tooltip, i.e., the actual content |
| unstyled | bool | true | Whether the tooltip should use styles from the style prop or not |
#### TooltipWithBounds
This tooltip component is exactly the same as Tooltip above, but it is aware of its boundaries
meaning that it will flip left/right and bottom/top based on whether it would overflow its parent's
boundaries. It accepts the following props, and will spread any additional props on the Tooltip
component (i.e., ...restProps):
| Name | Type | Default | Description |
| :---------- | :----- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| left | number | -- | The horizontal position of the cursor, tooltip will be place to the left or right of this coordinate depending on the width of the tooltip and the size of the parent container. |
| top | number | -- | The vertical position of the cursor, tooltip will be place to the bottom or top of this coordinate depending on the height of the tooltip and the size of the parent container. |
| offsetLeft | number | 10 | Horizontal offset of the tooltip from the passed left value, functions as a horizontal padding. |top
| offsetRight | number | 10 | Vertical offset of the tooltip from the passed value, functions as a vertical padding. |
| style | object | -- | Sets / overrides any styles on the tooltip container (including top and left) |
| children | node | -- | Sets the children of the tooltip, i.e., the actual content |
| unstyled | bool | true | Whether the tooltip should use styles from the style prop or not |
Note that this component is positioned using a transform, so overriding left and top via
styles may have no effect.
#### useTooltipInPortal
##### ⚠️ ResizeObserver dependency
This hook relies on ResizeObservers. If you need a polyfill, you can either polute the windowpolyfill
object or inject it cleanly using the config option below.
useTooltipInPortal is a hook which gives you a TooltipInPortal component for rendering TooltipTooltipWithBounds
or in a Portal, outside of your component DOM tree which can be useful in manyPortal
circumstances (see below for more on s).
##### API
`ts
type Options = {
/* whether TooltipWithBounds should be used to auto-detect (page) boundaries and reposition itself. /
detectBounds?: boolean;
/ Debounce resize or scroll events in milliseconds (needed for positioning) /
debounce?: number | { scroll: number; resize: number }
/* React to nested scroll changes, don't use this if you know your view is static /
scroll?: boolean
/* You can optionally inject a resize-observer polyfill /
polyfill?: { new (cb: ResizeObserverCallback): ResizeObserver }
}
useTooltipInPortal(
options: Options = { debounce: 0, scroll: true, detectBounds: true }
): {
/* Set ref={containerRef} on the element corresponding to the coordinate system that left/top (passed to TooltipInPortal) are relative to. /
containerRef: React.MutableRefObject
/* Access to the container's bounding box if useful to you. This will be empty on first render. /
containterBounds: RectReadOnly;
/* React.FunctionComponent
TooltipInPortal ({ top: containerTop, left: containerLeft, ...tooltipProps }: TooltipProps) => ReactNode;
interface RectReadOnly {
readonly x: number
readonly y: number
readonly width: number
readonly height: number
readonly top: number
readonly right: number
readonly bottom: number
readonly left: number
}
`
#### Portal
Portal is a component which simply renders its children inside a div element appended todocument.body created by ReactDOM. A Portal can be an effective strategy for solving thez-index
( stacking contextTooltip
problem)[rg/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context] fors.
For example, if your chart is rendered inside a stacking context with a lower z-index than az-index
surrounding container, it may get clipped by that container even if you specify a higher .Portal
This is solvable with a because the separate container will not be subject to the stacking
context of your chart.
To use a Portal, simply pass your Tooltip as a child: . Youleft
will also need to correct the and top positions to be in _page coordinates_, not thePortal
coordinates of your container which you would use when _not_ using a . If reacting to aevent.pageX/Y
mouse event, you can use . Alternatively, if you have container coordinates, you canuseTooltipInPortal
convert them to page coordinates using the following (note: does handles this
for you):
`js`
const pageX = containerX + containerBoundingBox.left + window.scrollLeft;
const pageY = containerY + containerBoundingBox.top + window.scrollTop;
#### useTooltip and useTooltipInPortal For Functional Components
`jsx
import { useTooltip, useTooltipInPortal, TooltipWithBounds } from '@vx/tooltip';
import { localPoint } from '@vx/event';
const ChartWithTooltip = () => {
const {
tooltipData,
tooltipLeft,
tooltipTop,
tooltipOpen,
showTooltip,
hideTooltip,
} = useTooltip();
// If you don't want to use a Portal, simply replace TooltipInPortal below withTooltip
// or TooltipWithBounds and remove containerRef
const { containerRef, TooltipInPortal } = useTooltipInPortal({
// use TooltipWithBounds
detectBounds: true,
// when tooltip containers are scrolled, this will correctly update the Tooltip position
scroll: true,
})
const handleMouseOver = (event, datum) => {
const coords = localPoint(event.target.ownerSVGElement, event);
showTooltip({
tooltipLeft: coords.x,
tooltipTop: coords.y,
tooltipData: datum
});
};
return (
// Set ref={containerRef} on the element corresponding to the coordinate system thatleft/top
// (passed to TooltipInPortal) are relative to.
<>
{tooltipOpen && (
key={Math.random()}
top={tooltipTop}
left={tooltipLeft}
>
Data value {tooltipData}
)}
>
)
};
render(
`
#### withTooltip For Class Components
`js
import { withTooltip, TooltipWithBounds } from '@vx/tooltip';
import { localPoint } from '@vx/event';
class Chart extends React.Component {
handleMouseOver = (event, datum) => {
const coords = localPoint(event.target.ownerSVGElement, event);
this.props.showTooltip({
tooltipLeft: coords.x,
tooltipTop: coords.y,
tooltipData: datum
});
};
render() {
const {
tooltipData,
tooltipLeft,
tooltipTop,
tooltipOpen,
hideTooltip
} = this.props;
return (
// note React.Fragment is only available in >= react@16.2
{tooltipOpen && (
key={Math.random()}
top={tooltipTop}
left={tooltipLeft}
>
Data value {tooltipData}
)}
);
}
}
const ChartWithTooltip = withTooltip(Chart);
render(
``
Example codesandbox here.