Skip to content

Donut chart

TIP

For core principles, concept description, API and changelog, refer to the D3 chart.

Basic usage

tsx
import { Chart } from '@semcore/ui/d3-chart';
import React from 'react';

import DonutMockData from './mock';

const Demo = () => {
  return (
    <div style={{ width: '450px' }}>
      <Chart.Donut plotWidth={300} plotHeight={300} data={data} aria-label='Donut chart' />
    </div>
  );
};

const data = DonutMockData.Default;

export default Demo;

Advanced usage

  • You can draw donut and pie charts with the Donut component.
  • Pie is a separate sector.
  • Label is a text label inside the chart.
tsx
import { Flex } from '@semcore/ui/base-components';
import { Donut, Plot } from '@semcore/ui/d3-chart';
import { Text } from '@semcore/ui/typography';
import React from 'react';

import DonutMockData from './mock';

const Demo = () => {
  return (
    <Plot width={300} height={300} data={data}>
      <Donut innerRadius={100}>
        <Donut.Pie dataKey='a' name='Pie 1' />
        <Donut.Pie dataKey='b' name='Pie 2' />
        <Donut.Pie dataKey='c' name='Pie 3' />
        <Donut.Label>
          <Text tag='tspan' fill='#191b23' size={400}>
            Example
          </Text>
        </Donut.Label>
      </Donut>
      <Donut.Tooltip>
        {({ dataKey, name }) => {
          return {
            children: (
              <>
                <Donut.Tooltip.Title>{name}</Donut.Tooltip.Title>
                <Flex justifyContent='space-between'>
                  {/* @ts-ignore */}
                  <Text bold>{data[dataKey]}</Text>
                </Flex>
              </>
            ),
          };
        }}
      </Donut.Tooltip>
    </Plot>
  );
};

const data = DonutMockData.Default;

export default Demo;

Controlled highlight

Use active property to control segments highlight.

tsx
import { Flex } from '@semcore/ui/base-components';
import Checkbox from '@semcore/ui/checkbox';
import { Donut, Plot } from '@semcore/ui/d3-chart';
import React from 'react';

import DonutMockData from './mock';

const Demo = () => {
  const [selected, setSelected] = React.useState(['b']);
  const handleCheckboxToggle = React.useCallback(
    (name: any) => () => {
      setSelected((selected) => {
        if (selected.includes(name)) {
          return selected.filter((selectedName) => selectedName !== name);
        } else {
          return [...selected, name];
        }
      });
    },
    [setSelected],
  );

  return (
    <Flex mt={3} alignItems='flex-start' flexWrap>
      <Plot height={120} width={120} m='0 28px 24px 0' data={data}>
        <Donut innerRadius={30}>
          {Object.keys(data).map((name, index) => (
            <Donut.Pie
              key={name}
              dataKey={name}
              name={`Pie ${index}`}
              active={selected.includes(name)}
            />
          ))}
        </Donut>
      </Plot>
      <Flex direction='column'>
        {Object.keys(data).map((name, index) => {
          return (
            <Checkbox key={name} id={name} theme={`chart-palette-order-${index + 1}`}>
              <Checkbox.Value
                value={name}
                checked={selected.includes(name)}
                onChange={handleCheckboxToggle(name)}
              />
              <Checkbox.Text>{`Option ${name.toUpperCase()}`}</Checkbox.Text>
            </Checkbox>
          );
        })}
      </Flex>
    </Flex>
  );
};

const data = DonutMockData.Default;

export default Demo;

Semi-Donut

To create a half-size chart, you need to specify the halfsize value and reduce the height of the chart by half.

tsx
import { Flex } from '@semcore/ui/base-components';
import { Plot, Donut } from '@semcore/ui/d3-chart';
import { Text } from '@semcore/ui/typography';
import React from 'react';

import DonutMockData from './mock';

