From 8f6220707b297d467b85997afcd8d222187df478 Mon Sep 17 00:00:00 2001 From: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com> Date: Wed, 8 Jan 2025 08:35:08 +0000 Subject: [PATCH] Dashboards: Panels react to `timeFrom` and `timeShift` changes using variables (#98510) --- .../scene/PanelTimeRange.test.tsx | 39 ++++++++++++++++++- .../dashboard-scene/scene/PanelTimeRange.tsx | 16 ++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/PanelTimeRange.test.tsx b/public/app/features/dashboard-scene/scene/PanelTimeRange.test.tsx index 28fae4414ca..1f8c08cdff0 100644 --- a/public/app/features/dashboard-scene/scene/PanelTimeRange.test.tsx +++ b/public/app/features/dashboard-scene/scene/PanelTimeRange.test.tsx @@ -1,7 +1,14 @@ import { advanceTo, clear } from 'jest-date-mock'; import { dateTime } from '@grafana/data'; -import { SceneCanvasText, SceneFlexItem, SceneFlexLayout, SceneTimeRange } from '@grafana/scenes'; +import { + SceneCanvasText, + SceneFlexItem, + SceneFlexLayout, + SceneTimeRange, + SceneVariableSet, + TestVariable, +} from '@grafana/scenes'; import { activateFullSceneTree } from '../utils/test-utils'; @@ -47,6 +54,36 @@ describe('PanelTimeRange', () => { expect(panelTime.state.value.from.toISOString()).toBe('2019-02-11T15:00:00.000Z'); expect(panelTime.state.timeInfo).toBe('Last 2 hours timeshift -2h'); }); + + it('should update timeInfo when timeShift and timeFrom are variable expressions', async () => { + const customTimeFrom = new TestVariable({ + name: 'testFrom', + value: '10s', + }); + const customTimeShift = new TestVariable({ + name: 'testShift', + value: '20s', + }); + const panelTime = new PanelTimeRange({ timeFrom: '$testFrom', timeShift: '$testShift' }); + const panel = new SceneCanvasText({ text: 'Hello', $timeRange: panelTime }); + const scene = new SceneFlexLayout({ + $variables: new SceneVariableSet({ + variables: [customTimeFrom, customTimeShift], + }), + $timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }), + children: [new SceneFlexItem({ body: panel })], + }); + activateFullSceneTree(scene); + + expect(panelTime.state.timeInfo).toBe('Last 10 seconds timeshift -20s'); + + customTimeFrom.setState({ value: '15s' }); + customTimeShift.setState({ value: '25s' }); + + panelTime.forceRender(); + + expect(panelTime.state.timeInfo).toBe('Last 15 seconds timeshift -25s'); + }); }); function buildAndActivateSceneFor(panelTime: PanelTimeRange) { diff --git a/public/app/features/dashboard-scene/scene/PanelTimeRange.tsx b/public/app/features/dashboard-scene/scene/PanelTimeRange.tsx index 372327eb58d..f6e5287c93e 100644 --- a/public/app/features/dashboard-scene/scene/PanelTimeRange.tsx +++ b/public/app/features/dashboard-scene/scene/PanelTimeRange.tsx @@ -7,6 +7,7 @@ import { SceneTimeRangeLike, SceneTimeRangeState, SceneTimeRangeTransformerBase, + VariableDependencyConfig, } from '@grafana/scenes'; import { Icon, PanelChrome, TimePickerTooltip, Tooltip, useStyles2 } from '@grafana/ui'; import { TimeOverrideResult } from 'app/features/dashboard/utils/panel'; @@ -33,12 +34,19 @@ export class PanelTimeRange extends SceneTimeRangeTransformerBase this._onActivate()); } + protected _variableDependency: VariableDependencyConfig = new VariableDependencyConfig(this, { + statePaths: ['timeFrom', 'timeShift'], + }); + private _onActivate() { this._subs.add( - this.subscribeToState((n, p) => { - // Listen to own changes and update time info when required - if (n.timeFrom !== p.timeFrom || n.timeShift !== p.timeShift) { - const { timeInfo, timeRange } = this.getTimeOverride(this.getAncestorTimeRange().state.value); + this.subscribeToState((n) => { + const { timeInfo, timeRange } = this.getTimeOverride(this.getAncestorTimeRange().state.value); + + // When timeFrom or timeShift is a variable we cannot compare to previous interpolated value + // therefore we need to compare timeInfo directly and update when required + // Note: compare to newState.timeInfo because it is always one behind + if (n.timeInfo !== timeInfo) { this.setState({ timeInfo, value: timeRange }); } })