Skip to content

Commit

Permalink
[charts] Add an overlay for "no data" or "loading" states (#12817)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfauquette authored Apr 29, 2024
1 parent 1c57445 commit f9b1a63
Show file tree
Hide file tree
Showing 40 changed files with 600 additions and 27 deletions.
76 changes: 76 additions & 0 deletions docs/data/charts/styling/CustomOverlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Stack from '@mui/material/Stack';
import { BarChart } from '@mui/x-charts/BarChart';
import { useDrawingArea, useXScale, useYScale } from '@mui/x-charts/hooks';

const ratios = [0.2, 0.8, 0.6, 0.5];

const LoadingReact = styled('rect')({
opacity: 0.2,
fill: 'lightgray',
});

const LoadingText = styled('text')(({ theme }) => ({
stroke: 'none',
fill: theme.palette.text.primary,
shapeRendering: 'crispEdges',
textAnchor: 'middle',
dominantBaseline: 'middle',
}));

function LoadingOverlay() {
const xScale = useXScale();
const yScale = useYScale();
const { left, width, height } = useDrawingArea();

const bandWidth = xScale.bandwidth();

const [bottom, top] = yScale.range();

return (
<g>
{xScale.domain().map((item, index) => {
const ratio = ratios[index % ratios.length];
const barHeight = ratio * (bottom - top);

return (
<LoadingReact
x={xScale(item)}
width={bandWidth}
y={bottom - barHeight}
height={height}
/>
);
})}
<LoadingText x={left + width / 2} y={top + height / 2}>
Loading data ...
</LoadingText>
</g>
);
}

export default function CustomOverlay() {
return (
<Stack direction={{ md: 'row', xs: 'column' }} sx={{ width: '100%' }}>
<BarChart
slotProps={{
noDataOverlay: { message: 'No data to display in this chart' },
}}
series={[]}
margin={{ top: 10, right: 10, left: 25, bottom: 25 }}
height={150}
/>
<BarChart
loading
xAxis={[
{ scaleType: 'band', data: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] },
]}
slots={{ loadingOverlay: LoadingOverlay }}
series={[]}
margin={{ top: 10, right: 10, left: 25, bottom: 25 }}
height={150}
/>
</Stack>
);
}
76 changes: 76 additions & 0 deletions docs/data/charts/styling/CustomOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Stack from '@mui/material/Stack';
import { BarChart } from '@mui/x-charts/BarChart';
import { useDrawingArea, useXScale, useYScale } from '@mui/x-charts/hooks';

const ratios = [0.2, 0.8, 0.6, 0.5];

const LoadingReact = styled('rect')({
opacity: 0.2,
fill: 'lightgray',
});

const LoadingText = styled('text')(({ theme }) => ({
stroke: 'none',
fill: theme.palette.text.primary,
shapeRendering: 'crispEdges',
textAnchor: 'middle',
dominantBaseline: 'middle',
}));

function LoadingOverlay() {
const xScale = useXScale<'band'>();
const yScale = useYScale();
const { left, width, height } = useDrawingArea();

const bandWidth = xScale.bandwidth();

const [bottom, top] = yScale.range();

return (
<g>
{xScale.domain().map((item, index) => {
const ratio = ratios[index % ratios.length];
const barHeight = ratio * (bottom - top);

return (
<LoadingReact
x={xScale(item)}
width={bandWidth}
y={bottom - barHeight}
height={height}
/>
);
})}
<LoadingText x={left + width / 2} y={top + height / 2}>
Loading data ...
</LoadingText>
</g>
);
}

export default function CustomOverlay() {
return (
<Stack direction={{ md: 'row', xs: 'column' }} sx={{ width: '100%' }}>
<BarChart
slotProps={{
noDataOverlay: { message: 'No data to display in this chart' },
}}
series={[]}
margin={{ top: 10, right: 10, left: 25, bottom: 25 }}
height={150}
/>
<BarChart
loading
xAxis={[
{ scaleType: 'band', data: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] },
]}
slots={{ loadingOverlay: LoadingOverlay }}
series={[]}
margin={{ top: 10, right: 10, left: 25, bottom: 25 }}
height={150}
/>
</Stack>
);
}
18 changes: 18 additions & 0 deletions docs/data/charts/styling/Overlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import { LineChart } from '@mui/x-charts/LineChart';

const emptySeries = {
series: [],
margin: { top: 10, right: 10, left: 25, bottom: 25 },
height: 150,
};

export default function Overlay() {
return (
<Stack direction={{ md: 'row', xs: 'column' }} sx={{ width: '100%' }}>
<LineChart loading {...emptySeries} />
<LineChart {...emptySeries} />
</Stack>
);
}
18 changes: 18 additions & 0 deletions docs/data/charts/styling/Overlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import { LineChart } from '@mui/x-charts/LineChart';

const emptySeries = {
series: [],
margin: { top: 10, right: 10, left: 25, bottom: 25 },
height: 150,
};

export default function Overlay() {
return (
<Stack direction={{ md: 'row', xs: 'column' }} sx={{ width: '100%' }}>
<LineChart loading {...emptySeries} />
<LineChart {...emptySeries} />
</Stack>
);
}
2 changes: 2 additions & 0 deletions docs/data/charts/styling/Overlay.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<LineChart loading {...emptySeries} />
<LineChart {...emptySeries} />
38 changes: 38 additions & 0 deletions docs/data/charts/styling/OverlayWithAxis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import { LineChart } from '@mui/x-charts/LineChart';