const Demo = () => {
  return (
    <Plot width={300} height={150} data={data}>
      <Donut halfsize innerRadius={100}>
        <Donut.Pie dataKey='a' name='Pie 1' />
        <Donut.Pie dataKey='b' name='Pie 2' />
        <Donut.Pie dataKey='c' name='Pie 3' />
        <Donut.Label label='71,240 engagements'>
          <Text tag='tspan' x='0' dy='-1.2em' fill='#191b23' size={600}>
            71,240
          </Text>
          <Text tag='tspan' x='0' dy='1.2em' fill='#6c6e79' size={200}>
            Engagements
          </Text>
        </Donut.Label>
      </Donut>
      <Donut.Tooltip>
        {({ dataKey, name }) => {
          return {
            children: (
              <>
                <Donut.Tooltip.Title>{name}</Donut.Tooltip.Title>
                <Flex justifyContent='space-between'>
                  {/* @ts-ignore */}
                  <Text bold>{data[dataKey]}</Text>
                </Flex>
              </>
            ),
          };
        }}
      </Donut.Tooltip>
    </Plot>
  );
};

const data = DonutMockData.Default;

export default Demo;

Edge cases

  • If any data is missing – don't display it on the chart.
  • If only one value is known – display it with a small sector. Be sure to also specify the percentage or value of the unknown data.
tsx
import { Plot, Donut } from '@semcore/ui/d3-chart';
import React from 'react';

import DonutMockData from './mock';

const Demo = () => {
  return (
    <Plot width={300} height={300} data={data}>
      <Donut innerRadius={100}>
        <Donut.EmptyData />
        <Donut.Pie dataKey='a' name='a' />
        <Donut.Pie dataKey='b' name='b' />
        <Donut.Pie dataKey='c' name='c' />
      </Donut>
    </Plot>
  );
};

const data = DonutMockData.EdgeCase;

export default Demo;
  • If there is no data – show an empty gray chart.
tsx
import { Plot, Donut } from '@semcore/ui/d3-chart';
import React from 'react';

import DonutMockData from './mock';

const Demo = () => {
  return (
    <Plot width={300} height={300} data={data}>
      <Donut innerRadius={100}>
        <Donut.EmptyData />
        <Donut.Pie dataKey='a' name='a' />
        <Donut.Pie dataKey='b' name='b' />
        <Donut.Pie dataKey='c' name='c' />
      </Donut>
    </Plot>
  );
};

const data = DonutMockData.EdgeCase;

export default Demo;

Legend and pattern fill

Note that for ChartLegend patterns property works only with default shape={'Checkbox'}.

tsx
import { Flex } from '@semcore/ui/base-components';
import { ChartLegend, Donut, makeDataHintsContainer, Plot } from '@semcore/ui/d3-chart';
import React from 'react';

import DonutMockData from './mock';

const dataHints = makeDataHintsContainer();

const Demo = () => {
  const width = 250;
  const height = 250;

  const [legendItems, setLegendItems] = React.useState(
    Object.keys(data).map((item, index) => {
      return {
        id: item,
        label: `Category ${index + 1}`,
        checked: true,
        color: `chart-palette-order-${index + 1}`,
      };
    }),
  );

  const [highlightedLine, setHighlightedLine] = React.useState(-1);

  const handleChangeVisible = React.useCallback((id: string, isVisible: boolean) => {
    setLegendItems((prevItems) => {
      return prevItems.map((item) => {
        if (item.id === id) {
          item.checked = isVisible;
        }

        return item;
      });
    });
  }, []);

  const handleMouseEnter = React.useCallback((id: string) => {
    setHighlightedLine(legendItems.findIndex((line) => line.id === id));
  }, []);
  const handleMouseLeave = React.useCallback(() => {
    setHighlightedLine(-1);
  }, []);

  return (
    <Flex direction='row' gap={5}>
      <Plot width={width} height={height} data={data} dataHints={dataHints} patterns>
        <Donut innerRadius={height / 2 - 50}>
          {legendItems.filter((item) => item.checked).length === 0 && <Donut.EmptyData />}
          {legendItems.map((item, index) => {
            return (
              item.checked && (
                <Donut.Pie
                  dataKey={item.id}
                  key={item.id}
                  name={item.label}
                  color={item.color}
                  transparent={highlightedLine !== -1 && highlightedLine !== index}
                />
              )
            );
          })}
        </Donut>
      </Plot>
      <ChartLegend
        direction='column'
        wMin={100}
        items={legendItems}
        onChangeVisibleItem={handleChangeVisible}
        onMouseEnterItem={handleMouseEnter}
        onMouseLeaveItem={handleMouseLeave}
        dataHints={dataHints}
        patterns
        aria-label='Donut chart legend'
      />
    </Flex>
  );
};

const data = DonutMockData.Default;

export default Demo;

Released under the MIT License.

Released under the MIT License.