Skip to content

Commit

Permalink
[charts] Allow onItemClick on the Legend component (mui#14231)
Browse files Browse the repository at this point in the history
Signed-off-by: Jose C Quintas Jr <[email protected]>
Co-authored-by: Alexandre Fauquette <[email protected]>
  • Loading branch information
JCQuintas and alexfauquette authored Sep 11, 2024
1 parent ff6866d commit b0703ab
Show file tree
Hide file tree
Showing 27 changed files with 520 additions and 40 deletions.
148 changes: 148 additions & 0 deletions docs/data/charts/legend/LegendClickNoSnap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import UndoOutlinedIcon from '@mui/icons-material/UndoOutlined';

import { ChartsLegend, PiecewiseColorLegend } from '@mui/x-charts/ChartsLegend';

import { HighlightedCode } from '@mui/docs/HighlightedCode';
import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer';

const pieSeries = [
{
type: 'pie',
id: 'series-1',
label: 'Series 1',
data: [
{ label: 'Pie A', id: 'P1-A', value: 400 },
{ label: 'Pie B', id: 'P2-B', value: 300 },
],
},
];

const barSeries = [
{
type: 'bar',
id: 'series-1',
label: 'Series 1',
data: [0, 1, 2],
},
{
type: 'bar',
id: 'series-2',
label: 'Series 2',
data: [0, 1, 2],
},
];

const lineSeries = [
{
type: 'line',
id: 'series-1',
label: 'Series 1',
data: [0, 1, 2],
},
{
type: 'line',
id: 'series-2',
label: 'Series 2',
data: [0, 1, 2],
},
];

export default function LegendClickNoSnap() {
const [itemData, setItemData] = React.useState();

return (
<Stack
direction={{ xs: 'column', md: 'row' }}
spacing={{ xs: 0, md: 4 }}
sx={{ width: '100%' }}
>
<Box sx={{ flexGrow: 1 }}>
<Typography>Chart Legend</Typography>
<ResponsiveChartContainer series={barSeries} width={400} height={60}>
<ChartsLegend
direction="row"
position={{
horizontal: 'left',
vertical: 'top',
}}
onItemClick={(event, context, index) => setItemData([context, index])}
/>
</ResponsiveChartContainer>
<Typography>Pie Chart Legend</Typography>
<ResponsiveChartContainer series={pieSeries} width={400} height={60}>
<ChartsLegend
direction="row"
position={{
horizontal: 'left',
vertical: 'top',
}}
onItemClick={(event, context, index) => setItemData([context, index])}
/>
</ResponsiveChartContainer>
<Typography>Pie Chart Legend</Typography>
<ResponsiveChartContainer
series={lineSeries}
width={400}
height={60}
xAxis={[
{
scaleType: 'linear',
data: [0, 1, 3],
disableLine: true,
colorMap: {
type: 'piecewise',
thresholds: [0, 2],
colors: ['blue', 'gray', 'red'],
},
},
]}
>
<PiecewiseColorLegend
direction="row"
position={{
horizontal: 'left',
vertical: 'top',
}}
axisDirection="x"
onItemClick={(event, context, index) => setItemData([context, index])}
/>
</ResponsiveChartContainer>
</Box>

<Stack direction="column" sx={{ width: { xs: '100%', md: '40%' } }}>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Typography>Click on the chart</Typography>
<IconButton
aria-label="reset"
size="small"
onClick={() => {
setItemData(null);
}}
>
<UndoOutlinedIcon fontSize="small" />
</IconButton>
</Box>
<HighlightedCode
code={`// Index from item click: ${itemData ? itemData[1] : ''}
// Context from item click
${itemData ? JSON.stringify(itemData[0], null, 2) : '// The data will appear here'}
`}
language="json"
copyButtonHidden
/>
</Stack>
</Stack>
);
}
18 changes: 18 additions & 0 deletions docs/data/charts/legend/legend.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,21 @@ labelFormatter = ({ min, max, formattedMin, formattedMax }) => string | null;
```

{{"demo": "PiecewiseInteractiveDemoNoSnap.js", "hideToolbar": true, "bg": "playground"}}

## Click event

You can pass an `onItemClick` function to the `ChartsLegend` or `PiecewiseColorLegend` components to handle click events.
They both provide the following signature.

```js
const clickHandler = (
event, // The click event.
context, // An object that identifies the clicked item.
index, // The index of the clicked item.
) => {};
```

The `context` object contains different properties depending on the legend type.
Click the legend items to see their content.

{{"demo": "LegendClickNoSnap.js"}}
13 changes: 13 additions & 0 deletions docs/pages/x/api/charts/charts-legend.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"itemMarkWidth": { "type": { "name": "number" }, "default": "20" },
"labelStyle": { "type": { "name": "object" }, "default": "theme.typography.subtitle1" },
"markGap": { "type": { "name": "number" }, "default": "5" },
"onItemClick": {
"type": { "name": "func" },
"signature": {
"type": "function(event: React.MouseEvent<SVGRectElement, MouseEvent>, legendItem: SeriesLegendItemContext, index: number) => void",
"describedArgs": ["event", "legendItem", "index"]
}
},
"padding": {
"type": {
"name": "union",
Expand Down Expand Up @@ -49,6 +56,12 @@
"description": "Styles applied to the legend with column layout.",
"isGlobal": false
},
{
"key": "itemBackground",
"className": "MuiChartsLegend-itemBackground",
"description": "Styles applied to the item background.",
"isGlobal": false
},
{
"key": "label",
"className": "MuiChartsLegend-label",
Expand Down
13 changes: 13 additions & 0 deletions docs/pages/x/api/charts/default-charts-legend.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
"itemMarkWidth": { "type": { "name": "number" }, "default": "20" },
"labelStyle": { "type": { "name": "object" }, "default": "theme.typography.subtitle1" },
"markGap": { "type": { "name": "number" }, "default": "5" },
"onItemClick": {
"type": { "name": "func" },
"signature": {
"type": "function(event: React.MouseEvent<SVGRectElement, MouseEvent>, legendItem: SeriesLegendItemContext, index: number) => void",
"describedArgs": ["event", "legendItem", "index"]
}
},
"padding": {
"type": {
"name": "union",
Expand All @@ -39,6 +46,12 @@
"description": "Styles applied to the legend with column layout.",
"isGlobal": false
},
{
"key": "itemBackground",
"className": "MuiDefaultChartsLegend-itemBackground",
"description": "Styles applied to the item background.",
"isGlobal": false
},
{
"key": "label",
"className": "MuiDefaultChartsLegend-label",
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/x/api/charts/pie-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"legend": {
"type": {
"name": "shape",
"description": "{ classes?: object, direction?: 'column'<br>&#124;&nbsp;'row', hidden?: bool, itemGap?: number, itemMarkHeight?: number, itemMarkWidth?: number, labelStyle?: object, markGap?: number, padding?: number<br>&#124;&nbsp;{ bottom?: number, left?: number, right?: number, top?: number }, position?: { horizontal: 'left'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'right', vertical: 'bottom'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'top' }, slotProps?: object, slots?: object }"
"description": "{ classes?: object, direction?: 'column'<br>&#124;&nbsp;'row', hidden?: bool, itemGap?: number, itemMarkHeight?: number, itemMarkWidth?: number, labelStyle?: object, markGap?: number, onItemClick?: func, padding?: number<br>&#124;&nbsp;{ bottom?: number, left?: number, right?: number, top?: number }, position?: { horizontal: 'left'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'right', vertical: 'bottom'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'top' }, slotProps?: object, slots?: object }"
},
"default": "{ direction: 'column', position: { vertical: 'middle', horizontal: 'right' } }",
"deprecated": true,
Expand Down
13 changes: 13 additions & 0 deletions docs/pages/x/api/charts/piecewise-color-legend.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
},
"labelStyle": { "type": { "name": "object" }, "default": "theme.typography.subtitle1" },
"markGap": { "type": { "name": "number" }, "default": "5" },
"onItemClick": {
"type": { "name": "func" },
"signature": {
"type": "function(event: React.MouseEvent<SVGRectElement, MouseEvent>, legendItem: PiecewiseColorLegendItemContext, index: number) => void",
"describedArgs": ["event", "legendItem", "index"]
}
},
"padding": {
"type": {
"name": "union",
Expand All @@ -56,6 +63,12 @@
"description": "Styles applied to the legend with column layout.",
"isGlobal": false
},
{
"key": "itemBackground",
"className": "MuiPiecewiseColorLegend-itemBackground",
"description": "Styles applied to the item background.",
"isGlobal": false
},
{
"key": "label",
"className": "MuiPiecewiseColorLegend-label",
Expand Down
12 changes: 12 additions & 0 deletions docs/translations/api-docs/charts/charts-legend/charts-legend.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
"itemMarkWidth": { "description": "Width of the item mark (in px)." },
"labelStyle": { "description": "Style applied to legend labels." },
"markGap": { "description": "Space between the mark and the label (in px)." },
"onItemClick": {
"description": "Callback fired when a legend item is clicked.",
"typeDescriptions": {
"event": "The click event.",
"legendItem": "The legend item data.",
"index": "The index of the clicked legend item."
}
},
"padding": {
"description": "Legend padding (in px). Can either be a single number, or an object with top, left, bottom, right properties."
},
Expand All @@ -23,6 +31,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the legend with column layout"
},
"itemBackground": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the item background"
},
"label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the series label" },
"mark": { "description": "Styles applied to {{nodeName}}.", "nodeName": "series mark element" },
"root": { "description": "Styles applied to the root element." },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
"itemMarkWidth": { "description": "Width of the item mark (in px)." },
"labelStyle": { "description": "Style applied to legend labels." },
"markGap": { "description": "Space between the mark and the label (in px)." },
"onItemClick": {
"description": "Callback fired when a legend item is clicked.",
"typeDescriptions": {
"event": "The click event.",
"legendItem": "The legend item data.",
"index": "The index of the clicked legend item."
}
},
"padding": {
"description": "Legend padding (in px). Can either be a single number, or an object with top, left, bottom, right properties."
},
Expand All @@ -21,6 +29,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the legend with column layout"
},
"itemBackground": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the item background"
},
"label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the series label" },
"mark": { "description": "Styles applied to {{nodeName}}.", "nodeName": "series mark element" },
"root": { "description": "Styles applied to the root element." },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
},
"labelStyle": { "description": "Style applied to legend labels." },
"markGap": { "description": "Space between the mark and the label (in px)." },
"onItemClick": {
"description": "Callback fired when a legend item is clicked.",
"typeDescriptions": {
"event": "The click event.",
"legendItem": "The legend item data.",
"index": "The index of the clicked legend item."
}
},
"padding": {
"description": "Legend padding (in px). Can either be a single number, or an object with top, left, bottom, right properties."
},
Expand All @@ -39,6 +47,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the legend with column layout"
},
"itemBackground": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the item background"
},
"label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the series label" },
"mark": { "description": "Styles applied to {{nodeName}}.", "nodeName": "series mark element" },
"root": { "description": "Styles applied to the root element." },
Expand Down
1 change: 1 addition & 0 deletions packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ BarChartPro.propTypes = {
itemMarkWidth: PropTypes.number,
labelStyle: PropTypes.object,
markGap: PropTypes.number,
onItemClick: PropTypes.func,
padding: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
Expand Down
1 change: 1 addition & 0 deletions packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ LineChartPro.propTypes = {
itemMarkWidth: PropTypes.number,
labelStyle: PropTypes.object,
markGap: PropTypes.number,
onItemClick: PropTypes.func,
padding: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ ScatterChartPro.propTypes = {
itemMarkWidth: PropTypes.number,
labelStyle: PropTypes.object,
markGap: PropTypes.number,
onItemClick: PropTypes.func,
padding: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
Expand Down
1 change: 1 addition & 0 deletions packages/x-charts/src/BarChart/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ BarChart.propTypes = {
itemMarkWidth: PropTypes.number,
labelStyle: PropTypes.object,
markGap: PropTypes.number,
onItemClick: PropTypes.func,
padding: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
Expand Down
4 changes: 3 additions & 1 deletion packages/x-charts/src/BarChart/legend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ const legendGetter: LegendGetter<'bar'> = (params) => {
}

acc.push({
id: seriesId,
seriesId,
color: series[seriesId].color,
label: formattedLabel,
id: seriesId,
});

return acc;
}, [] as LegendItemParams[]);
};
Expand Down
Loading

0 comments on commit b0703ab

Please sign in to comment.