const emptySeries = {
series: [],
margin: { top: 10, right: 10, left: 25, bottom: 25 },
height: 150,
};

export default function OverlayWithAxis() {
return (
<Stack direction={{ md: 'row', xs: 'column' }} sx={{ width: '100%' }}>
<LineChart
loading
xAxis={[{ data: [0, 1, 2, 4, 5] }]}
yAxis={[{ min: 0, max: 10 }]}
{...emptySeries}
/>
<LineChart
yAxis={[{ min: -5, max: 5 }]}
xAxis={[
{
scaleType: 'time',
data: [
new Date(2019, 0, 1),
new Date(2020, 0, 1),
new Date(2021, 0, 1),
new Date(2022, 0, 1),
],
tickNumber: 3,
},
]}
{...emptySeries}
/>
</Stack>
);
}
38 changes: 38 additions & 0 deletions docs/data/charts/styling/OverlayWithAxis.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as React from 'react';
import Stack from '@mui/material/Stack';
import { LineChart } from '@mui/x-charts/LineChart';

const emptySeries = {
series: [],
margin: { top: 10, right: 10, left: 25, bottom: 25 },
height: 150,
};

export default function OverlayWithAxis() {
return (
<Stack direction={{ md: 'row', xs: 'column' }} sx={{ width: '100%' }}>
<LineChart
loading
xAxis={[{ data: [0, 1, 2, 4, 5] }]}
yAxis={[{ min: 0, max: 10 }]}
{...emptySeries}
/>
<LineChart
yAxis={[{ min: -5, max: 5 }]}
xAxis={[
{
scaleType: 'time',
data: [
new Date(2019, 0, 1),
new Date(2020, 0, 1),
new Date(2021, 0, 1),
new Date(2022, 0, 1),
],
tickNumber: 3,
},
]}
{...emptySeries}
/>
</Stack>
);
}
34 changes: 34 additions & 0 deletions docs/data/charts/styling/styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,40 @@ This configuration can be used in Bar Charts to set colors according to string c
}
```

## Overlay

Charts have a _loading_ and _noData_ overlays that appears if

- `loading` prop is set to `true`.
- There is no data to display.

{{"demo": "Overlay.js"}}

### Axis display

You can provide the axes data in order to display them while loading the data.

{{"demo": "OverlayWithAxis.js"}}

### Custom overlay

To modify the overly message, you can use the `message` props as follow.

```jsx
<BarChart
slotProps={{
// Custom loading message
loadingOverlay: { message: 'Data should be available soon.' },
// Custom message for empty chart
noDataOverlay: { message: 'Select some data to display.' },
}}
/>
```

For more advanced customization, use the `loadingOverlay` and `noDataOverlay` slots link in the following demo.

{{"demo": "CustomOverlay.js"}}

## Styling

### Size
Expand Down
13 changes: 13 additions & 0 deletions docs/pages/x/api/charts/bar-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"type": { "name": "union", "description": "object<br>&#124;&nbsp;string" },
"default": "yAxisIds[0] The id of the first provided axis"
},
"loading": { "type": { "name": "bool" } },
"margin": {
"type": {
"name": "shape",
Expand Down Expand Up @@ -152,6 +153,18 @@
"description": "Custom component for displaying tooltip content when triggered by item event.",
"default": "DefaultChartsItemTooltipContent",
"class": null
},
{
"name": "loadingOverlay",
"description": "Overlay component rendered when the chart is in a loading state.",
"default": "ChartsLoadingOverlay",
"class": null
},
{
"name": "noDataOverlay",
"description": "Overlay component rendered when the chart has no data to display.",
"default": "ChartsNoDataOverlay",
"class": null
}
],
"classes": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
},
"required": true
},
"axisValue": { "type": { "name": "any" }, "required": true },
"classes": {
"type": { "name": "object" },
"required": true,
Expand All @@ -18,6 +17,9 @@
"type": { "name": "arrayOf", "description": "Array&lt;object&gt;" },
"required": true
},
"axisValue": {
"type": { "name": "union", "description": "Date<br>&#124;&nbsp;number<br>&#124;&nbsp;string" }
},
"dataIndex": { "type": { "name": "number" } }
},
"name": "DefaultChartsAxisTooltipContent",
Expand Down
13 changes: 13 additions & 0 deletions docs/pages/x/api/charts/line-chart.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"type": { "name": "union", "description": "object<br>&#124;&nbsp;string" },
"default": "yAxisIds[0] The id of the first provided axis"
},
"loading": { "type": { "name": "bool" } },
"margin": {
"type": {
"name": "shape",
Expand Down Expand Up @@ -155,6 +156,18 @@
"description": "Custom component for displaying tooltip content when triggered by item event.",
"default": "DefaultChartsItemTooltipContent",
"class": null
},
{
"name": "loadingOverlay",
"description": "Overlay component rendered when the chart is in a loading state.",
"default": "ChartsLoadingOverlay",
"class": null
},
{
"name": "noDataOverlay",
"description": "Overlay component rendered when the chart has no data to display.",
"default": "ChartsNoDataOverlay",
"class": null
}
],
"classes": [],
Expand Down
Loading

0 comments on commit f9b1a63

Please sign in to comment.