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] Allow setting a custom marker in legends and tooltips #16654

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
38 changes: 38 additions & 0 deletions docs/data/charts/legend/LegendCustomLabelMark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { BarChart } from '@mui/x-charts/BarChart';
import * as React from 'react';

const seriesConfig = [
{ id: 0, data: [10], label: 'Series A' },
{ id: 1, data: [15], label: 'Series B' },
{ id: 2, data: [20], label: 'Series C' },
{ id: 3, data: [10], label: 'Series D' },
];
Comment on lines +5 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{ id: 0, data: [10], label: 'Series A' },
{ id: 1, data: [15], label: 'Series B' },
{ id: 2, data: [20], label: 'Series C' },
{ id: 3, data: [10], label: 'Series D' },
];
{ id: 0, data: [10, 15], label: 'Series A' },
{ id: 1, data: [15, 20], label: 'Series B' },
{ id: 2, data: [20, 25], label: 'Series C' },
{ id: 3, data: [10, 15], label: 'Series D' },
];


const cross =
'M-5.35,-1.783L-1.783,-1.783L-1.783,-5.35L1.783,-5.35L1.783,-1.783L5.35,-1.783L5.35,1.783L1.783,1.783L1.783,5.35L-1.783,5.35L-1.783,1.783L-5.35,1.783Z';
const diamond = 'M0,-7.423L4.285,0L0,7.423L-4.285,0Z';
const star =
'M0,-7.528L1.69,-2.326L7.16,-2.326L2.735,0.889L4.425,6.09L0,2.875L-4.425,6.09L-2.735,0.889L-7.16,-2.326L-1.69,-2.326Z';
const wye =
'M2.152,1.243L2.152,5.547L-2.152,5.547L-2.152,1.243L-5.88,-0.91L-3.728,-4.638L0,-2.485L3.728,-4.638L5.88,-0.91Z';
const symbols = [cross, diamond, star, wye];

function CustomLabelMark({ className, seriesId, color }) {
const symbol = symbols[seriesId];
return (
<svg className={className} viewBox="-8 -8 16 16" width={14} height={14}>
<path d={symbol} fill={color} />
</svg>
);
}

