Skip to content

Commit b66a472

Browse files
[charts] Add context to axis value formatter (#12172)
Signed-off-by: Alexandre Fauquette <[email protected]> Co-authored-by: Lukas <[email protected]>
1 parent 4f8f20a commit b66a472

File tree

7 files changed

+291
-7
lines changed

7 files changed

+291
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import * as React from 'react';
2+
import { axisClasses } from '@mui/x-charts/ChartsAxis';
3+
import { BarChart } from '@mui/x-charts/BarChart';
4+
5+
const otherSetting = {
6+
height: 300,
7+
yAxis: [{ label: 'rainfall (mm)' }],
8+
grid: { horizontal: true },
9+
sx: {
10+
[`& .${axisClasses.left} .${axisClasses.label}`]: {
11+
transform: 'translateX(-10px)',
12+
},
13+
},
14+
};
15+
16+
const dataset = [
17+
{
18+
london: 59,
19+
paris: 57,
20+
newYork: 86,
21+
seoul: 21,
22+
month: 'January',
23+
},
24+
{
25+
london: 50,
26+
paris: 52,
27+
newYork: 78,
28+
seoul: 28,
29+
month: 'February',
30+
},
31+
{
32+
london: 47,
33+
paris: 53,
34+
newYork: 106,
35+
seoul: 41,
36+
month: 'March',
37+
},
38+
{
39+
london: 54,
40+
paris: 56,
41+
newYork: 92,
42+
seoul: 73,
43+
month: 'April',
44+
},
45+
{
46+
london: 57,
47+
paris: 69,
48+
newYork: 92,
49+
seoul: 99,
50+
month: 'May',
51+
},
52+
{
53+
london: 60,
54+
paris: 63,
55+
newYork: 103,
56+
seoul: 144,
57+
month: 'June',
58+
},
59+
{
60+
london: 59,
61+
paris: 60,
62+
newYork: 105,
63+
seoul: 319,
64+
month: 'July',
65+
},
66+
{
67+
london: 65,
68+
paris: 60,
69+
newYork: 106,
70+
seoul: 249,
71+
month: 'August',
72+
},
73+
{
74+
london: 51,
75+
paris: 51,
76+
newYork: 95,
77+
seoul: 131,
78+
month: 'September',
79+
},
80+
{
81+
london: 60,
82+
paris: 65,
83+
newYork: 97,
84+
seoul: 55,
85+
month: 'October',
86+
},
87+
{
88+
london: 67,
89+
paris: 64,
90+
newYork: 76,
91+
seoul: 48,
92+
month: 'November',
93+
},
94+
{
95+
london: 61,
96+
paris: 70,
97+
newYork: 103,
98+
seoul: 25,
99+
month: 'December',
100+
},
101+
];
102+
103+
const valueFormatter = (value) => `${value}mm`;
104+
105+
export default function FormatterDemoNoSnap() {
106+
return (
107+
<BarChart
108+
dataset={dataset}
109+
xAxis={[
110+
{
111+
scaleType: 'band',
112+
dataKey: 'month',
113+
valueFormatter: (month, context) =>
114+
context.location === 'tick'
115+
? `${month.slice(0, 3)} \n2023`
116+
: `${month} 2023`,
117+
},
118+
]}
119+
series={[{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }]}
120+
{...otherSetting}
121+
/>
122+
);
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import * as React from 'react';
2+
import { axisClasses } from '@mui/x-charts/ChartsAxis';
3+
import { BarChart } from '@mui/x-charts/BarChart';
4+
5+
const otherSetting = {
6+
height: 300,
7+
yAxis: [{ label: 'rainfall (mm)' }],
8+
grid: { horizontal: true },
9+
sx: {
10+
[`& .${axisClasses.left} .${axisClasses.label}`]: {
11+
transform: 'translateX(-10px)',
12+
},
13+
},
14+
};
15+
16+
const dataset = [
17+
{
18+
london: 59,
19+
paris: 57,
20+
newYork: 86,
21+
seoul: 21,
22+
month: 'January',
23+
},
24+
{
25+
london: 50,
26+
paris: 52,
27+
newYork: 78,
28+
seoul: 28,
29+
month: 'February',
30+
},
31+
{
32+
london: 47,
33+
paris: 53,
34+
newYork: 106,
35+
seoul: 41,
36+
month: 'March',
37+
},
38+
{
39+
london: 54,
40+
paris: 56,
41+
newYork: 92,
42+
seoul: 73,
43+
month: 'April',
44+
},
45+
{
46+
london: 57,
47+
paris: 69,
48+
newYork: 92,
49+
seoul: 99,
50+
month: 'May',
51+
},
52+
{
53+
london: 60,
54+
paris: 63,
55+
newYork: 103,
56+
seoul: 144,
57+
month: 'June',
58+
},
59+
{
60+
london: 59,
61+
paris: 60,
62+
newYork: 105,
63+
seoul: 319,
64+
month: 'July',
65+
},
66+
{
67+
london: 65,
68+
paris: 60,
69+
newYork: 106,
70+
seoul: 249,
71+
month: 'August',
72+
},
73+
{
74+
london: 51,
75+
paris: 51,
76+
newYork: 95,
77+
seoul: 131,
78+
month: 'September',
79+
},
80+
{
81+
london: 60,
82+
paris: 65,
83+
newYork: 97,
84+
seoul: 55,
85+
month: 'October',
86+
},
87+
{
88+
london: 67,
89+
paris: 64,
90+
newYork: 76,
91+
seoul: 48,
92+
month: 'November',
93+
},
94+
{
95+
london: 61,
96+
paris: 70,
97+
newYork: 103,
98+
seoul: 25,
99+
month: 'December',
100+
},
101+
];
102+
103+
const valueFormatter = (value: number | null) => `${value}mm`;
104+
105+
export default function FormatterDemoNoSnap() {
106+
return (
107+
<BarChart
108+
dataset={dataset}
109+
xAxis={[
110+
{
111+
scaleType: 'band',
112+
dataKey: 'month',
113+
valueFormatter: (month, context) =>
114+
context.location === 'tick'
115+
? `${month.slice(0, 3)} \n2023`
116+
: `${month} 2023`,
117+
},
118+
]}
119+
series={[{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }]}
120+
{...otherSetting}
121+
/>
122+
);
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<BarChart
2+
dataset={dataset}
3+
xAxis={[
4+
{
5+
scaleType: 'band',
6+
dataKey: 'month',
7+
valueFormatter: (month, context) =>
8+
context.location === 'tick'
9+
? `${month.slice(0, 3)} \n2023`
10+
: `${month} 2023`,
11+
},
12+
]}
13+
series={[{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }]}
14+
{...otherSetting}
15+
/>

docs/data/charts/axis/axis.md

+12
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ Some series types also require specific axis attributes:
5959
- line plots require an `xAxis` to have `data` provided
6060
- bar plots require an `xAxis` with `scaleType='band'` and some `data` provided.
6161

62+
### Axis formatter
63+
64+
Axis data can be displayed in the axes ticks and the tooltip.
65+
To modify how data is displayed use the `valueFormatter` property.
66+
67+
The second argument of `valueFormatter` provides some rendering context for advanced use cases.
68+
69+
In the next demo, `valueFormatter` is used to shorten months and introduce a breaking space for ticks only.
70+
To distinguish tick and tooltip, it uses the `context.location`.
71+
72+
{{"demo": "FormatterDemoNoSnap.js"}}
73+
6274
### Axis sub domain
6375

6476
By default, the axis domain is computed such that all your data is visible.

packages/x-charts/src/ChartsTooltip/DefaultChartsAxisTooltipContent.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function DefaultChartsAxisTooltipContent(props: ChartsAxisContentProps) {
3131
<thead>
3232
<ChartsTooltipRow>
3333
<ChartsTooltipCell colSpan={3}>
34-
<Typography>{axisFormatter(axisValue)}</Typography>
34+
<Typography>{axisFormatter(axisValue, { location: 'tooltip' })}</Typography>
3535
</ChartsTooltipCell>
3636
</ChartsTooltipRow>
3737
</thead>

packages/x-charts/src/hooks/useTicks.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { D3Scale } from '../models/axis';
2+
import { AxisConfig, D3Scale } from '../models/axis';
33
import { isBandScale } from '../internals/isBandScale';
44

55
export interface TickParams {
@@ -80,7 +80,7 @@ export type TickItemType = {
8080
export function useTicks(
8181
options: {
8282
scale: D3Scale;
83-
valueFormatter?: (value: any) => string;
83+
valueFormatter?: AxisConfig['valueFormatter'];
8484
} & Pick<TickParams, 'tickNumber' | 'tickInterval' | 'tickPlacement' | 'tickLabelPlacement'>,
8585
): TickItemType[] {
8686
const {
@@ -102,7 +102,7 @@ export function useTicks(
102102
return [
103103
...domain.map((value) => ({
104104
value,
105-
formattedValue: valueFormatter?.(value) ?? `${value}`,
105+
formattedValue: valueFormatter?.(value, { location: 'tick' }) ?? `${value}`,
106106
offset:
107107
scale(value)! -
108108
(scale.step() - scale.bandwidth()) / 2 +
@@ -133,7 +133,7 @@ export function useTicks(
133133

134134
return filteredDomain.map((value) => ({
135135
value,
136-
formattedValue: valueFormatter?.(value) ?? `${value}`,
136+
formattedValue: valueFormatter?.(value, { location: 'tick' }) ?? `${value}`,
137137
offset: scale(value)!,
138138
labelOffset: 0,
139139
}));
@@ -142,7 +142,8 @@ export function useTicks(
142142
const ticks = typeof tickInterval === 'object' ? tickInterval : scale.ticks(tickNumber);
143143
return ticks.map((value: any) => ({
144144
value,
145-
formattedValue: valueFormatter?.(value) ?? scale.tickFormat(tickNumber)(value),
145+
formattedValue:
146+
valueFormatter?.(value, { location: 'tick' }) ?? scale.tickFormat(tickNumber)(value),
146147
offset: scale(value),
147148
labelOffset: 0,
148149
}));

packages/x-charts/src/models/axis.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ interface AxisScaleConfig {
186186
};
187187
}
188188

189+
export type AxisValueFormatterContext = {
190+
location: 'tick' | 'tooltip';
191+
};
192+
189193
export type AxisConfig<S extends ScaleName = ScaleName, V = any> = {
190194
/**
191195
* Id used to identify the axis.
@@ -209,7 +213,13 @@ export type AxisConfig<S extends ScaleName = ScaleName, V = any> = {
209213
* The key used to retrieve `data` from the `dataset` prop.
210214
*/
211215
dataKey?: string;
212-
valueFormatter?: (value: V) => string;
216+
/**
217+
* Formats the axis value.
218+
* @param {V} value The value to format.
219+
* @param {AxisValueFormatterContext} context The rendering context of the value.
220+
* @returns {string} The string to display.
221+
*/
222+
valueFormatter?: (value: V, context: AxisValueFormatterContext) => string;
213223
/**
214224
* If `true`, hide this value in the tooltip
215225
*/

0 commit comments

Comments
 (0)