[v11.4.x] Dashboards: Panel react for timeFrom and timeShift changes using variables (#98659)

* Dashboards: Panels react to `timeFrom` and `timeShift` changes using variables (#98510)

(cherry picked from commit 8f6220707b)

* restore problematic file

* add the required test

---------

Co-authored-by: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com>
Co-authored-by: Sergej-Vlasov <sergej.s.vlasov@gmail.com>
This commit is contained in:
grafana-delivery-bot[bot]
2025-08-05 15:02:40 +01:00
committed by GitHub
co-authored by Sergej-Vlasov Sergej-Vlasov
parent 751b438915
commit 5594687921
2 changed files with 50 additions and 5 deletions
@@ -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';
@@ -60,6 +67,36 @@ describe('PanelTimeRange', () => {
expect(panelTime.state.from).toBe('now-12h');
expect(panelTime.state.to).toBe('now-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) {
@@ -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<PanelTimeRange
this.addActivationHandler(() => this._onActivate());
}
protected _variableDependency: VariableDependencyConfig<PanelTimeRangeState> = 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 });
}
})