I am trying to make a stacked bar chart like this in mui-x-charts:
The data source has percentages, and i want the different strengths to be stacked as seen below, where the different colours relate to different strengths of the drug.
The only way i could see to stack the values was to subtract the values from the preceding value. The issue is when i do it this way, the hover-tooltip values are not correct:
i could fall-back to a non-stacked chart easily enough
Q: Any suggestions on how to show the tooltips with correct values and keep the stacked chart as intended?
styles.module.scss
.root {
margin-top: 20px;
margin-bottom:20px;
:global {
.MuiChartsAxis-directionY .MuiChartsAxis-label {
transform: translateX(-5px) !important;
}
}
}
DrugEffectiveness.tsx
/**
* data is from here: https://www.bmj.com/content/326/7404/1423.long
*/
import React, { type ReactNode } from 'react'
import { BarChart } from '@mui/x-charts/BarChart'
import Typography from '@mui/material/Typography'
import styles from './styles.module.scss'
interface reductionType {
strength: string
reduction: number | null
}
interface drugType {
name: string
reduction: reductionType[]
}
interface seriesType {
data: Array<number | null>
label: string
stack: string
}
const atorvastatin: drugType = {
name: 'atorvastatin',
reduction: [
{ strength: '5mg', reduction: 31 },
{ strength: '10mg', reduction: 37 },
{ strength: '20mg', reduction: 43 },
{ strength: '40mg', reduction: 49 },
{ strength: '80mg', reduction: 55 }
]
}
const fluvastatin: drugType = {
name: 'fluvastatin',
reduction: [
{ strength: '5mg', reduction: 10 },
{ strength: '10mg', reduction: 15 },
{ strength: '20mg', reduction: 21 },
{ strength: '40mg', reduction: 27 },
{ strength: '80mg', reduction: 33 }
]
}
const lovastatin: drugType = {
name: 'lovastatin',
reduction: [
{ strength: '5mg', reduction: null },
{ strength: '10mg', reduction: 21 },
{ strength: '20mg', reduction: 29 },
{ strength: '40mg', reduction: 37 },
{ strength: '80mg', reduction: 45 }
]
}
const pravastatin: drugType = {
name: 'pravastatin',
reduction: [
{ strength: '5mg', reduction: 15 },
{ strength: '10mg', reduction: 20 },
{ strength: '20mg', reduction: 24 },
{ strength: '40mg', reduction: 29 },
{ strength: '80mg', reduction: 33 }
]
}
const rosuvastatin: drugType = {
name: 'rosuvastatin',
reduction: [
{ strength: '5mg', reduction: 38 },
{ strength: '10mg', reduction: 43 },
{ strength: '20mg', reduction: 48 },
{ strength: '40mg', reduction: 53 },
{ strength: '80mg', reduction: 58 }
]
}
const simvastatin: drugType = {
name: 'simvastatin',
reduction: [
{ strength: '5mg', reduction: 23 },
{ strength: '10mg', reduction: 27 },
{ strength: '20mg', reduction: 32 },
{ strength: '40mg', reduction: 37 },
{ strength: '80mg', reduction: 42 }
]
}
const drugs: drugType[] = [atorvastatin, fluvastatin, lovastatin, pravastatin, rosuvastatin, simvastatin]
const strengths: string[] = simvastatin.reduction.map(s => s.strength)
const series5mg: seriesType = { data: [], stack: 'stack', label: '5mg' }
const series10mg: seriesType = { data: [], stack: 'stack', label: '10mg' }
const series20mg: seriesType = { data: [], stack: 'stack', label: '20mg' }
const series40mg: seriesType = { data: [], stack: 'stack', label: '40mg' }
const series80mg: seriesType = { data: [], stack: 'stack', label: '80mg' }
const getReduction = ({ drug, strength }: { drug: drugType, strength: string }): number | null => {
const filteredDrug = drug.reduction.filter((drugItem: reductionType) => drugItem.strength === strength)
return filteredDrug.length > 0
? filteredDrug[0].reduction
: null
}
strengths.forEach((strength: string) => {
drugs.forEach((drug: drugType) => {
switch (strength) {
case '5mg' :
series5mg.data.push(getReduction({ drug, strength }))
break
case '10mg':
series10mg.data.push(Number(getReduction({ drug, strength })) - Number(getReduction({
drug,
strength: '5mg'
})))
break
case '20mg':
series20mg.data.push(Number(getReduction({ drug, strength })) - Number(getReduction({
drug,
strength: '10mg'
})))
break
case '40mg':
series40mg.data.push(Number(getReduction({ drug, strength })) - Number(getReduction({
drug,
strength: '20mg'
})))
break
case '80mg':
series80mg.data.push(Number(getReduction({ drug, strength })) - Number(getReduction({
drug,
strength: '40mg'
})))
break
}
})
})
const valueFormatter = (val: number | null): string => {
return val !== null ? val + '%' : ''
}
const DrugEffectiveness = (): ReactNode => (
<div className={`${styles.root} DrugEffectiveness`}>
<Typography variant="h2">Percentage reduction in LDL cholesterol</Typography>
<BarChart
title="tester"
width={800}
slotProps={{ legend: { hidden: true } }}
height={400}
xAxis={[{
scaleType: 'band',
data: drugs.map((d: drugType) => d.name)
}]}
series={[
{ ...series5mg, valueFormatter },
{ ...series10mg, valueFormatter },
{ ...series20mg, valueFormatter },
{ ...series40mg, valueFormatter },
{ ...series80mg, valueFormatter }
]}
yAxis={[{
label: 'Percentage reduction',
valueFormatter: (v) => v + '%',
labelStyle: {
fontSize: 14
},
tickLabelStyle: {
angle: -45,
textAnchor: 'end',
fontSize: 12
}
}]}
/>
</div>
)
export default DrugEffectiveness

