From bdd82aeac9faf01ad68ed2e8f3258c39fc29ee49 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:35:41 +0100 Subject: [PATCH] [charts] Plot data at first render if `skipAnimation` is set to `true` (#16166) Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Signed-off-by: Jose C Quintas Jr Co-authored-by: Jose C Quintas Jr --- .../charts/getting-started/getting-started.md | 9 ++ .../src/BarChart/BarLabel/BarLabelPlot.tsx | 2 +- packages/x-charts/src/BarChart/BarPlot.tsx | 4 +- .../x-charts/src/LineChart/AnimatedArea.tsx | 2 +- .../x-charts/src/LineChart/AnimatedLine.tsx | 2 +- .../x-charts/src/LineChart/AppearingMask.tsx | 2 +- .../x-charts/src/PieChart/PieArcLabelPlot.tsx | 4 +- packages/x-charts/src/PieChart/PieArcPlot.tsx | 4 +- .../src/PieChart/dataTransform/transition.ts | 91 ++++++++++--------- 9 files changed, 68 insertions(+), 52 deletions(-) diff --git a/docs/data/charts/getting-started/getting-started.md b/docs/data/charts/getting-started/getting-started.md index 7efdf925eebfa..64eddde9f28f1 100644 --- a/docs/data/charts/getting-started/getting-started.md +++ b/docs/data/charts/getting-started/getting-started.md @@ -92,6 +92,15 @@ The outer margin space is where information like axes, titles, and legends are d See the [Styling documentation](/x/react-charts/styling/#placement) for complete details. +## Server-side rendering + +The chart support server-side rendering under two conditions: + +1. The `width` and `height` props needs to be provided. +2. The animation should be disabled with the `skipAnimation` prop. + +The reason is that it is not possible to compute the SVG dimensions on the server, and the `skipAnimation` ensures that the animation is not in an "empty" state when first rendering. + ## Axis management MUIĀ X Charts take a flexible approach to axis management, with support for multiple axes and any combination of scales and ranges. diff --git a/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx b/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx index c11af816ae046..1f42747bc6e2d 100644 --- a/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx +++ b/packages/x-charts/src/BarChart/BarLabel/BarLabelPlot.tsx @@ -40,7 +40,7 @@ function BarLabelPlot(props: BarLabelPlotProps) { const barLabelTransition = useTransition(bars, { keys: (bar) => `${bar.seriesId}-${bar.dataIndex}`, - from: leaveStyle, + from: skipAnimation ? undefined : leaveStyle, leave: null, enter: enterStyle, update: enterStyle, diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx index 4c3df0c27f52b..71baa7d972c81 100644 --- a/packages/x-charts/src/BarChart/BarPlot.tsx +++ b/packages/x-charts/src/BarChart/BarPlot.tsx @@ -265,7 +265,7 @@ function BarPlot(props: BarPlotProps) { const transition = useTransition(completedData, { keys: (bar) => `${bar.seriesId}-${bar.dataIndex}`, - from: leaveStyle, + from: skipAnimation ? undefined : leaveStyle, leave: leaveStyle, enter: enterStyle, update: enterStyle, @@ -274,7 +274,7 @@ function BarPlot(props: BarPlotProps) { const maskTransition = useTransition(withoutBorderRadius ? [] : masksData, { keys: (v) => v.id, - from: leaveStyle, + from: skipAnimation ? undefined : leaveStyle, leave: leaveStyle, enter: enterStyle, update: enterStyle, diff --git a/packages/x-charts/src/LineChart/AnimatedArea.tsx b/packages/x-charts/src/LineChart/AnimatedArea.tsx index ade2f449880a4..35f1bbac08ce4 100644 --- a/packages/x-charts/src/LineChart/AnimatedArea.tsx +++ b/packages/x-charts/src/LineChart/AnimatedArea.tsx @@ -32,7 +32,7 @@ function AnimatedArea(props: AnimatedAreaProps) { const stringInterpolator = useStringInterpolator(d); const transitionChange = useTransition([stringInterpolator], { - from: { value: 0 }, + from: skipAnimation ? undefined : { value: 0 }, to: { value: 1 }, enter: { value: 1 }, reset: false, diff --git a/packages/x-charts/src/LineChart/AnimatedLine.tsx b/packages/x-charts/src/LineChart/AnimatedLine.tsx index df67b20e93fda..47f7d57716bbf 100644 --- a/packages/x-charts/src/LineChart/AnimatedLine.tsx +++ b/packages/x-charts/src/LineChart/AnimatedLine.tsx @@ -32,7 +32,7 @@ function AnimatedLine(props: AnimatedLineProps) { const stringInterpolator = useStringInterpolator(d); const transitionChange = useTransition([stringInterpolator], { - from: { value: 0 }, + from: skipAnimation ? undefined : { value: 0 }, to: { value: 1 }, enter: { value: 1 }, reset: false, diff --git a/packages/x-charts/src/LineChart/AppearingMask.tsx b/packages/x-charts/src/LineChart/AppearingMask.tsx index 6121e2e215502..4c919c3414621 100644 --- a/packages/x-charts/src/LineChart/AppearingMask.tsx +++ b/packages/x-charts/src/LineChart/AppearingMask.tsx @@ -21,7 +21,7 @@ export function AppearingMask(props: AppearingMaskProps) { const transitionAppear = useTransition( [drawingArea], { - from: (v) => ({ animatedWidth: v.left }), + from: props.skipAnimation ? undefined : (v) => ({ animatedWidth: v.left }), enter: (v) => ({ animatedWidth: v.width + v.left + v.right }), leave: (v) => ({ animatedWidth: v.width + v.left + v.right }), reset: false, diff --git a/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx b/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx index a2adde4ea9e7f..66b048e46b5bc 100644 --- a/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx +++ b/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx @@ -8,7 +8,7 @@ import { DefaultizedPieValueType, PieSeriesType, } from '../models/seriesType/pie'; -import { defaultLabelTransitionConfig } from './dataTransform/transition'; +import { getDefaultLabelTransitionConfig } from './dataTransform/transition'; import { AnimatedObject, ValueWithHighlight, @@ -121,7 +121,7 @@ function PieArcLabelPlot(props: PieArcLabelPlotProps) { data, }); const transition = useTransition(transformedData, { - ...defaultLabelTransitionConfig, + ...getDefaultLabelTransitionConfig(skipAnimation), immediate: skipAnimation, }); diff --git a/packages/x-charts/src/PieChart/PieArcPlot.tsx b/packages/x-charts/src/PieChart/PieArcPlot.tsx index 2c43b0879966b..9ae657021d414 100644 --- a/packages/x-charts/src/PieChart/PieArcPlot.tsx +++ b/packages/x-charts/src/PieChart/PieArcPlot.tsx @@ -9,7 +9,7 @@ import { DefaultizedPieValueType, PieItemIdentifier, } from '../models/seriesType/pie'; -import { defaultTransitionConfig } from './dataTransform/transition'; +import { getDefaultTransitionConfig } from './dataTransform/transition'; import { AnimatedObject, ValueWithHighlight, @@ -91,7 +91,7 @@ function PieArcPlot(props: PieArcPlotProps) { data, }); const transition = useTransition(transformedData, { - ...defaultTransitionConfig, + ...getDefaultTransitionConfig(skipAnimation), immediate: skipAnimation, }); diff --git a/packages/x-charts/src/PieChart/dataTransform/transition.ts b/packages/x-charts/src/PieChart/dataTransform/transition.ts index 85eafa46cdd19..9bf329faba708 100644 --- a/packages/x-charts/src/PieChart/dataTransform/transition.ts +++ b/packages/x-charts/src/PieChart/dataTransform/transition.ts @@ -1,27 +1,31 @@ import { UseTransitionProps } from '@react-spring/web'; import { ValueWithHighlight } from './useTransformData'; -export const defaultTransitionConfig: UseTransitionProps = { +export const getDefaultTransitionConfig = ( + skipAnimation?: boolean, +): UseTransitionProps => ({ keys: (item) => item.id, - from: ({ - innerRadius, - outerRadius, - cornerRadius, - startAngle, - endAngle, - paddingAngle, - color, - isFaded, - }) => ({ - innerRadius, - outerRadius: (innerRadius + outerRadius) / 2, - cornerRadius, - startAngle: (startAngle + endAngle) / 2, - endAngle: (startAngle + endAngle) / 2, - paddingAngle, - fill: color, - opacity: isFaded ? 0.3 : 1, - }), + from: skipAnimation + ? undefined + : ({ + innerRadius, + outerRadius, + cornerRadius, + startAngle, + endAngle, + paddingAngle, + color, + isFaded, + }) => ({ + innerRadius, + outerRadius: (innerRadius + outerRadius) / 2, + cornerRadius, + startAngle: (startAngle + endAngle) / 2, + endAngle: (startAngle + endAngle) / 2, + paddingAngle, + fill: color, + opacity: isFaded ? 0.3 : 1, + }), leave: ({ innerRadius, startAngle, endAngle }) => ({ innerRadius, outerRadius: innerRadius, @@ -31,7 +35,6 @@ export const defaultTransitionConfig: UseTransitionProps = { enter: ({ innerRadius, outerRadius, startAngle, endAngle }) => ({ innerRadius, outerRadius, - startAngle, endAngle, }), @@ -59,28 +62,32 @@ export const defaultTransitionConfig: UseTransitionProps = { friction: 14, clamp: true, }, -}; +}); -export const defaultLabelTransitionConfig: UseTransitionProps = { +export const getDefaultLabelTransitionConfig = ( + skipAnimation?: boolean, +): UseTransitionProps => ({ keys: (item) => item.id, - from: ({ - innerRadius, - outerRadius, - arcLabelRadius, - cornerRadius, - startAngle, - endAngle, - paddingAngle, - }) => ({ - innerRadius, - outerRadius: (innerRadius + outerRadius) / 2, - cornerRadius, - arcLabelRadius, - startAngle: (startAngle + endAngle) / 2, - endAngle: (startAngle + endAngle) / 2, - paddingAngle, - opacity: 0, - }), + from: skipAnimation + ? undefined + : ({ + innerRadius, + outerRadius, + arcLabelRadius, + cornerRadius, + startAngle, + endAngle, + paddingAngle, + }) => ({ + innerRadius, + outerRadius: (innerRadius + outerRadius) / 2, + cornerRadius, + arcLabelRadius, + startAngle: (startAngle + endAngle) / 2, + endAngle: (startAngle + endAngle) / 2, + paddingAngle, + opacity: 0, + }), leave: ({ innerRadius, startAngle, endAngle }) => ({ innerRadius, outerRadius: innerRadius, @@ -120,4 +127,4 @@ export const defaultLabelTransitionConfig: UseTransitionProps