This is an automated email from the ASF dual-hosted git repository. EnxDev pushed a commit to branch fix/timeshift-line-chart-report-render in repository https://gitbox.apache.org/repos/asf/superset.git
commit 3e70b4472c47030ed4a6990fd70f731504d40e7d Author: Enzo Martellucci <[email protected]> AuthorDate: Mon Jul 13 17:43:46 2026 +0200 fix(chart-echarts): disable animation for report screenshots so time-shift lines render fully --- .../src/components/Echart.test.tsx | 53 +++++++++++++++++++++- .../plugin-chart-echarts/src/components/Echart.tsx | 30 +++++++++--- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.test.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.test.tsx index 110b4214fd6..f353fd2532d 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.test.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.test.tsx @@ -18,7 +18,7 @@ */ import { render, waitFor } from '../../../../spec/helpers/testing-library'; import type { EChartsCoreOption } from 'echarts/core'; -import Echart from './Echart'; +import Echart, { isReportScreenshotMode } from './Echart'; import type { EchartsProps } from '../types'; type Handler = (params: unknown) => void; @@ -130,6 +130,20 @@ const trigger = (name: string) => { (listeners[name] || []).forEach(listener => listener.handler({})); }; +const originalLocation = `${window.location.pathname}${window.location.search}`; + +const setStandalone = (standalone?: string) => { + window.history.replaceState( + {}, + '', + standalone === undefined ? '/' : `/?standalone=${standalone}`, + ); +}; + +afterEach(() => { + window.history.replaceState({}, '', originalLocation); +}); + beforeEach(() => { Object.keys(listeners).forEach(name => { delete listeners[name]; @@ -221,3 +235,40 @@ test('replaces stale query event handlers without clearing regular event handler expect(firstQueryHandler).not.toHaveBeenCalled(); expect(secondQueryHandler).not.toHaveBeenCalled(); }); + +test.each([ + // Report/thumbnail screenshots render in standalone "true" (charts) or 3 (reports) + ['true', true], + ['3', true], + // Live embeds use 1/2 and must keep animation + ['1', false], + ['2', false], + ['0', false], + [undefined, false], +])( + 'isReportScreenshotMode() is %p for standalone=%p', + (standalone, expected) => { + setStandalone(standalone); + expect(isReportScreenshotMode()).toBe(expected); + }, +); + +test('disables animation when rendering in report screenshot mode', async () => { + setStandalone('true'); + render(renderEchart(), { initialState, useRedux: true }); + + await waitFor(() => expect(mockChart.setOption).toHaveBeenCalled()); + + const lastOptions = mockChart.setOption.mock.calls.at(-1)?.[0]; + expect(lastOptions.animation).toBe(false); +}); + +test('keeps animation enabled when not in report screenshot mode', async () => { + setStandalone(undefined); + render(renderEchart(), { initialState, useRedux: true }); + + await waitFor(() => expect(mockChart.setOption).toHaveBeenCalled()); + + const lastOptions = mockChart.setOption.mock.calls.at(-1)?.[0]; + expect(lastOptions.animation).not.toBe(false); +}); diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx index 52111fd4f86..43fc7bf2bb0 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx @@ -131,6 +131,19 @@ const loadLocale = async (locale: string) => { return lang?.default; }; +// Report/thumbnail screenshots use standalone="true" (charts) or 3 (reports); +// live embeds use 1/2 and keep animation. See superset/utils/screenshots.py. +export function isReportScreenshotMode(): boolean { + try { + const standalone = new URLSearchParams(window.location.search).get( + 'standalone', + ); + return standalone === 'true' || standalone === '3'; + } catch { + return false; + } +} + function Echart( { width, @@ -278,13 +291,16 @@ function Echart( ? theme.echartsOptionsOverridesByChartType?.[vizType] || {} : {}; - // Disable animations during auto-refresh to reduce visual noise - const animationOverride = isDashboardRefreshing - ? { - animation: false, - animationDuration: 0, - } - : {}; + // Disable animation on auto-refresh and screenshots. Screenshots have no + // "render finished" signal, so a running draw can be captured mid-frame, + // producing partial/blank charts. + const animationOverride = + isDashboardRefreshing || isReportScreenshotMode() + ? { + animation: false, + animationDuration: 0, + } + : {}; const themedEchartOptions = mergeEchartsThemeOverrides( baseTheme,
