Stacked bar chart
TIP
For core principles, concept description, API and changelog, refer to the D3 chart.
Basic usage
tsx
import React from 'react';
import { Chart } from '@semcore/ui/d3-chart';
const Demo = () => {
return (
<Chart.Bar
groupKey={'bar'}
data={data}
plotWidth={500}
plotHeight={200}
type={'stack'}
aria-label={'Stacked bar chart'}
/>
);
};
const data = Array(5)
.fill({})
.map((d, i) => ({
bar: `Bar ${i + 1}`,
Category1: Math.random() * 10,
Category2: Math.random() * 10,
}));
export default Demo;
Bar
Use scaleBand
and scaleLinear
for creating bar charts. See d3 Ordinal Scales for more information.
scaleBand
can work with non-numeric values, so be sure to specify a complete list of values in domain
instead of just minimum and maximum values.
Stacked bar chart
To draw a stacked chart, use <StackBar/>
and <StackBar.Bar/>
.
tsx
import React from 'react';
import { Plot, StackBar, YAxis, XAxis, HoverRect } from '@semcore/ui/d3-chart';
import { scaleLinear, scaleBand } from 'd3-scale';
import { Box, Flex } from '@semcore/ui/flex-box';
import { Text } from '@semcore/ui/typography';
const Demo = () => {
const MARGIN = 40;
const width = 500;
const height = 220;
const xScale = scaleBand()
.range([MARGIN, width - MARGIN])
.domain(data.map((d) => d.bar))
.paddingInner(0.4)
.paddingOuter(0.2);
const yScale = scaleLinear()
.range([height - MARGIN, MARGIN])
.domain([0, 20]);
return (
<Plot data={data} scale={[xScale, yScale]} width={width} height={height}>
<YAxis>
<YAxis.Ticks />
<YAxis.Grid />
</YAxis>
<XAxis>
<XAxis.Ticks />
</XAxis>
<HoverRect.Tooltip x='category' wMin={100}>
{({ xIndex }) => {
return {
children: (
<>
<HoverRect.Tooltip.Title>{data[xIndex].bar}</HoverRect.Tooltip.Title>
<Flex justifyContent='space-between'>
<HoverRect.Tooltip.Dot mr={4}>Stack 1</HoverRect.Tooltip.Dot>
<Text bold>{data[xIndex].category1}</Text>
</Flex>
<Flex mt={2} justifyContent='space-between'>
<HoverRect.Tooltip.Dot mr={4}>Stack 2</HoverRect.Tooltip.Dot>
<Text bold>{data[xIndex].category2}</Text>
</Flex>
<Flex mt={2} justifyContent='space-between'>
<Box mr={4}>Total</Box>
<Text bold>{data[xIndex].category1 + data[xIndex].category2}</Text>
</Flex>
</>
),
};
}}
</HoverRect.Tooltip>
<StackBar x='bar'>
<StackBar.Bar y='category1' />
<StackBar.Bar y='category2' />
</StackBar>
</Plot>
);
};
const data = [...Array(5).keys()].map((d, i) => ({
bar: `Bar ${i + 1}`,
category1: Math.random() * 10,
category2: Math.random() * 10,
}));
export default Demo;
Legend and pattern fill
Note that for ChartLegend patterns
property works only with default shape={'Checkbox'}
.
tsx
import React from 'react';
import {
Plot,
StackBar,
YAxis,
XAxis,
ChartLegend,
makeDataHintsContainer,
} from '@semcore/ui/d3-chart';
import { scaleLinear, scaleBand } from 'd3-scale';
import { Flex } from '@semcore/ui/flex-box';
import Card from '@semcore/ui/card';
const dataHints = makeDataHintsContainer();
const Demo = () => {
const MARGIN = 30;
const width = 500;
const height = 300;
const xScale = scaleBand()
.range([MARGIN, width - MARGIN])
.domain(data.map((d) => d.bar))
.paddingInner(0.4)
.paddingOuter(0.2);
const yScale = scaleLinear()
.range([height - MARGIN, 0])
.domain([0, 15]);
const [legendItems, setLegendItems] = React.useState(
Object.keys(data[0])
.filter((name) => name !== 'bar')
.map((item, index) => {
return {
id: item,
label: `Category ${item}`,
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 (
<Card w={'550px'}>
<Card.Header pt={4}>
<Card.Title tag={'h4'} m={0} inline={true}>
Chart legend
</Card.Title>
</Card.Header>
<Card.Body tag={Flex} direction='column'>
<ChartLegend
items={legendItems}
onChangeVisibleItem={handleChangeVisible}
onMouseEnterItem={handleMouseEnter}
onMouseLeaveItem={handleMouseLeave}
dataHints={dataHints}
patterns
aria-label={'Stacked bar chart legend'}
/>
<Plot
data={data}
scale={[xScale, yScale]}
width={width}
height={height}
dataHints={dataHints}
patterns
>
<YAxis>
<YAxis.Ticks />
<YAxis.Grid />
</YAxis>
<XAxis>
<XAxis.Ticks />
</XAxis>
<StackBar x='bar'>
{legendItems.map((stack, index) => {
return (
stack.checked && (
<StackBar.Bar
y={stack.id}
key={stack.id}
color={stack.color}
transparent={highlightedLine !== -1 && highlightedLine !== index}
/>
)
);
})}
</StackBar>
</Plot>
</Card.Body>
</Card>
);
};
const data = [...Array(5).keys()].map((d, i) => ({
bar: `Bar ${i + 1}`,
1: Math.random() * 5,
2: Math.random() * 5,
3: Math.random() * 5,
}));
export default Demo;
Stacked and grouped chart
To group bars that are not stacked, refer to Grouped bars example.
tsx
import React from 'react';
import { Plot, YAxis, XAxis, StackGroupBar, HoverRect } from '@semcore/d3-chart';
import { scaleLinear, scaleBand } from 'd3-scale';
import { Box, Flex } from '@semcore/flex-box';
import { Text } from '@semcore/typography';
const Demo = () => {
const MARGIN = 40;
const width = 500;
const height = 220;
const xScale = scaleBand()
.range([MARGIN, width - MARGIN])
.domain(data.map((d) => d.category))
.paddingInner(0.4)
.paddingOuter(0.2);
const yScale = scaleLinear()
.range([height - MARGIN, MARGIN])
.domain([0, 20]);
return (
<Plot data={data} scale={[xScale, yScale]} width={width} height={height}>
<YAxis>
<YAxis.Ticks />
<YAxis.Grid />
</YAxis>
<XAxis>
<XAxis.Ticks />
</XAxis>
<HoverRect.Tooltip x='category' wMin={100}>
{({ xIndex }) => {
return {
children: (
<>
<HoverRect.Tooltip.Title>{data[xIndex].category}</HoverRect.Tooltip.Title>
<Text bold>Group 1</Text>
<Flex justifyContent='space-between'>
<HoverRect.Tooltip.Dot mr={4}>a</HoverRect.Tooltip.Dot>
<Text bold>{data[xIndex].a}</Text>
</Flex>
<Flex mt={2} justifyContent='space-between'>
<HoverRect.Tooltip.Dot mr={4}>b</HoverRect.Tooltip.Dot>
<Text bold>{data[xIndex].b}</Text>
</Flex>
<Text bold>Group 2</Text>
<Flex justifyContent='space-between'>
<HoverRect.Tooltip.Dot mr={4}>c</HoverRect.Tooltip.Dot>
<Text bold>{data[xIndex].c}</Text>
</Flex>
<Flex mt={2} justifyContent='space-between'>
<HoverRect.Tooltip.Dot mr={4}>d</HoverRect.Tooltip.Dot>
<Text bold>{data[xIndex].d}</Text>
</Flex>
<Flex mt={2} justifyContent='space-between'>
<Box mr={4}>Total group 1</Box>
<Text bold>{data[xIndex].a + data[xIndex].b}</Text>
</Flex>
<Flex mt={2} justifyContent='space-between'>
<Box mr={4}>Total group 2</Box>
<Text bold>{data[xIndex].c + data[xIndex].d}</Text>
</Flex>
</>
),
};
}}
</HoverRect.Tooltip>
<StackGroupBar x='category'>
<StackGroupBar.Bar group='x' y={'a'} />
<StackGroupBar.Bar group='x' y={'b'} />
<StackGroupBar.Bar group='z' y={'c'} />
<StackGroupBar.Bar group='z' y={'d'} />
</StackGroupBar>
</Plot>
);
};
const data = [...Array(5).keys()].map((d, i) => ({
category: `Category ${i}`,
a: Math.random() * 10,
b: Math.random() * 10,
c: Math.random() * 10,
d: Math.random() * 10,
}));
export default Demo;
Last updated: