Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[charts] Add a "seriesIndex" Property to the AxisContent Custom Charts Tooltip in an AreaPlot #13944

Open
halfacandan opened this issue Jul 22, 2024 · 7 comments
Labels
component: charts This is the name of the generic UI component, not the React module! enhancement This is not a bug, nor a new feature

Comments

@halfacandan
Copy link

halfacandan commented Jul 22, 2024

Summary

Feature: Add a "seriesIndex" Property to the AxisContent Custom Charts Tooltip

Scenario: User hovers their mouse over a data series in a stacked area chart
Given a ResponsiveChartContainer which contains an AreaPlot with a ChartsTooltip
And an array of data series called "set1" & "set2" which are stacked in a stack called "total"
When the properties on the ChartsTooltip are set to trigger = "axis" and slots = {{ axisContent: formatTooltip }}
And the user hovers their mouse over "set2" in the rendered chart
Then the formatTooltip function should have access to dataIndex i.e. the x axis's value (which it currently does)
And the formatTooltip function should have access to another property which identifies the data series that has focus e.g. "set2"

Examples

Actual Behaviour

Only the dataIndex is available, no reference to the data series which has focus is available:
https://stackblitz.com/edit/react-hydnyg-hzvrzt?file=Demo.js

If I stringify the props passed to the "formatTooltip" function (shown below), I have access to the "dataIndex" and "axisValue" properties to identify the x-axis but nothing refers to the data series which is being hovered over. I added some "highlightScope" properties to the data series to show that a reference to the hovered series is stored somewhere.

{
    "axisData": {
        "x": {
            "index": 2,
            "value": "C"
        },
        "y": {
            "value": 0.30000000000000027
        }
    },
    "series": [
        {
            "id": "auto-generated-id-0",
            "color": "#02B2AF",
            "label": "set1",
            "type": "line",
            "area": true,
            "stack": "total",
            "data": [
                1,
                2,
                3,
                2,
                1
            ],
            "highlightScope": {
                "highlighted": "item",
                "faded": "global"
            },
            "stackedData": [
                [
                    0,
                    1
                ],
                [
                    0,
                    2
                ],
                [
                    0,
                    3
                ],
                [
                    0,
                    2
                ],
                [
                    0,
                    1
                ]
            ]
        },
        {
            "id": "auto-generated-id-1",
            "color": "#2E96FF",
            "label": "set2",
            "type": "line",
            "area": true,
            "stack": "total",
            "data": [
                1,
                2,
                3,
                2,
                1
            ],
            "highlightScope": {
                "highlighted": "item",
                "faded": "global"
            },
            "stackedData": [
                [
                    1,
                    2
                ],
                [
                    2,
                    4
                ],
                [
                    3,
                    6
                ],
                [
                    2,
                    4
                ],
                [
                    1,
                    2
                ]
            ]
        }
    ],
    "axis": {
        "categoryGapRatio": 0.2,
        "barGapRatio": 0.1,
        "id": "x-axis-id",
        "data": [
            "A",
            "B",
            "C",
            "D",
            "E"
        ],
        "scaleType": "band",
        "tickNumber": 5
    },
    "dataIndex": 2,
    "axisValue": "C",
    "sx": {
        "mx": 2
    },
    "classes": {
        "root": "MuiChartsTooltip-root",
        "table": "MuiChartsTooltip-table",
        "row": "MuiChartsTooltip-row",
        "cell": "MuiChartsTooltip-cell",
        "mark": "MuiChartsTooltip-mark",
        "markCell": "MuiChartsTooltip-markCell",
        "labelCell": "MuiChartsTooltip-labelCell",
        "valueCell": "MuiChartsTooltip-valueCell"
    },
    "ownerState": {}
}

Example code

import * as React from 'react';
import Box from '@mui/material/Box';
import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer';
import { AreaPlot, MarkPlot } from '@mui/x-charts/LineChart';
import { ChartsTooltip, ChartsXAxis } from '@mui/x-charts';

