Skip to content

Commit db0e5f8

Browse files
committed
[charts] Enhance scatter demo tooltip with item details
1 parent 1c65c1f commit db0e5f8

2 files changed

Lines changed: 196 additions & 2 deletions

File tree

docs/data/charts/bubble/ScatterZAxis.js

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import * as React from 'react';
22
import { ScatterChart } from '@mui/x-charts/ScatterChart';
3+
import { ChartsTooltipContainer, useItemTooltip } from '@mui/x-charts/ChartsTooltip';
4+
import { styled } from '@mui/material/styles';
35
import Stack from '@mui/material/Stack';
6+
import Box from '@mui/material/Box';
7+
import Divider from '@mui/material/Divider';
48
import Typography from '@mui/material/Typography';
59
import { useZAxis } from '@mui/x-charts-premium/hooks';
610
import { irisData } from '../dataset/irisDataset';
@@ -9,6 +13,19 @@ const species = ['Setosa', 'Versicolor', 'Virginica'];
913
const speciesColors = ['#2e7d32', '#ed6c02', '#9c27b0'];
1014
const speciesPredictionColors = ['#5aa35e', '#be7f4b', '#ba68c9'];
1115

16+
const TooltipPaper = styled('div', {
17+
name: 'Tooltip',
18+
slot: 'Paper',
19+
})(({ theme }) => {
20+
return {
21+
padding: theme.spacing(1),
22+
backgroundColor: (theme.vars || theme).palette.background.paper,
23+
color: (theme.vars || theme).palette.text.primary,
24+
borderRadius: (theme.vars || theme).shape?.borderRadius,
25+
border: `solid ${(theme.vars || theme).palette.divider} 1px`,
26+
};
27+
});
28+
1229
function IrisMarker(props) {
1330
const { x, y, color, size, isHighlighted, isFaded, dataIndex, ...other } = props;
1431

@@ -51,6 +68,86 @@ function IrisAnnotation() {
5168
);
5269
}
5370

71+
function CustomTooltip() {
72+
const item = useItemTooltip();
73+
74+
function numberFormatter(value) {
75+
if (typeof value === 'number') {
76+
return new Intl.NumberFormat('en-US').format(value);
77+
}
78+
return String(value);
79+
}
80+
81+
// Get the full data item from irisData using dataIndex from identifier
82+
const dataIndex = item?.identifier.dataIndex;
83+
const dataItem = dataIndex !== undefined ? irisData[dataIndex] : null;
84+
85+
if (!item || !dataItem) {
86+
return null;
87+
}
88+
89+
return (
90+
<ChartsTooltipContainer trigger="item">
91+
<TooltipPaper>
92+
<Box
93+
sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', mb: 1 }}
94+
>
95+
<Box
96+
sx={{
97+
width: 20,
98+
height: 20,
99+
backgroundColor: item?.color,
100+
borderRadius: 1,
101+
mr: 2,
102+
}}
103+
/>
104+
<Typography sx={{ fontWeight: 600 }}>{dataItem.species}</Typography>
105+
</Box>
106+
<Divider sx={{ my: 1 }} />
107+
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr', gap: 0.75 }}>
108+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
109+
<Typography variant="caption">Sepal Length:</Typography>
110+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
111+
{numberFormatter(dataItem.sepalLength)} cm
112+
</Typography>
113+
</Box>
114+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
115+
<Typography variant="caption">Sepal Width:</Typography>
116+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
117+
{numberFormatter(dataItem.sepalWidth)} cm
118+
</Typography>
119+
</Box>
120+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
121+
<Typography variant="caption">Petal Length:</Typography>
122+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
123+
{numberFormatter(dataItem.petalLength)} cm
124+
</Typography>
125+
</Box>
126+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
127+
<Typography variant="caption">Petal Width:</Typography>
128+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
129+
{numberFormatter(dataItem.petalWidth)} cm
130+
</Typography>
131+
</Box>
132+
<Divider sx={{ my: 0.5 }} />
133+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
134+
<Typography variant="caption">Predicted:</Typography>
135+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
136+
{dataItem.prediction}
137+
</Typography>
138+
</Box>
139+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
140+
<Typography variant="caption">Confidence:</Typography>
141+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
142+
{numberFormatter(dataItem.confidence)}%
143+
</Typography>
144+
</Box>
145+
</Box>
146+
</TooltipPaper>
147+
</ChartsTooltipContainer>
148+
);
149+
}
150+
54151
export default function ScatterZAxis() {
55152
return (
56153
<Stack sx={{ width: '100%' }}>
@@ -109,7 +206,7 @@ export default function ScatterZAxis() {
109206
},
110207
},
111208
]}
112-
slots={{ marker: IrisMarker }}
209+
slots={{ marker: IrisMarker, tooltip: CustomTooltip }}
113210
></ScatterChart>
114211
<IrisAnnotation />
115212
</Stack>

