Victory 0.12.0: The One True Tooltip

September 19, 2016

We are pleased to announce that the latest release of Victory _finally_ includes tooltips! This feature has been in demand for a while, but nailing down an implementation strategy proved challenging. No other chart element exhibits the range of behaviors or visual possibilities as the simple tooltip. The One True Tooltip was impossible to build, because it was impossible to define. Instead we built a set of components that could be configured and combined to create The One True Tooltip however it might look. ## `` ![Demonstration of different tooltip sizes](https://res.cloudinary.com/formidablelabs/image/upload/f\_auto,q\_auto/v1675121564/dotcom/assets-uploads-2016-09-biggerTooltip) ```js } data={[ {x: 2, y: 5, label: "right-side-up"}, {x: 4, y: -6, label: "upside-down"}, {x: 6, y: 4, label: "tiny"}, {x: 8, y: -5, label: "or a little \n BIGGER"}, {x: 10, y: 7, label: "automatically"} ]} style={{ data: {width: 15, fill: "tomato"} }} /> ``` The easiest way to add a tooltip to a chart is to replace the default label component with a tooltip like `labelComponent={}` Any Victory component that uses labels will pass in the appropriate text to your tooltip component. By default, tooltips are automatically sized to accomodate label text, and oriented correctly for each data point. `onMouseOver` and `onMouseOut` event handlers are also automatically registered for each data point. **Customizing `VictoryTooltip`** Default behaviors are incredibly convenient until they get in the way. `VictoryTooltip` is written to be configurable, extendable, and replaceable. Users can alter props directly on the component like `labelComponent={}`. Custom tooltip containers can be created by replacing the default `flyoutCompoment` with a custom path, or any other `svg` element. Event handlers can even been replaced by extending `VictoryTooltip` and overriding its static `defaultEvents` array. ## `` By default, `VictoryTooltip` uses a component called `Flyout` to render the tooltip container. `Flyout` renders a path element that is calculated based on `x`, `y`, `width`, `height`, `orientation`, `cornerRadius`, `pointerLength` and `pointerWidth`. Because the path is calculated rather than scaled or transformed from a hard-coded path, a wide variety of container shapes is possible with minor configuration. ![Tooltips with different styling variations](https://res.cloudinary.com/formidablelabs/image/upload/f\_auto,q\_auto/v1675121564/dotcom/assets-uploads-2016-09-Screen-Shot-2016-09-14-at-11.02.44-AM) If something completely custom is required, a completely custom component can be suplied to `VictoryTooltip`. Here’s an example of an extremely silly custom tooltip. ![Emoji Tooltip](https://res.cloudinary.com/formidablelabs/image/upload/f\_auto,q\_auto/v1675121564/dotcom/assets-uploads-2016-09-emojiTooltip) The custom `HandPointer` component renders emoji hands rather than a path element. ```js class HandPointer extends React.Component { static propTypes = { x: React.PropTypes.number, y: React.PropTypes.number, orientation: React.PropTypes.string }; render() { const size = 70; const pointer = this.props.orientation === "top" ? emoji : emoji; const offset = this.props.orientation === "top" ? 5 : 55; const x = this.props.x - size / 2; const y = this.props.y + offset; return ( {pointer} ); } } ``` Once written, the custom component is supplied to `VictoryTooltip` like `}/>`, and is useable just like any other tooltip. ## `` We wrote `VictoryVoronoiTooltip` to improve the behavior of tooltips on components that are not easy to interact with, like line components or very small scatter points. `VictoryVoronoiTooltip` has more in common with full data components like `VictoryBar` than it does with simple label components, as it renders both data and labels. The data rendered by `VictoryVoronoiTooltip` is a transparent [voronoi diagram](https://en.wikipedia.org/wiki/Voronoi\_diagram), with each polygon corresponding to a closest data point. This creates a much larger interactive area for each data point, and results in more fluid tooltip interactions. ![Victory Voronoi Tooltip](https://res.cloudinary.com/formidablelabs/image/upload/f\_auto,q\_auto/v1675121564/dotcom/assets-uploads-2016-09-voronoiTooltip) To add a voronoi tooltip to a chart, simply provide it with data and labels like so: ```js `x: ${d.x} \n y: ${d.y}`} data={[ {x: 1, y: -5}, {x: 2, y: 4}, {x: 3, y: 2}, {x: 4, y: 3}, {x: 5, y: 1}, {x: 6, y: -3}, {x: 7, y: 3} ]} /> ``` We decided it was a little tedious to supply the same data to each component, so we made an enhancement to the `VictoryGroup` wrapper component so that it can supply data to all its children. The equivalent example using `VictoryGroup` looks like this: ```js `x: ${d.x} \n y: ${d.y}`} /> ``` Much cleaner! **Customizing `VictoryVoronoiTooltip`** `VictoryVoronoiTooltip` is an instance of the `VictoryVoronoi` component that uses `VictoryTooltip` as a default `labelComponent`. Because of this modular assembly, any customizations that are possible for `VictoryTooltip` are easily applied to `VictoryVoronoiTooltip` by simply replacing components. To specify props such as `cornerRadius` and `pointerLength` from `VictoryVoronoiTooltip`, add them directly to the label component `labelComponent` like so: `labelComponent={}` ## Supporting Features Supporting tooltips was the main goal of this release, but the work required to complete this feature resulted in a few cool features and enhancements. **`VictoryVoronoi` Compoment** `VictoryVoronoi` is exposed as its own component rather than being an intrinsic part of `VictoryVoronoiTooltip`. Read more about this component in our [docs](https://formidable.com/open-source/victory/docs/victory-voronoi). **Enhanced `VictoryGroup` wrapper** The enhancements made to `VictoryGroup` are convenient for sharing data and styles between child components, but they also make it possible to stack components as a set. Read more about when to use `VictoryGroup` [here](https://formidable.com/open-source/victory/docs/victory-group). **Arbitrary component events with `defaultEvents`** `VictoryTooltip` attaches events to whatever component uses it by exposing a static `defaultEvents` array. Now any component that is included by another as a `dataComponent`, `labelComponent` etc., can register `defaultEvents` and target elements in the component including it. For example, the default events supplied by `VictoryTooltip` look like this: ```js static defaultEvents = [{ target: "data", eventHandlers: { onMouseOver: () => { return { target: "labels", mutation: () => { return { active: true }; } }; }, onMouseOut: () => { return { target: "labels", mutation: () => { return { active: false }; } }; } } }]; ``` **All rendered components are exported from `VictoryCore`** To make Victory more flexible and easier to extend we’ve exported all small, rendered components (_i.e._ `Point`, `Bar`, `Flyout` etc.). We hope this change will make Victory even more fun to use! _[See the Victory source code.](http://www.github.com/FormidableLabs/victory)\_ _[Read the Victory docs.](https://formidable.com/open-source/victory)\_

Related Posts

Flexible Charting in React with Victory (and Introducing FormidableCharts)

November 9, 2016
Victory: React charts tailored to your data Charting directly with d3 can be difficult, but other libraries are often too simplistic. Enter Victory: React charting that is easier than direct use of d3.js but with as much flexibility as possible. Victory allows fully customized charts ranging from...

Introducing React Game Kit

September 15, 2016
React Game Kit is Formidable’s newest release, written by the one and only Ken Wheeler. Since Ken is busy killing it in Israel at ReactNext, I’m giving a rundown of what React Game Kit is and why you should use it. Ken's slides from his React Next talk can be found here, and I highly recommend...

Announcing Victory 0.10.2 and VictoryNative

August 5, 2016
It’s been a while since the release of Victory 0.9.0 back in June, so we’re excited to add several new features and bug fixes in order to continue making the creation of D3 charts in React as painless as possible. This post will explore some of the highlights of the new...