From bf9a34f2ca81fdec3c4be74f5e63a9fe6bbf5102 Mon Sep 17 00:00:00 2001 From: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:45:24 +0000 Subject: [PATCH] TimePicker: Ability to manually specify quick ranges (#101465) * add quickRanges to scene model transforms * add test for quickRanges transformations * fix lint --- .../src/schema/dashboard/v2alpha0/examples.ts | 12 + .../transformSceneToSaveModel.test.ts.snap | 375 ++++++++++++++++++ .../transformSaveModelSchemaV2ToScene.test.ts | 4 +- .../transformSaveModelSchemaV2ToScene.ts | 4 +- .../transformSaveModelToScene.test.ts | 30 ++ .../transformSaveModelToScene.ts | 4 +- .../transformSceneToSaveModel.test.ts | 27 ++ .../transformSceneToSaveModel.ts | 1 + .../transformSceneToSaveModelSchemaV2.test.ts | 12 + .../transformSceneToSaveModelSchemaV2.ts | 1 + 10 files changed, 465 insertions(+), 5 deletions(-) diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts index c12f1cc73f7..fe92defe024 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha0/examples.ts @@ -18,6 +18,18 @@ export const handyTestingSchema: DashboardV2Spec = { timezone: 'UTC', to: 'now', weekStart: 'monday', + quickRanges: [ + { + display: 'Last 6 hours', + from: 'now-6h', + to: 'now', + }, + { + display: 'Last 3 days', + from: 'now-3d', + to: 'now', + }, + ], }, annotations: [ { diff --git a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap index 585b51daf4e..eaf887a176b 100644 --- a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap +++ b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap @@ -1,5 +1,380 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Given a scene with custom quick ranges should save quick ranges to save model 1`] = ` +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana", + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard", + }, + { + "datasource": { + "type": "testdata", + "uid": "gdev-testdata", + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "Enabled", + "target": { + "lines": 4, + "refId": "Anno", + "scenarioId": "annotations", + }, + }, + { + "datasource": { + "type": "testdata", + "uid": "gdev-testdata", + }, + "enable": false, + "hide": false, + "iconColor": "yellow", + "name": "Disabled", + "target": { + "lines": 5, + "refId": "Anno", + "scenarioId": "annotations", + }, + }, + { + "datasource": { + "type": "testdata", + "uid": "gdev-testdata", + }, + "enable": true, + "hide": true, + "iconColor": "dark-purple", + "name": "Hidden", + "target": { + "lines": 6, + "refId": "Anno", + "scenarioId": "annotations", + }, + }, + ], + }, + "editable": true, + "fiscalYearStartMonth": 1, + "graphTooltip": 1, + "id": 1351, + "links": [], + "panels": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, + "description": "This is a simple time series graph", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic", + }, + "custom": { + "fillOpacity": 0, + "gradientMode": "none", + "lineWidth": 2, + }, + }, + "overrides": [], + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0, + }, + "id": 28, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true, + }, + "tooltip": { + "mode": "single", + "sort": "none", + }, + }, + "targets": [ + { + "alias": "series", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + }, + ], + "title": "Simple time series graph ", + "type": "timeseries", + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8, + }, + "id": 5, + "panels": [], + "title": "Row title", + "type": "row", + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, + "fieldConfig": { + "defaults": {}, + "overrides": [], + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 9, + }, + "id": 29, + "options": {}, + "targets": [ + { + "alias": "series", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + }, + ], + "title": "panel inside row", + "type": "timeseries", + }, + { + "fieldConfig": { + "defaults": {}, + "overrides": [], + }, + "gridPos": { + "h": 10, + "w": 11, + "x": 12, + "y": 9, + }, + "id": 25, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false, + }, + "content": "content", + "mode": "markdown", + }, + "pluginVersion": "10.2.0-pre", + "title": "Transparent text panel", + "transparent": true, + "type": "text", + }, + ], + "preload": false, + "refresh": "", + "schemaVersion": 41, + "tags": [ + "gdev", + "graph-ng", + "demo", + ], + "templating": { + "list": [ + { + "baseFilters": [], + "datasource": { + "type": "prometheus", + "uid": "wc2AL7L7k", + }, + "filters": [], + "name": "Filters", + "type": "adhoc", + }, + { + "auto": true, + "auto_count": 30, + "auto_min": "10s", + "current": { + "text": "1m", + "value": "1m", + }, + "name": "intervalVar", + "options": [ + { + "selected": true, + "text": "1m", + "value": "1m", + }, + { + "selected": false, + "text": "10m", + "value": "10m", + }, + { + "selected": false, + "text": "30m", + "value": "30m", + }, + { + "selected": false, + "text": "1h", + "value": "1h", + }, + { + "selected": false, + "text": "6h", + "value": "6h", + }, + { + "selected": false, + "text": "12h", + "value": "12h", + }, + { + "selected": false, + "text": "1d", + "value": "1d", + }, + { + "selected": false, + "text": "7d", + "value": "7d", + }, + { + "selected": false, + "text": "14d", + "value": "14d", + }, + { + "selected": false, + "text": "30d", + "value": "30d", + }, + ], + "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d", + "refresh": 2, + "type": "interval", + }, + { + "current": { + "text": [ + "a", + ], + "value": [ + "a", + ], + }, + "includeAll": true, + "multi": true, + "name": "customVar", + "options": [], + "query": "a, b, c", + "type": "custom", + }, + { + "current": { + "text": "gdev-testdata", + "value": "PD8C576611E62080A", + }, + "includeAll": false, + "name": "dsVar", + "options": [], + "query": "grafana-testdata-datasource", + "refresh": 1, + "regex": "", + "type": "datasource", + }, + { + "current": { + "text": "A", + "value": "A", + }, + "definition": "*", + "includeAll": false, + "name": "query0", + "options": [], + "query": { + "query": "*", + "refId": "StandardVariableQuery", + }, + "refresh": 1, + "regex": "", + "type": "query", + }, + { + "current": { + "text": "test", + "value": "test", + }, + "hide": 2, + "name": "constant", + "query": "test", + "skipUrlSync": true, + "type": "constant", + }, + ], + }, + "time": { + "from": "now-5m", + "to": "now", + }, + "timepicker": { + "quick_ranges": [ + { + "display": "Last 6 hours", + "from": "now-6h", + "to": "now", + }, + { + "display": "Last 3 days", + "from": "now-3d", + "to": "now", + }, + ], + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d", + ], + }, + "timezone": "America/New_York", + "title": "Dashboard to load1", + "uid": "nP8rcffGkasd", + "version": 2, + "weekStart": "saturday", +} +`; + exports[`transformSceneToSaveModel Annotations should transform annotations to save model 1`] = ` [ { diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts index cf8308c90ca..e1bd8b6fe0f 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts @@ -131,6 +131,7 @@ describe('transformSaveModelSchemaV2ToScene', () => { expect(dashboardControls).toBeDefined(); expect(dashboardControls.state.refreshPicker.state.intervals).toEqual(time.autoRefreshIntervals); expect(dashboardControls.state.hideTimeControls).toBe(time.hideTimepicker); + expect(dashboardControls.state.timePicker.state.quickRanges).toEqual(dash.timeSettings.quickRanges); // Variables const variables = scene.state?.$variables; @@ -223,9 +224,6 @@ describe('transformSaveModelSchemaV2ToScene', () => { expect(dataLayers.state.annotationLayers[3].state.isEnabled).toBe(dash.annotations[3].spec.enable); expect(dataLayers.state.annotationLayers[3].state.isHidden).toBe(dash.annotations[3].spec.hide); - // To be implemented - // expect(timePicker.state.ranges).toEqual(dash.timeSettings.quickRanges); - // VizPanel const vizPanels = (scene.state.body as DashboardLayoutManager).getVizPanels(); expect(vizPanels).toHaveLength(3); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index 40b5b01fbf9..43219038d22 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -203,7 +203,9 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo { }); }); +describe('When creating a snapshot dashboard scene', () => { + it('should initialize a dashboard scene with SnapshotVariables', () => { + const dashboard = { + ...defaultDashboard, + title: 'With custom quick ranges', + uid: 'test-uid', + timepicker: { + ...defaultTimePickerConfig, + quick_ranges: [ + { + display: 'Last 6 hours', + from: 'now-6h', + to: 'now', + }, + { + display: 'Last 3 days', + from: 'now-3d', + to: 'now', + }, + ], + }, + }; + + const oldModel = new DashboardModel(dashboard); + const scene = createDashboardSceneFromDashboardModel(oldModel, dashboard); + + expect(scene.state.controls?.state.timePicker.state.quickRanges).toBe(dashboard.timepicker.quick_ranges); + }); +}); + function buildGridItemForTest(saveModel: Partial): { gridItem: DashboardGridItem; vizPanel: VizPanel } { const gridItem = buildGridItemForPanel(new PanelModel(saveModel)); if (gridItem instanceof DashboardGridItem) { diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index b271697a87e..cd65a502b7c 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -283,7 +283,9 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, $data: new DashboardDataLayerSet({ annotationLayers, alertStatesLayer }), controls: new DashboardControls({ variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()], - timePicker: new SceneTimePicker({}), + timePicker: new SceneTimePicker({ + quickRanges: oldModel.timepicker.quick_ranges, + }), refreshPicker: new SceneRefreshPicker({ refresh: oldModel.refresh, intervals: oldModel.timepicker.refresh_intervals, diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts index d9576c47fd0..9d6061e3c6f 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts @@ -1057,6 +1057,33 @@ describe('transformSceneToSaveModel', () => { }); }); +describe('Given a scene with custom quick ranges', () => { + it('should save quick ranges to save model', () => { + const dashboardWithCustomSettings = { + ...dashboard_to_load1, + timepicker: { + ...dashboard_to_load1.timepicker, + quick_ranges: [ + { + display: 'Last 6 hours', + from: 'now-6h', + to: 'now', + }, + { + display: 'Last 3 days', + from: 'now-3d', + to: 'now', + }, + ], + }, + }; + const scene = transformSaveModelToScene({ dashboard: dashboardWithCustomSettings as DashboardDataDTO, meta: {} }); + const saveModel = transformSceneToSaveModel(scene); + + expect(saveModel).toMatchSnapshot(); + }); +}); + export function buildGridItemFromPanelSchema(panel: Partial) { return buildGridItemForPanel(new PanelModel(panel)); } diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index 89710e4c7f1..b9f280b4a42 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -100,6 +100,7 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa refresh_intervals: refreshPicker?.state.intervals, hidden: controlsState?.hideTimeControls, nowDelay: timeRange.UNSAFE_nowDelay, + quick_ranges: controlsState?.timePicker.state.quickRanges, }, defaultTimePickerConfig ); diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.test.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.test.ts index 050565bc711..7e438b72b56 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.test.ts @@ -404,6 +404,18 @@ function getMinimalSceneState(body: DashboardLayoutManager): Partial