export default function SwitchSeriesType() {
  const formatTooltip = (props) => {
    console.log(props.dataIndex); // This is the index of the x-axis
    console.log(JSON.stringify(props)); // THe props do not contain any reference to which series is being hovered over
  };

  return (
    <Box sx={{ width: '100%' }}>
      <div>
        <ResponsiveChartContainer
          series={[
            {
              label: 'set1',
              type: 'line',
              area: true,
              stack: 'total',
              data: [1, 2, 3, 2, 1],
              highlightScope: {
                  highlighted: 'item',
                  faded: 'global'
              }
            },
            {
              label: 'set2',
              type: 'line',
              area: true,
              stack: 'total',
              data: [1, 2, 3, 2, 1],
              highlightScope: {
                  highlighted: 'item',
                  faded: 'global'
              }
            },
          ]}
          xAxis={[
            {
              data: ['A', 'B', 'C', 'D', 'E'],
              scaleType: 'band',
              id: 'x-axis-id',
            },
          ]}
          height={200}
        >
          <AreaPlot />
          <ChartsXAxis label="X axis" position="bottom" axisId="x-axis-id" />
          <MarkPlot />
          <ChartsTooltip
            trigger="axis"
            slots={{ axisContent: formatTooltip }}
          />
        </ResponsiveChartContainer>
      </div>
    </Box>
  );
}

Motivation

In the Axis tooltip, I would like to have an option to visually highlight which of the series is currently hovered over by the user's mouse e.g. with emboldened text. This gives users a simple visual prompt which is not dependent on colour or pattern.

Search keywords: ResponsiveChartContainer AreaPlot ChartsTooltip

@halfacandan halfacandan added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Jul 22, 2024
@halfacandan halfacandan changed the title Add a "seriesIndex" Property to the AxisContent Custom Charts Tooltip [charts] Add a "seriesIndex" Property to the AxisContent Custom Charts Tooltip in an AreaPlot Jul 22, 2024
@alexfauquette
Copy link
Member

For now, you can either use

  • trigger="axis" then the tooltip is here to display information about the axis values
  • trigger="item" then the tooltip is here to display information about one item

You could consider using useHighlighted() to get information about the current series with highlight. Those hooks are not yet documented

@halfacandan
Copy link
Author

Thanks @alexfauquette. I have tried your second suggestion already and documented what I believe is a bugged behaviour: #13945

Do you have any example code for how to use the "useHighlighted()" function as that sounds ideal?

@alexfauquette
Copy link
Member

Do you have any example code for how to use the "useHighlighted()" function as that sounds ideal?

Not yet. We did not decided a strategy about how to document such hooks

@alexfauquette alexfauquette added enhancement This is not a bug, nor a new feature component: charts This is the name of the generic UI component, not the React module! labels Jul 22, 2024
@halfacandan
Copy link
Author

For anyone else interested in using the "useHighlighted()" function, here's some hacky code that you can use to capture the value

const [highlightedSeriesId, setHighlightedSeriesId] = React.useState<string|null>();
function UseHighlighted(): JSX.Element {
      const { highlightedItem } = useHighlighted();
      setHighlightedSeriesId(highlightedItem?.seriesId?.toString() ?? null);
      return <></>;
  }

They you just nest a element inside your ResponsiveChartContainer e.g.

<ResponsiveChartContainer>
    <UseHighlighted />
    <AreaPlot />
    <ChartsTooltip trigger="axis" slots={{ axisContent: formatTooltip }} />
</ResponsiveChartContainer>

You can then use the captured highlightedSeriesId value to identify a specific data series in your custom tooltip function.

@alexfauquette
Copy link
Member

If your custom component is inside the <ResponsiveChartContainer /> you don't need to save it in a dedicated state (it's already in a context)

The slots are expecting components to render. To avoid losing internal state, those components should be declare outside of the function rendering the charts

+ const FormatTooltip = (props) => {
+   console.log(props.dataIndex); // This is the index of the x-axis
+   console.log(JSON.stringify(props)); // THe props do not contain any reference to which series is being hovered over   };
+ }

export default function SwitchSeriesType() {
-   const formatTooltip = (props) => {
-     console.log(props.dataIndex); // This is the index of the x-axis
-     console.log(JSON.stringify(props)); // THe props do not contain any reference to which series is being hovered over
-   };

  return (
    <Box sx={{ width: '100%' }}>
      <div>
        <ResponsiveChartContainer

Here is a quick example, highlighting the current series, by increasing a div size

https://stackblitz.com/edit/react-hydnyg-zja5ef?file=Demo.js,package.json

@alexfauquette alexfauquette removed the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Jul 23, 2024
@halfacandan
Copy link
Author

Thank you for the example code @alexfauquette :D

@alexfauquette
Copy link
Member

The current "solution" of this issue will probably be easier to create and document after #13819

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: charts This is the name of the generic UI component, not the React module! enhancement This is not a bug, nor a new feature
Projects
None yet
Development

No branches or pull requests

2 participants