Skip to content
Advertisement

SVG overlapping shapes with Recharts

I am using Recharts to create a simple pie chart. My issue likely stems from the fact the entire thing is SVG-based. I would like to have a selected pie slice change color (both fill and stroke). So I do the following:

import { useState } from 'react';
import { Sector, Pie, PieChart, ResponsiveContainer } from 'recharts';

export function SinglePie({ data }) {
    const [selectedCellLabel, setHighlightedCell] = useState(undefined);

    const onMouseEnter = (event) => setHighlightedCell(event.payload.payload.label);
    const onMouseLeave = () => setHighlightedCell(undefined);

    function renderActiveShape(props) {
        return <Sector {...props} fill="#CFBCF7" stroke="#7C53E4" />;
    }

    return (
        <ResponsiveContainer width="100%" height="100%">
            <PieChart>
                <Pie
                    data={data}
                    activeIndex={data.findIndex((datum) => datum.label === selectedCellLabel)}
                    activeShape={renderActiveShape}
                    dataKey={ChartDataKeys.VALUE}
                    nameKey={ChartDataKeys.LABEL}
                    fill="#CEF1EE"
                    stroke="#64E0D5"
                    onMouseEnter={onMouseEnter}
                    onMouseLeave={onMouseLeave}
                />
            </PieChart>
        </ResponsiveContainer>
    );
}

Then render with:

<div style={{ width: 250, height: 250 }}>
    <SinglePie data={[
        {
            label: 'First Slice',
            value: 4,
        },
        {
            label: 'Second Slice',
            value: 6,
        },
        {
            label: 'Third Slice',
            value: 2,
        },
    ]} />
</div>

And unfortunately, on hovering the First Slice, what I see is this:

enter image description here

However, hovering the Third Slice looks ok:

enter image description here

You can see the difference between stroke width in the two cases. this is because the SVG slices overlap each other. I know that SVG rendering is based on order, and adding a z prop won’t help. But what will?

I would like to be able to see all slices with their strokes, as is required by my UI designer:

enter image description here

Advertisement

Answer

You can use .raise() function of d3 library.

First, you need to add <script src="https://d3js.org/d3.v7.min.js"></script> tag to your index.html.

And then you can move the selected slice to last in your onMouseEnter function like this:

const onMouseEnter = (event) => {
    setHighlightedCell(event.payload.payload.label);
    const selectedIndex = data.findIndex(
      (datum) => datum.label === event.payload.payload.label
    );
    if (selectedIndex > -1) {
      const gListItem = d3.select(
        `.recharts-pie-sector:has(path[name="${event.payload.payload.label}"])`
      );
      gListItem.raise();
    }
};

You can take a look at this stackblitz for a live working example of this solution.

Advertisement