docs/data/charts/bubble/ScatterZAxis.tsx

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import * as React from 'react';
22
import { ScatterChart, ScatterMarkerProps } from '@mui/x-charts/ScatterChart';
3+
import { ChartsTooltipContainer, useItemTooltip } from '@mui/x-charts/ChartsTooltip';
4+
import { styled } from '@mui/material/styles';
35
import Stack from '@mui/material/Stack';
6+
import Box from '@mui/material/Box';
7+
import Divider from '@mui/material/Divider';
48
import Typography from '@mui/material/Typography';
59
import { useZAxis } from '@mui/x-charts-premium/hooks';
610
import { irisData } from '../dataset/irisDataset';
@@ -9,6 +13,19 @@ const species = ['Setosa', 'Versicolor', 'Virginica'];
913
const speciesColors = ['#2e7d32', '#ed6c02', '#9c27b0'];
1014
const speciesPredictionColors = ['#5aa35e', '#be7f4b', '#ba68c9'];
1115

16+
const TooltipPaper = styled('div', {
17+
name: 'Tooltip',
18+
slot: 'Paper',
19+
})(({ theme }) => {
20+
return {
21+
padding: theme.spacing(1),
22+
backgroundColor: (theme.vars || theme).palette.background.paper,
23+
color: (theme.vars || theme).palette.text.primary,
24+
borderRadius: (theme.vars || theme).shape?.borderRadius,
25+
border: `solid ${(theme.vars || theme).palette.divider} 1px`,
26+
};
27+
});
28+
1229
function IrisMarker(props: ScatterMarkerProps) {
1330
const { x, y, color, size, isHighlighted, isFaded, dataIndex, ...other } = props;
1431

@@ -51,6 +68,86 @@ function IrisAnnotation() {
5168
);
5269
}
5370

71+
function CustomTooltip() {
72+
const item = useItemTooltip<'scatter'>();
73+
74+
function numberFormatter(value: number | string | undefined) {
75+
if (typeof value === 'number') {
76+
return new Intl.NumberFormat('en-US').format(value);
77+
}
78+
return String(value);
79+
}
80+
81+
// Get the full data item from irisData using dataIndex from identifier
82+
const dataIndex = item?.identifier.dataIndex;
83+
const dataItem = dataIndex !== undefined ? irisData[dataIndex] : null;
84+
85+
if (!item || !dataItem) {
86+
return null;
87+
}
88+
89+
return (
90+
<ChartsTooltipContainer trigger="item">
91+
<TooltipPaper>
92+
<Box
93+
sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', mb: 1 }}
94+
>
95+
<Box
96+
sx={{
97+
width: 20,
98+
height: 20,
99+
backgroundColor: item?.color,
100+
borderRadius: 1,
101+
mr: 2,
102+
}}
103+
/>
104+
<Typography sx={{ fontWeight: 600 }}>{dataItem.species}</Typography>
105+
</Box>
106+
<Divider sx={{ my: 1 }} />
107+
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr', gap: 0.75 }}>
108+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
109+
<Typography variant="caption">Sepal Length:</Typography>
110+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
111+
{numberFormatter(dataItem.sepalLength)} cm
112+
</Typography>
113+
</Box>
114+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
115+
<Typography variant="caption">Sepal Width:</Typography>
116+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
117+
{numberFormatter(dataItem.sepalWidth)} cm
118+
</Typography>
119+
</Box>
120+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
121+
<Typography variant="caption">Petal Length:</Typography>
122+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
123+
{numberFormatter(dataItem.petalLength)} cm
124+
</Typography>
125+
</Box>
126+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
127+
<Typography variant="caption">Petal Width:</Typography>
128+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
129+
{numberFormatter(dataItem.petalWidth)} cm
130+
</Typography>
131+
</Box>
132+
<Divider sx={{ my: 0.5 }} />
133+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
134+
<Typography variant="caption">Predicted:</Typography>
135+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
136+
{dataItem.prediction}
137+
</Typography>
138+
</Box>
139+
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
140+
<Typography variant="caption">Confidence:</Typography>
141+
<Typography variant="caption" sx={{ fontWeight: 500 }}>
142+
{numberFormatter(dataItem.confidence)}%
143+
</Typography>
144+
</Box>
145+
</Box>
146+
</TooltipPaper>
147+
</ChartsTooltipContainer>
148+
);
149+
}
150+
54151
export default function ScatterZAxis() {
55152
return (
56153
<Stack sx={{ width: '100%' }}>
@@ -109,7 +206,7 @@ export default function ScatterZAxis() {
109206
},
110207
},
111208
]}
112-
slots={{ marker: IrisMarker }}
209+
slots={{ marker: IrisMarker, tooltip: CustomTooltip }}
113210
></ScatterChart>
114211
<IrisAnnotation />
115212
</Stack>

0 commit comments

Comments
 (0)