mui-x
mui-x copied to clipboard
[charts] Allows to customize elements color based on its value
The problem in depth 🔍
I am trying to make the chart below with X Charts:
The bars in the chart should be color coordinated based on wether or not they exceed the horizontal position of ChartsReferenceLine
, with green being above and red being below.
I was able to build the structure pretty easily using ResponsiveChartContainer
. How should I go about calculating the color if I do not have access to the position of ChartsReferenceLine
?
The value of the bar is not available in the prop passed thru to the slots
props on the bar, so I would need to compare the position of the reference line to that of the height of the bar.
I tried using a series for this but because series groups its not practical. Only the first color of colors is on ResponsiveChartContainer
is respected for BarCharts
Attempts
Basic Structure https://codesandbox.io/p/sandbox/mui-bar-chart-with-rounded-corders-right-forked-s5yd28?file=%2FDemo.js
With Slot (and BarElement export workaround)
https://codesandbox.io/p/sandbox/mui-bar-chart-with-rounded-corders-right-forked-q73qzp?file=%2FDemo.js%3A22%2C30
Your environment 🌎
System:
OS: macOS 14.1.1
Binaries:
Node: 20.5.1 - /opt/homebrew/bin/node
Yarn: 1.22.21 - ~/Desktop/Projects/web/node_modules/.bin/yarn
npm: 9.8.0 - /opt/homebrew/bin/npm
Browsers:
Chrome: 119.0.6045.199
Edge: Not Found
Safari: 17.1
npmPackages:
@emotion/react: ^11.10.6 => 11.10.6
@emotion/styled: ^11.10.6 => 11.10.6
@mui/base: 5.0.0-alpha.126
@mui/core-downloads-tracker: 5.12.1
@mui/lab: ^5.0.0-alpha.127 => 5.0.0-alpha.127
@mui/material: ^5.12.1 => 5.12.1
@mui/private-theming: 5.12.0
@mui/styled-engine: 5.12.0
@mui/system: 5.12.1
@mui/types: 7.2.4
@mui/utils: 5.12.0
@mui/x-charts: ^v7.0.0-alpha.2 => 7.0.0-alpha.2
@mui/x-data-grid: 6.18.2
@mui/x-data-grid-pro: ^6.18.2 => 6.18.2
@mui/x-license-pro: 6.10.2
@types/react: ^18.0.26 => 18.0.26
react: ^18.2.0 => 18.2.0
react-dom: ^18.2.0 => 18.2.0
typescript: ^5.1.6 => 5.1.6
Don't forget to mention which browser you used.
Output from `npx @mui/envinfo` goes here.
Search keywords: bar chart color customization Order ID: 58383
I solved this using the useYScale
hook. Not sure if it's the best solution. Feedback appreciated.
A side affect is that the hover element has the wrong bar color associated
Once the BarElement export fix is released the custom rect can be replaced. https://github.com/mui/mui-x/issues/11234
Updated Code Pen: https://codesandbox.io/p/sandbox/mui-bar-chart-with-rounded-corders-right-forked-trvfp4?file=%2FDemo.js
export const BarChart = () => {
const SlotBarElement = (props) =>{
const yAxisScale = useYScale('left_axis_id')
const yAxisValue = yAxisScale.invert(props.style.y.animation.to)
const isBelowBar = yAxisValue < 8.5
const color = isBelowBar ? '#ff0000' : '#00ff00'
// work around export of BarElement
return <rect
fill={color}
height={props.style.height.animation.to}
width={props.style.width.animation.to}
x={props.style.x.animation.to}
y={props.style.y.animation.to}
/>
}
return (
<ResponsiveChartContainer
height={350}
series={[
{ type: 'bar', data: [11, 10, 1, 9], yAxisKey: 'left_axis_id'},
]}
xAxis={[{ scaleType: 'band', data: ['11/15', '11/16', '11/17', '11/18'] }]}
yAxis={[{ id: 'left_axis_id'}]}
>
<BarPlot slots={{bar: SlotBarElement}}/>
<ChartsXAxis />
<ChartsYAxis axisId="left_axis_id" position="left"/>
<ChartsReferenceLine label={'8.5'} labelAlign="end" y={8.5}/>
<ChartsTooltip/>
</ResponsiveChartContainer>
)
}
For some reason the colors do not change automatically. Sometimes I have to mouse over the chart before they swap. Any thoughts why?
About why you need to pass the mouse over, the props.style.y.animation.to
is NaN
at first render so your component does not render.
About how to get the information, using the useScale
sounds to be ok. Maybe better would be that we pass this information to the component to simplify this.
For now, the ownerState
get id
and dataIndex
which are the series' id and the index of the data rendered. So if you data is not too complicated, the best might be to rely on that information.
https://codesandbox.io/p/sandbox/mui-bar-chart-with-rounded-corders-right-forked-h5jvsd?file=%2FDemo.js%3A17%2C52-17%2C54
Thank you @alexfauquette this resolved my problem!!
I'm just keeping this issue open to track the progress on simplifying conditional rendering of bar elements
I'm just keeping this issue open to track the progress on simplifying conditional rendering of bar elements
Makes sense..
I noticed the tooltip popup shows the default bar color and not the custom color of the bar. It's not a big deal for me, but figured I'd call it out since you're using this for tracking.
I noticed the tooltip popup shows the default bar color and not the custom color of the bar.
Interesting. That's a sign the customization should probably not be into the component customization (bar, line, ...) but at a higher level to have coherence between chart element, the legend and the tooltip
For grouping same topic issues, here is the suggestion of similar behavior for the grid
Have you considered the option/need to have conditional grid color styling? Wouldn't it be problem adding it in given the current API?
https://github.com/mui/mui-x/pull/11034#pullrequestreview-1879779670
@RayHughes You might be interested by #12490 and the preview demonstration visible in https://deploy-preview-12490--material-ui-x.netlify.app/x/react-charts/bars/#color-scale
The over all idea is to map values to color in the different axes. Any feedback about the developer experience is welcome :)
:warning: This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue. Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.
@RayHughes: How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey.