Skip to content

Commit a62b88f

Browse files
GuillaumeMeheutalexfauquetteJCQuintas
authored
[charts] Add domainLimit to axis config. (#15294)
Co-authored-by: alex <[email protected]> Co-authored-by: Jose Quintas <[email protected]>
1 parent 7062bbb commit a62b88f

File tree

34 files changed

+396
-26
lines changed

34 files changed

+396
-26
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as React from 'react';
2+
import Box from '@mui/material/Box';
3+
import { BarChart } from '@mui/x-charts/BarChart';
4+
import TextField from '@mui/material/TextField';
5+
import MenuItem from '@mui/material/MenuItem';
6+
7+
const settings = {
8+
valueFormatter: (v) => `${v}%`,
9+
height: 200,
10+
showTooltip: true,
11+
showHighlight: true,
12+
series: [{ data: [60, -15, 66, 68, 87, 82, 83, 85, 92, 75, 76, 50, 91] }],
13+
margin: { top: 10, bottom: 20 },
14+
};
15+
16+
// Extend a value to match a multiple of the step.
17+
function extend(value, step) {
18+
if (value > 0) {
19+
// If >0 go to the next step
20+
return step * Math.ceil(value / step);
21+
}
22+
// If <0 go to the previous step
23+
return step * Math.floor(value / step);
24+
}
25+
26+
export default function CustomDomainYAxis() {
27+
const [domainLimit, setDomainLimit] = React.useState('nice');
28+
29+
return (
30+
<Box sx={{ width: '100%' }}>
31+
<TextField
32+
select
33+
value={domainLimit}
34+
onChange={(event) => setDomainLimit(event.target.value)}
35+
label="domain limit"
36+
sx={{ minWidth: 150, mb: 2 }}
37+
>
38+
<MenuItem value="nice">nice</MenuItem>
39+
<MenuItem value="strict">strict</MenuItem>
40+
<MenuItem value="function">function</MenuItem>
41+
</TextField>
42+
43+
<BarChart
44+
yAxis={[
45+
{
46+
domainLimit:
47+
domainLimit === 'function'
48+
? (min, max) => ({
49+
min: extend(min, 10),
50+
max: extend(max, 10),
51+
})
52+
: domainLimit,
53+
},
54+
]}
55+
{...settings}
56+
/>
57+
</Box>
58+
);
59+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import * as React from 'react';
2+
import Box from '@mui/material/Box';
3+
import { BarChart } from '@mui/x-charts/BarChart';
4+
import TextField from '@mui/material/TextField';
5+
import MenuItem from '@mui/material/MenuItem';
6+
7+
const settings = {
8+
valueFormatter: (v: number | null) => `${v}%`,
9+
height: 200,
10+
showTooltip: true,
11+
showHighlight: true,
12+
series: [{ data: [60, -15, 66, 68, 87, 82, 83, 85, 92, 75, 76, 50, 91] }],
13+
margin: { top: 10, bottom: 20 },
14+
};
15+
16+
// Extend a value to match a multiple of the step.
17+
function extend(value: number, step: number) {
18+
if (value > 0) {
19+
// If >0 go to the next step
20+
return step * Math.ceil(value / step);
21+
}
22+
// If <0 go to the previous step
23+
return step * Math.floor(value / step);
24+
}
25+
26+
export default function CustomDomainYAxis() {
27+
const [domainLimit, setDomainLimit] = React.useState<
28+
'nice' | 'strict' | 'function'
29+
>('nice');
30+
31+
return (
32+
<Box sx={{ width: '100%' }}>
33+
<TextField
34+
select
35+
value={domainLimit}
36+
onChange={(event) =>
37+
setDomainLimit(event.target.value as 'nice' | 'strict' | 'function')
38+
}
39+
label="domain limit"
40+
sx={{ minWidth: 150, mb: 2 }}
41+
>
42+
<MenuItem value="nice">nice</MenuItem>
43+
<MenuItem value="strict">strict</MenuItem>
44+
<MenuItem value="function">function</MenuItem>
45+
</TextField>
46+
47+
<BarChart
48+
yAxis={[
49+
{
50+
domainLimit:
51+
domainLimit === 'function'
52+
? (min, max) => ({
53+
min: extend(min, 10),
54+
max: extend(max, 10),
55+
})
56+
: domainLimit,
57+
},
58+
]}
59+
{...settings}
60+
/>
61+
</Box>
62+
);
63+
}

docs/data/charts/axis/axis.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ xAxis={[
8686

8787
{{"demo": "MinMaxExample.js"}}
8888

89+
### Relative axis sub domain
90+
91+
You can adjust the axis range relatively to its data by using the `domainLimit` option.
92+
It can take 3 different values:
93+
94+
- `"nice"` Rounds the domain at human friendly values. It's the default behavior.
95+
- `"strict"` Sets the domain to the min/max value to display.
96+
- `([minValue, maxValue]) => [min, max]` Receives the calculated extremums as parameters, and should return the axis domain.
97+
98+
The demo below shows different ways to set the y-axis range.
99+
They always display the same data, going from -15 to 92, but with different `domainLimit` settings.
100+
101+
{{"demo": "CustomDomainYAxis.js"}}
102+
89103
### Axis direction
90104

91105
By default, the axes' directions are left to right and bottom to top.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import * as React from 'react';
2+
import TextField from '@mui/material/TextField';
3+
import MenuItem from '@mui/material/MenuItem';
4+
import Stack from '@mui/material/Stack';
5+
import Box from '@mui/material/Box';
6+
import Typography from '@mui/material/Typography';
7+
8+
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
9+
10+
const settings = {
11+
valueFormatter: (v) => `${v}%`,
12+
height: 100,
13+
showTooltip: true,
14+
showHighlight: true,
15+
data: [60, -15, 66, 68, 87, 82, 83, 85, 92, 75, 76, 50, 91],
16+
margin: { top: 10, bottom: 20, left: 5, right: 5 },
17+
sx: (theme) => ({
18+
borderWidth: 1,
19+
borderStyle: 'solid',
20+
borderColor: theme.palette.divider,
21+
}),
22+
};
23+
24+
// Extend a value to match a multiple of the step.
25+
function extend(value, step) {
26+
if (value > 0) {
27+
// If >0 go to the next step
28+
return step * Math.ceil(value / step);
29+
}
30+
// If <0 go to the previous step
31+
return step * Math.floor(value / step);
32+
}
33+
34+
const yRange = {
35+
nice: '-100, 100',
36+
strict: '-15, 92',
37+
function: '-20, 100',
38+
};
39+
export default function CustomDomainYAxis() {
40+
const [domainLimitKey, setDomainLimitKey] = React.useState('nice');
41+
42+
const domainLimit =
43+
domainLimitKey === 'function'
44+
? (min, max) => ({
45+
min: extend(min, 10),
46+
max: extend(max, 10),
47+
})
48+
: domainLimitKey;
49+
return (
50+
<Box
51+
sx={{
52+
width: '100%',
53+
}}
54+
>
55+
<Stack direction="row" alignItems="baseline" justifyContent="space-between">
56+
<TextField
57+
select
58+
value={domainLimitKey}
59+
onChange={(event) => setDomainLimitKey(event.target.value)}
60+
label="domain limit"
61+
sx={{ minWidth: 150, mb: 2 }}
62+
>
63+
<MenuItem value="nice">nice</MenuItem>
64+
<MenuItem value="strict">strict</MenuItem>
65+
<MenuItem value="function">function</MenuItem>
66+
</TextField>
67+
<Typography>y-axis range: {yRange[domainLimitKey]}</Typography>
68+
</Stack>
69+
70+
<Stack
71+
sx={{
72+
width: '100%',
73+
}}
74+
direction="row"
75+
spacing={2}
76+
>
77+
<Box sx={{ flexGrow: 1 }}>
78+
<SparkLineChart {...settings} yAxis={{ domainLimit }} />
79+
</Box>
80+
<Box sx={{ flexGrow: 1 }}>
81+
<SparkLineChart plotType="bar" {...settings} yAxis={{ domainLimit }} />
82+
</Box>
83+
</Stack>
84+
</Box>
85+
);
86+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import * as React from 'react';
2+
import TextField from '@mui/material/TextField';
3+
import MenuItem from '@mui/material/MenuItem';
4+
import Stack from '@mui/material/Stack';
5+
import Box from '@mui/material/Box';
6+
import Typography from '@mui/material/Typography';
7+
import { Theme } from '@mui/material/styles';
8+
import { SparkLineChart } from '@mui/x-charts/SparkLineChart';
9+
10+
const settings = {
11+
valueFormatter: (v: number | null) => `${v}%`,
12+
height: 100,
13+
showTooltip: true,
14+
showHighlight: true,
15+
data: [60, -15, 66, 68, 87, 82, 83, 85, 92, 75, 76, 50, 91],
16+
margin: { top: 10, bottom: 20, left: 5, right: 5 },
17+
sx: (theme: Theme) => ({
18+
borderWidth: 1,
19+
borderStyle: 'solid',
20+
borderColor: theme.palette.divider,
21+
}),
22+
};
23+
24+
// Extend a value to match a multiple of the step.
25+
function extend(value: number, step: number) {
26+
if (value > 0) {
27+
// If >0 go to the next step
28+
return step * Math.ceil(value / step);
29+
}
30+
// If <0 go to the previous step
31+
return step * Math.floor(value / step);
32+
}
33+
34+
const yRange = {
35+
nice: '-100, 100',
36+
strict: '-15, 92',
37+
function: '-20, 100',
38+
};
39+
export default function CustomDomainYAxis() {
40+
const [domainLimitKey, setDomainLimitKey] = React.useState<
41+
'nice' | 'strict' | 'function'
42+
>('nice');
43+
44+
const domainLimit =
45+
domainLimitKey === 'function'
46+
? (min: number, max: number) => ({
47+
min: extend(min, 10),
48+
max: extend(max, 10),
49+
})
50+
: domainLimitKey;
51+
return (
52+
<Box
53+
sx={{
54+
width: '100%',
55+
}}
56+
>
57+
<Stack direction="row" alignItems="baseline" justifyContent="space-between">
58+
<TextField
59+
select
60+
value={domainLimitKey}
61+
onChange={(event) =>
62+
setDomainLimitKey(event.target.value as 'nice' | 'strict' | 'function')
63+
}
64+
label="domain limit"
65+
sx={{ minWidth: 150, mb: 2 }}
66+
>
67+
<MenuItem value="nice">nice</MenuItem>
68+
<MenuItem value="strict">strict</MenuItem>
69+
<MenuItem value="function">function</MenuItem>
70+
</TextField>
71+
<Typography>y-axis range: {yRange[domainLimitKey]}</Typography>
72+
</Stack>
73+
74+
<Stack
75+
sx={{
76+
width: '100%',
77+
}}
78+
direction="row"
79+
spacing={2}
80+
>
81+
<Box sx={{ flexGrow: 1 }}>
82+
<SparkLineChart {...settings} yAxis={{ domainLimit }} />
83+
</Box>
84+
<Box sx={{ flexGrow: 1 }}>
85+
<SparkLineChart plotType="bar" {...settings} yAxis={{ domainLimit }} />
86+
</Box>
87+
</Stack>
88+
</Box>
89+
);
90+
}

docs/data/charts/sparkline/sparkline.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,11 @@ The following demo shows two sparklines, one with small and another with large v
5858
The first row has the default y-axis values, while on the second row a fixed range from `0` to `100` has been set.
5959

6060
{{"demo": "CustomYAxis.js"}}
61+
62+
You can adjust the y-axis range of a sparkline relatively to its data by using the `domainLimit` option in the `yAxis` configuration.
63+
See the [axis docs page](http://localhost:3001/x/react-charts/axis/#relative-axis-sub-domain) form more information.
64+
65+
The demo below shows different ways to set the y-axis range.
66+
They always display the same data, going from -15 to 92, but with different `domainLimit` settings.
67+
68+
{{"demo": "CustomDomainYAxis.js"}}

docs/pages/x/api/charts/axis-config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
},
1010
"data": { "type": { "description": "V[]" } },
1111
"dataKey": { "type": { "description": "string" } },
12+
"domainLimit": {
13+
"type": {
14+
"description": "'nice' | 'strict' | ((min: number, max: number) =&gt; { min: number; max: number })"
15+
}
16+
},
1217
"hideTooltip": { "type": { "description": "boolean" } },
1318
"max": { "type": { "description": "number | Date" } },
1419
"min": { "type": { "description": "number | Date" } },

docs/pages/x/api/charts/bar-chart-pro.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@
108108
"xAxis": {
109109
"type": {
110110
"name": "arrayOf",
111-
"description": "Array&lt;{ classes?: object, colorMap?: { colors: Array&lt;string&gt;, type: 'ordinal', unknownColor?: string, values?: Array&lt;Date<br>&#124;&nbsp;number<br>&#124;&nbsp;string&gt; }<br>&#124;&nbsp;{ color: Array&lt;string&gt;<br>&#124;&nbsp;func, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, type: 'continuous' }<br>&#124;&nbsp;{ colors: Array&lt;string&gt;, thresholds: Array&lt;Date<br>&#124;&nbsp;number&gt;, type: 'piecewise' }, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number<br>&#124;&nbsp;string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, position?: 'bottom'<br>&#124;&nbsp;'top', reverse?: bool, scaleType?: 'band'<br>&#124;&nbsp;'linear'<br>&#124;&nbsp;'log'<br>&#124;&nbsp;'point'<br>&#124;&nbsp;'pow'<br>&#124;&nbsp;'sqrt'<br>&#124;&nbsp;'time'<br>&#124;&nbsp;'utc', slotProps?: object, slots?: object, stroke?: string, sx?: Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object, tickFontSize?: number, tickInterval?: 'auto'<br>&#124;&nbsp;array<br>&#124;&nbsp;func, tickLabelInterval?: 'auto'<br>&#124;&nbsp;func, tickLabelPlacement?: 'middle'<br>&#124;&nbsp;'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'<br>&#124;&nbsp;'extremities'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'start', tickSize?: number, valueFormatter?: func, zoom?: { filterMode?: 'discard'<br>&#124;&nbsp;'keep', maxEnd?: number, maxSpan?: number, minSpan?: number, minStart?: number, panning?: bool, step?: number }<br>&#124;&nbsp;bool }&gt;"
111+
"description": "Array&lt;{ classes?: object, colorMap?: { colors: Array&lt;string&gt;, type: 'ordinal', unknownColor?: string, values?: Array&lt;Date<br>&#124;&nbsp;number<br>&#124;&nbsp;string&gt; }<br>&#124;&nbsp;{ color: Array&lt;string&gt;<br>&#124;&nbsp;func, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, type: 'continuous' }<br>&#124;&nbsp;{ colors: Array&lt;string&gt;, thresholds: Array&lt;Date<br>&#124;&nbsp;number&gt;, type: 'piecewise' }, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, domainLimit?: 'nice'<br>&#124;&nbsp;'strict'<br>&#124;&nbsp;func, fill?: string, hideTooltip?: bool, id?: number<br>&#124;&nbsp;string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, position?: 'bottom'<br>&#124;&nbsp;'top', reverse?: bool, scaleType?: 'band'<br>&#124;&nbsp;'linear'<br>&#124;&nbsp;'log'<br>&#124;&nbsp;'point'<br>&#124;&nbsp;'pow'<br>&#124;&nbsp;'sqrt'<br>&#124;&nbsp;'time'<br>&#124;&nbsp;'utc', slotProps?: object, slots?: object, stroke?: string, sx?: Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object, tickFontSize?: number, tickInterval?: 'auto'<br>&#124;&nbsp;array<br>&#124;&nbsp;func, tickLabelInterval?: 'auto'<br>&#124;&nbsp;func, tickLabelPlacement?: 'middle'<br>&#124;&nbsp;'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'<br>&#124;&nbsp;'extremities'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'start', tickSize?: number, valueFormatter?: func, zoom?: { filterMode?: 'discard'<br>&#124;&nbsp;'keep', maxEnd?: number, maxSpan?: number, minSpan?: number, minStart?: number, panning?: bool, step?: number }<br>&#124;&nbsp;bool }&gt;"
112112
}
113113
},
114114
"yAxis": {
115115
"type": {
116116
"name": "arrayOf",
117-
"description": "Array&lt;{ classes?: object, colorMap?: { colors: Array&lt;string&gt;, type: 'ordinal', unknownColor?: string, values?: Array&lt;Date<br>&#124;&nbsp;number<br>&#124;&nbsp;string&gt; }<br>&#124;&nbsp;{ color: Array&lt;string&gt;<br>&#124;&nbsp;func, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, type: 'continuous' }<br>&#124;&nbsp;{ colors: Array&lt;string&gt;, thresholds: Array&lt;Date<br>&#124;&nbsp;number&gt;, type: 'piecewise' }, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number<br>&#124;&nbsp;string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, position?: 'left'<br>&#124;&nbsp;'right', reverse?: bool, scaleType?: 'band'<br>&#124;&nbsp;'linear'<br>&#124;&nbsp;'log'<br>&#124;&nbsp;'point'<br>&#124;&nbsp;'pow'<br>&#124;&nbsp;'sqrt'<br>&#124;&nbsp;'time'<br>&#124;&nbsp;'utc', slotProps?: object, slots?: object, stroke?: string, sx?: Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object, tickFontSize?: number, tickInterval?: 'auto'<br>&#124;&nbsp;array<br>&#124;&nbsp;func, tickLabelInterval?: 'auto'<br>&#124;&nbsp;func, tickLabelPlacement?: 'middle'<br>&#124;&nbsp;'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'<br>&#124;&nbsp;'extremities'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'start', tickSize?: number, valueFormatter?: func, zoom?: { filterMode?: 'discard'<br>&#124;&nbsp;'keep', maxEnd?: number, maxSpan?: number, minSpan?: number, minStart?: number, panning?: bool, step?: number }<br>&#124;&nbsp;bool }&gt;"
117+
"description": "Array&lt;{ classes?: object, colorMap?: { colors: Array&lt;string&gt;, type: 'ordinal', unknownColor?: string, values?: Array&lt;Date<br>&#124;&nbsp;number<br>&#124;&nbsp;string&gt; }<br>&#124;&nbsp;{ color: Array&lt;string&gt;<br>&#124;&nbsp;func, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, type: 'continuous' }<br>&#124;&nbsp;{ colors: Array&lt;string&gt;, thresholds: Array&lt;Date<br>&#124;&nbsp;number&gt;, type: 'piecewise' }, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, domainLimit?: 'nice'<br>&#124;&nbsp;'strict'<br>&#124;&nbsp;func, fill?: string, hideTooltip?: bool, id?: number<br>&#124;&nbsp;string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date<br>&#124;&nbsp;number, min?: Date<br>&#124;&nbsp;number, position?: 'left'<br>&#124;&nbsp;'right', reverse?: bool, scaleType?: 'band'<br>&#124;&nbsp;'linear'<br>&#124;&nbsp;'log'<br>&#124;&nbsp;'point'<br>&#124;&nbsp;'pow'<br>&#124;&nbsp;'sqrt'<br>&#124;&nbsp;'time'<br>&#124;&nbsp;'utc', slotProps?: object, slots?: object, stroke?: string, sx?: Array&lt;func<br>&#124;&nbsp;object<br>&#124;&nbsp;bool&gt;<br>&#124;&nbsp;func<br>&#124;&nbsp;object, tickFontSize?: number, tickInterval?: 'auto'<br>&#124;&nbsp;array<br>&#124;&nbsp;func, tickLabelInterval?: 'auto'<br>&#124;&nbsp;func, tickLabelPlacement?: 'middle'<br>&#124;&nbsp;'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'<br>&#124;&nbsp;'extremities'<br>&#124;&nbsp;'middle'<br>&#124;&nbsp;'start', tickSize?: number, valueFormatter?: func, zoom?: { filterMode?: 'discard'<br>&#124;&nbsp;'keep', maxEnd?: number, maxSpan?: number, minSpan?: number, minStart?: number, panning?: bool, step?: number }<br>&#124;&nbsp;bool }&gt;"
118118
}
119119
},
120120
"zoom": {

0 commit comments

Comments
 (0)