Adding in Linked Highlighting

At this point, the application renders a static line and a static radial heatmap. To add in linked highlighting behaviour on mouse-over, we’ll make use of Reflux’s Actions and Stores. If you didn’t want to use the Flux architecture, you could just pass callbacks to the components in the standard React way.

We can use Yeoman to generate a couple of stubs to get started:

$ yo react-webpack:action chart --es6

$ yo react-webpack:store chart --es6

(Note that I renamed the created action file from ChartActionCreators to ChartActions.)

We’ll add one action to ChartActions called highlight and a corresponding callback to the ChartStore that broadcasts the data point to be highlighted.

Now, we can update the LinkedHighlightApp to listen for changes to the store and push the data point to be highlighted to the LineChart and RadialHeatmap via the highlight prop. To do this, we use Reflux’s ListenerMixin to allow LinkedHighlightApp to listen to the ChartStore. I know that Reflux provides some more convenient mixins like connect, but I prefer the explicitness of having this.listenTo in componentDidMount.

Now both LineChart and RadialHeatmap are receiving the same value for the highlight prop, so we just need to configure them to use ChartActions.highlight to update the highlight value in the store when the user mouses over the components.

Hover Behaviour on the Radial Heatmap

The radial heatmap is fairly simple to handle because each data point corresponds to a circle element in the DOM. This means that we can attach mouse listeners using React as we would with any other component to get the behaviour we want. We create a simple callback called _handleHover that takes the hovered on data point as an argument and uses it to call ChartActions.highlight with.

UPDATE 7/31/15: It’s better to use mouseenter and mouseleave events to prevent flickering when the highlight is set to null. React seems to only re-render once after both events have fired as opposed to with mouseover and mouseout.

This gets us the mouse behaviour that we want, but we’ll need to style the highlighted circle differently to make it stand out. To do so, we’ll add the highlight class to the circle whose radius value matches the highlighted point’s radius’ value.

Hover Behaviour on the Line Chart

Getting mouse hover behaviour to work on the line chart is a bit more challenging since we have a single DOM element (the path) that represents all of the data points. To figure out which data point is being hovered on, we’ll use some of D3's utility functions to convert the mouse coordinates into domain coordinates and then find the point that closest matches them.

Instead of using React’s event handling system, I decided to use D3's so I could take advantage of the handy d3.mouse function, which converts the mouse’s position to coordinates relative to the SVG element. I imagine you can make this work with using React’s event handling system some way, but it wasn’t immediately obvious to me how to do it. Using the d3.mouse function allows us to use our D3 scales’ invert functions to get the domain coordinates of the mouse position. To attach the D3 mouse listeners to mousemove and mouseleave, use the componentDidMount and componentDidUnmount functions.

Once we have the domain coordinates, I use a modified version of d3.bisect to find the nearest point in the dataset. One of the problems I had with d3.bisect is that it always returns the closest element to the left of the specified point, or the closest element to the right depending on which function you call. I want it to give me whichever element is closest to the mouse, which is what my function findClosest returns. It’s not the prettiest function, but it is simple and it works.

Now we just need to add in the code to draw a small circle on the line when the highlight prop is set. We can do this by adding a circle to the DOM after the path element, causing the circle to be rendered “on top” of the path.

At this point, we have linked highlighting between the two charts! Hooray!