mui-x icon indicating copy to clipboard operation
mui-x copied to clipboard

[charts] Allows to customize elements color based on its value

Open RayHughes opened this issue 1 year ago • 8 comments

The problem in depth 🔍

I am trying to make the chart below with X Charts:

Screenshot 2023-12-02 at 1 10 12 AM

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

RayHughes avatar Dec 02 '23 22:12 RayHughes

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>
  )
}

RayHughes avatar Dec 02 '23 23:12 RayHughes

For some reason the colors do not change automatically. Sometimes I have to mouse over the chart before they swap. Any thoughts why?

RayHughes avatar Dec 05 '23 10:12 RayHughes

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

alexfauquette avatar Dec 05 '23 16:12 alexfauquette

Thank you @alexfauquette this resolved my problem!!

RayHughes avatar Dec 05 '23 17:12 RayHughes

I'm just keeping this issue open to track the progress on simplifying conditional rendering of bar elements

alexfauquette avatar Dec 05 '23 17:12 alexfauquette

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.

RayHughes avatar Dec 05 '23 18:12 RayHughes

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

alexfauquette avatar Dec 07 '23 08:12 alexfauquette

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

alexfauquette avatar Feb 14 '24 13:02 alexfauquette

@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 :)

alexfauquette avatar Apr 03 '24 08:04 alexfauquette

: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.

github-actions[bot] avatar Apr 15 '24 08:04 github-actions[bot]