export default function LegendCustomLabelMark() {
return (
<BarChart
series={seriesConfig}
xAxis={[{ scaleType: 'band', data: ['A'] }]}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not directly related to the PR, but the axis "A" and series "Series A" bugs me (or to be precise let me think their is a bug in the tooltip

image

Suggested change
xAxis={[{ scaleType: 'band', data: ['A'] }]}
xAxis={[{ scaleType: 'band', data: ['cat. 1', 'cat. 2'] }]}

height={200}
slots={{ labelMark: CustomLabelMark }}
/>
);
}
39 changes: 39 additions & 0 deletions docs/data/charts/legend/LegendCustomLabelMark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { BarChart } from '@mui/x-charts/BarChart';
import * as React from 'react';
import { ChartsLabelMarkProps } from '@mui/x-charts/ChartsLabel';

const seriesConfig = [
{ id: 0, data: [10], label: 'Series A' },
{ id: 1, data: [15], label: 'Series B' },
{ id: 2, data: [20], label: 'Series C' },
{ id: 3, data: [10], label: 'Series D' },
];

const cross =
'M-5.35,-1.783L-1.783,-1.783L-1.783,-5.35L1.783,-5.35L1.783,-1.783L5.35,-1.783L5.35,1.783L1.783,1.783L1.783,5.35L-1.783,5.35L-1.783,1.783L-5.35,1.783Z';
const diamond = 'M0,-7.423L4.285,0L0,7.423L-4.285,0Z';
const star =
'M0,-7.528L1.69,-2.326L7.16,-2.326L2.735,0.889L4.425,6.09L0,2.875L-4.425,6.09L-2.735,0.889L-7.16,-2.326L-1.69,-2.326Z';
const wye =
'M2.152,1.243L2.152,5.547L-2.152,5.547L-2.152,1.243L-5.88,-0.91L-3.728,-4.638L0,-2.485L3.728,-4.638L5.88,-0.91Z';
const symbols = [cross, diamond, star, wye];

function CustomLabelMark({ className, seriesId, color }: ChartsLabelMarkProps) {
const symbol = symbols[seriesId as number];
return (
<svg className={className} viewBox="-8 -8 16 16" width={14} height={14}>
<path d={symbol} fill={color} />
</svg>
);
}

export default function LegendCustomLabelMark() {
return (
<BarChart
series={seriesConfig}
xAxis={[{ scaleType: 'band', data: ['A'] }]}
height={200}
slots={{ labelMark: CustomLabelMark }}
/>
);
}
6 changes: 6 additions & 0 deletions docs/data/charts/legend/LegendCustomLabelMark.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<BarChart
series={seriesConfig}
xAxis={[{ scaleType: 'band', data: ['A'] }]}
height={200}
slots={{ labelMark: CustomLabelMark }}
/>
10 changes: 10 additions & 0 deletions docs/data/charts/legend/legend.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ For the `pie` series, the `labelMarkType` property is available for each of the

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

For more advanced use cases, you can also provide a component to the `labelMark` slot to fully customize the mark.

{{"demo": "LegendCustomLabelMark.js" }}

Comment on lines +74 to +77
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also adding a h4 title "Built-in shapes" in the previous section.

It's mostly to simplify to visual processing of information. Titles are useful to split text and demos in coherent bloks.

For example here without reading I can't guess to which demos the paragraph refers to

image

Suggested change
For more advanced use cases, you can also provide a component to the `labelMark` slot to fully customize the mark.
{{"demo": "LegendCustomLabelMark.js" }}
#### Custom shapes
For more advanced use cases, you can also provide a component to the `labelMark` slot to fully customize the mark.
{{"demo": "LegendCustomLabelMark.js" }}

Passing a component to `labelMark` or using `labelMarkType` affects not only the legend but other places where the label mark is shown, such as tooltips.

When a `labelMark` slot is provided, the `labelMarkType` property is ignored.

Customizing the mark shape of a pie chart depending on the series is slightly different. You can find how to do it in [this example](/x/react-charts/pie-demo/#pie-chart-with-custom-legend-and-tooltip).

### Scrollable legend

The legend can be made scrollable by setting the `overflowY` for vertical legends or `overflowX` for horizontal legends.
Expand Down
39 changes: 39 additions & 0 deletions docs/data/charts/pie-demo/PieChartWithCustomLegendTooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react';
import { PieChart } from '@mui/x-charts/PieChart';

export default function PieChartWithCustomLegendTooltip() {
return (
<PieChart
series={[
{
data: [
{ id: 'cross', value: 10, label: 'Cross' },
{ id: 'diamond', value: 15, label: 'Diamond' },
{ id: 'star', value: 20, label: 'Star' },
],
},
]}
width={200}
height={200}
slots={{ labelMark: CustomMark }}
/>
);
}

const cross =
'M-5.35,-1.783L-1.783,-1.783L-1.783,-5.35L1.783,-5.35L1.783,-1.783L5.35,-1.783L5.35,1.783L1.783,1.783L1.783,5.35L-1.783,5.35L-1.783,1.783L-5.35,1.783Z';
const diamond = 'M0,-7.423L4.285,0L0,7.423L-4.285,0Z';
const star =
'M0,-7.528L1.69,-2.326L7.16,-2.326L2.735,0.889L4.425,6.09L0,2.875L-4.425,6.09L-2.735,0.889L-7.16,-2.326L-1.69,-2.326Z';

const symbols = [cross, diamond, star];

function CustomMark({ dataIndex, color, className }) {
const data = symbols[dataIndex ?? 0];

return (
<svg className={className} width={14} height={14} viewBox="-8 -8 16 16">
<path d={data ?? symbols[0]} fill={color} />
</svg>
);
}
40 changes: 40 additions & 0 deletions docs/data/charts/pie-demo/PieChartWithCustomLegendTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from 'react';
import { PieChart } from '@mui/x-charts/PieChart';
import { ChartsLabelMarkProps } from '@mui/x-charts/ChartsLabel';

export default function PieChartWithCustomLegendTooltip() {
return (
<PieChart
series={[
{
data: [
{ id: 'cross', value: 10, label: 'Cross' },
{ id: 'diamond', value: 15, label: 'Diamond' },
{ id: 'star', value: 20, label: 'Star' },
],
},
]}
width={200}
height={200}
slots={{ labelMark: CustomMark }}
/>
);
}

const cross =
'M-5.35,-1.783L-1.783,-1.783L-1.783,-5.35L1.783,-5.35L1.783,-1.783L5.35,-1.783L5.35,1.783L1.783,1.783L1.783,5.35L-1.783,5.35L-1.783,1.783L-5.35,1.783Z';
const diamond = 'M0,-7.423L4.285,0L0,7.423L-4.285,0Z';
const star =
'M0,-7.528L1.69,-2.326L7.16,-2.326L2.735,0.889L4.425,6.09L0,2.875L-4.425,6.09L-2.735,0.889L-7.16,-2.326L-1.69,-2.326Z';

const symbols = [cross, diamond, star];

function CustomMark({ dataIndex, color, className }: ChartsLabelMarkProps) {
const data = symbols[dataIndex ?? 0];

return (
<svg className={className} width={14} height={14} viewBox="-8 -8 16 16">
<path d={data ?? symbols[0]} fill={color} />
</svg>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<PieChart
series={[
{
data: [
{ id: 'cross', value: 10, label: 'Cross' },
{ id: 'diamond', value: 15, label: 'Diamond' },
{ id: 'star', value: 20, label: 'Star' },
],
},
]}
width={200}
height={200}
slots={{ labelMark: CustomMark }}
/>
4 changes: 4 additions & 0 deletions docs/data/charts/pie-demo/pie-demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ components: PieArc, PieArcLabel, PieArcLabelPlot, PieArcPlot, PieChart, PiePlot
## PieChartWithCenterLabel

{{"demo": "PieChartWithCenterLabel.js"}}

## Pie chart with custom legend and tooltip

{{"demo": "PieChartWithCustomLegendTooltip.js"}}
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/bar-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
"default": "BarLabel",
"class": null
},
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "legend",
"description": "Custom rendering of the legend.",
Expand Down
10 changes: 9 additions & 1 deletion docs/pages/x/api/charts/charts-axis-tooltip-content.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
{
"props": { "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } } },
"props": {
"classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } },
"slotProps": { "type": { "name": "object" }, "default": "{}" },
"slots": {
"type": { "name": "object" },
"default": "{}",
"additionalInfo": { "slotsApi": true }
}
},
"name": "ChartsAxisTooltipContent",
"imports": [
"import { ChartsAxisTooltipContent } from '@mui/x-charts/ChartsTooltip';",
Expand Down
10 changes: 9 additions & 1 deletion docs/pages/x/api/charts/charts-item-tooltip-content.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
{
"props": { "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } } },
"props": {
"classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } },
"slotProps": { "type": { "name": "object" }, "default": "{}" },
"slots": {
"type": { "name": "object" },
"default": "{}",
"additionalInfo": { "slotsApi": true }
}
},
"name": "ChartsItemTooltipContent",
"imports": [
"import { ChartsItemTooltipContent } from '@mui/x-charts/ChartsTooltip';",
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/charts-legend.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"import { ChartsLegend } from '@mui/x-charts-pro';"
],
"slots": [
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "legend",
"description": "Custom rendering of the legend.",
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/charts-tooltip.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"import { ChartsTooltip } from '@mui/x-charts-pro';"
],
"slots": [
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "tooltip",
"description": "Custom component for the tooltip popper.",
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/line-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"default": "ChartsText",
"class": null
},
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "legend",
"description": "Custom rendering of the legend.",
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/pie-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"import { PieChart } from '@mui/x-charts-pro';"
],
"slots": [
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "legend",
"description": "Custom rendering of the legend.",
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/scatter-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"default": "ChartsText",
"class": null
},
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "legend",
"description": "Custom rendering of the legend.",
Expand Down
1 change: 1 addition & 0 deletions docs/pages/x/api/charts/spark-line-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"default": "BarElementPath",
"class": null
},
{ "name": "labelMark", "description": "Symbol next to the label of a series.", "class": null },
{
"name": "line",
"description": "The component that renders the line.",
Expand Down
1 change: 1 addition & 0 deletions docs/translations/api-docs/charts/bar-chart/bar-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"axisTickLabel": "Custom component for tick label.",
"bar": "The component that renders the bar.",
"barLabel": "The component that renders the bar label.",
"labelMark": "Symbol next to the label of a series.",
"legend": "Custom rendering of the legend.",
"loadingOverlay": "Overlay component rendered when the chart is in a loading state.",
"noDataOverlay": "Overlay component rendered when the chart has no data to display.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"componentDescription": "",
"propDescriptions": {
"classes": { "description": "Override or extend the styles applied to the component." }
"classes": { "description": "Override or extend the styles applied to the component." },
"slotProps": { "description": "The props used for each component slot." },
"slots": { "description": "Overridable component slots." }
},
"classDescriptions": {
"axisValueCell": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"componentDescription": "",
"propDescriptions": {
"classes": { "description": "Override or extend the styles applied to the component." }
"classes": { "description": "Override or extend the styles applied to the component." },
"slotProps": { "description": "The props used for each component slot." },
"slots": { "description": "Overridable component slots." }
},
"classDescriptions": {
"axisValueCell": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@
"nodeName": "the legend in column layout"
}
},
"slotDescriptions": { "legend": "Custom rendering of the legend." }
"slotDescriptions": {
"labelMark": "Symbol next to the label of a series.",
"legend": "Custom rendering of the legend."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@
"description": "Options provided to the <a href=\"https://popper.js.org/docs/v2/constructors/#options\"><code>Popper.js</code></a> instance."
},
"popperRef": { "description": "A ref that points to the used popper instance." },
"slotProps": { "description": "The props used for each slot inside the Popper." },
"slots": {
"description": "The components used for each slot inside the Popper. Either a string to use a HTML element or a component."
},
"slotProps": { "description": "The props used for each component slot." },
"slots": { "description": "Overridable component slots." },
"sx": {
"description": "The system prop that allows defining system overrides as well as additional CSS styles."
},
Expand Down Expand Up @@ -68,5 +66,8 @@
"nodeName": "the valueCell element"
}
},
"slotDescriptions": { "tooltip": "Custom component for the tooltip popper." }
"slotDescriptions": {
"labelMark": "Symbol next to the label of a series.",
"tooltip": "Custom component for the tooltip popper."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"axisLine": "Custom component for the axis main line.",
"axisTick": "Custom component for the axis tick.",
"axisTickLabel": "Custom component for tick label.",
"labelMark": "Symbol next to the label of a series.",
"legend": "Custom rendering of the legend.",
"line": "The component that renders the line.",
"lineHighlight": "",
Expand Down
1 change: 1 addition & 0 deletions docs/translations/api-docs/charts/pie-chart/pie-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
},
"classDescriptions": {},
"slotDescriptions": {
"labelMark": "Symbol next to the label of a series.",
"legend": "Custom rendering of the legend.",
"loadingOverlay": "Overlay component rendered when the chart is in a loading state.",
"noDataOverlay": "Overlay component rendered when the chart has no data to display.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"axisLine": "Custom component for the axis main line.",
"axisTick": "Custom component for the axis tick.",
"axisTickLabel": "Custom component for tick label.",
"labelMark": "Symbol next to the label of a series.",
"legend": "Custom rendering of the legend.",
"loadingOverlay": "Overlay component rendered when the chart is in a loading state.",
"noDataOverlay": "Overlay component rendered when the chart has no data to display.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"slotDescriptions": {
"area": "The component that renders the area.",
"bar": "The component that renders the bar.",
"labelMark": "Symbol next to the label of a series.",
"line": "The component that renders the line.",
"lineHighlight": "",
"mark": "",
Expand Down
Loading