From dd8bf2afa86f83264571342ed3abc6847b13ca7d Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Wed, 2 Jun 2021 15:45:36 -0400 Subject: [PATCH] State-timeline: support migrating from discrete panel (#35071) (#35134) (cherry picked from commit a9b5aa1088947ed480e4546bf902d3d6363804fd) Co-authored-by: Ryan McKinley --- .../__snapshots__/migrations.test.ts.snap | 67 ++++++++ .../panel/state-timeline/migrations.test.ts | 154 ++++++++++++++++++ .../panel/state-timeline/migrations.ts | 80 +++++++++ .../plugins/panel/state-timeline/module.tsx | 2 + 4 files changed, 303 insertions(+) create mode 100644 public/app/plugins/panel/state-timeline/__snapshots__/migrations.test.ts.snap create mode 100644 public/app/plugins/panel/state-timeline/migrations.test.ts create mode 100644 public/app/plugins/panel/state-timeline/migrations.ts diff --git a/public/app/plugins/panel/state-timeline/__snapshots__/migrations.test.ts.snap b/public/app/plugins/panel/state-timeline/__snapshots__/migrations.test.ts.snap new file mode 100644 index 00000000000..38b0ff20b6d --- /dev/null +++ b/public/app/plugins/panel/state-timeline/__snapshots__/migrations.test.ts.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Timeline Migrations from discrete panel 1`] = ` +Object { + "fieldConfig": Object { + "defaults": Object { + "custom": Object { + "fillOpacity": 100, + "lineWidth": 0, + }, + "mappings": Array [ + Object { + "options": Object { + "1": Object { + "color": "#7EB26D", + }, + "111": Object { + "text": "ONE", + }, + "20": Object { + "color": "#EAB839", + }, + "222": Object { + "text": "TWO", + }, + "30": Object { + "color": "#EF843C", + }, + "5": Object { + "color": "#E24D42", + }, + "90": Object { + "color": "#6ED0E0", + }, + }, + "type": "value", + }, + Object { + "options": Object { + "from": 1, + "result": Object { + "text": "AAA", + }, + "to": 3, + }, + "type": "range", + }, + Object { + "options": Object { + "from": 4, + "result": Object { + "text": "BBB", + }, + "to": 5, + }, + "type": "range", + }, + ], + "unit": "currencyGBP", + }, + "overrides": Array [], + }, + "options": Object { + "mergeValues": true, + }, +} +`; diff --git a/public/app/plugins/panel/state-timeline/migrations.test.ts b/public/app/plugins/panel/state-timeline/migrations.test.ts new file mode 100644 index 00000000000..e93d6aa067b --- /dev/null +++ b/public/app/plugins/panel/state-timeline/migrations.test.ts @@ -0,0 +1,154 @@ +import { PanelModel } from 'app/features/dashboard/state'; +import { timelinePanelChangedHandler } from './migrations'; + +describe('Timeline Migrations', () => { + it('from discrete panel', () => { + const panel = {} as PanelModel; + panel.options = timelinePanelChangedHandler(panel, 'natel-discrete-panel', { angular: discreteInV8 }); + expect(panel).toMatchSnapshot(); + }); +}); + +const discreteInV8 = { + id: 23763571993, + gridPos: { + h: 8, + w: 12, + x: 0, + y: 0, + }, + type: 'natel-discrete-panel', + title: 'Panel Title', + backgroundColor: 'rgba(128,128,128,0.1)', + colorMaps: [ + { + $$hashKey: 'object:365', + color: '#7EB26D', + text: '1', + }, + { + $$hashKey: 'object:366', + color: '#EAB839', + text: '20', + }, + { + $$hashKey: 'object:367', + color: '#6ED0E0', + text: '90', + }, + { + $$hashKey: 'object:368', + color: '#EF843C', + text: '30', + }, + { + $$hashKey: 'object:369', + color: '#E24D42', + text: '5', + }, + ], + crosshairColor: '#8F070C', + display: 'timeline', + extendLastValue: true, + highlightOnMouseover: true, + legendSortBy: '-ms', + lineColor: 'rgba(0,0,0,0.1)', + metricNameColor: '#000000', + rangeMaps: [ + { + $$hashKey: 'object:267', + from: '1', + text: 'AAA', + to: '3', + }, + { + from: '4', + to: '5', + text: 'BBB', + $$hashKey: 'object:544', + }, + ], + rowHeight: 50, + showLegend: true, + showLegendNames: true, + showLegendPercent: true, + showLegendValues: true, + showTimeAxis: true, + targets: [ + { + refId: 'A', + scenarioId: 'csv_metric_values', + stringInput: '1,20,90,30,5,0', + }, + { + scenarioId: 'csv_metric_values', + refId: 'B', + stringInput: '1,20,30,5,0', + hide: false, + }, + ], + textSize: 24, + textSizeTime: 12, + timeOptions: [ + { + name: 'Years', + value: 'years', + }, + { + name: 'Months', + value: 'months', + }, + { + name: 'Weeks', + value: 'weeks', + }, + { + name: 'Days', + value: 'days', + }, + { + name: 'Hours', + value: 'hours', + }, + { + name: 'Minutes', + value: 'minutes', + }, + { + name: 'Seconds', + value: 'seconds', + }, + { + name: 'Milliseconds', + value: 'milliseconds', + }, + ], + timePrecision: { + name: 'Minutes', + value: 'minutes', + }, + timeTextColor: '#d8d9da', + units: 'currencyGBP', + valueMaps: [ + { + $$hashKey: 'object:265', + op: '=', + text: 'ONE', + value: '111', + }, + { + value: '222', + op: '=', + text: 'TWO', + $$hashKey: 'object:546', + }, + ], + valueTextColor: '#000000', + writeLastValue: true, + expandFromQueryS: 0, + use12HourClock: false, + useTimePrecision: false, + writeAllValues: false, + writeMetricNames: false, + datasource: null, +}; diff --git a/public/app/plugins/panel/state-timeline/migrations.ts b/public/app/plugins/panel/state-timeline/migrations.ts new file mode 100644 index 00000000000..83a3f1ef46f --- /dev/null +++ b/public/app/plugins/panel/state-timeline/migrations.ts @@ -0,0 +1,80 @@ +import { FieldConfigSource, MappingType, PanelModel, ValueMap } from '@grafana/data'; +import { TimelineFieldConfig, TimelineOptions } from './types'; +import { isArray } from 'lodash'; + +// This is called when the panel changes from another panel +export const timelinePanelChangedHandler = ( + panel: PanelModel> | any, + prevPluginId: string, + prevOptions: any +) => { + let options = (panel.options ?? {}) as TimelineOptions; + + // Changing from angular singlestat + if (prevPluginId === 'natel-discrete-panel' && prevOptions.angular) { + const oldOptions = prevOptions.angular; + const fieldConfig: FieldConfigSource = panel.fieldConfig ?? { defaults: {}, overrides: [] }; + + if (oldOptions.units) { + fieldConfig.defaults.unit = oldOptions.units; + } + + const custom: TimelineFieldConfig = { + fillOpacity: 100, + lineWidth: 0, + }; + fieldConfig.defaults.custom = custom; + options.mergeValues = true; + + // Convert mappings + const valuemap: ValueMap = { type: MappingType.ValueToText, options: {} }; + fieldConfig.defaults.mappings = [valuemap]; + + if (isArray(oldOptions.colorMaps)) { + for (const p of oldOptions.colorMaps) { + const color = p.color as string; + if (color) { + valuemap.options[p.text as string] = { color }; + } + } + } + + if (isArray(oldOptions.valueMaps)) { + for (const p of oldOptions.valueMaps) { + const text = p.text as string; + const value = p.value as string; + if (text && value) { + let old = valuemap.options[value]; + if (old) { + old.text = text; + } else { + valuemap.options[value] = { text }; + } + } + } + } + + if (isArray(oldOptions.rangeMaps)) { + for (const p of oldOptions.rangeMaps) { + let from = +p.from; + let to = +p.to; + const text = p.text as string; + if (text) { + fieldConfig.defaults.mappings.push({ + type: MappingType.RangeToText, + options: { + from, + to, + result: { text }, + }, + }); + } + } + } + + // mutates the input + panel.fieldConfig = fieldConfig; + } + + return options; +}; diff --git a/public/app/plugins/panel/state-timeline/module.tsx b/public/app/plugins/panel/state-timeline/module.tsx index 00898b28886..64648ddcdc4 100755 --- a/public/app/plugins/panel/state-timeline/module.tsx +++ b/public/app/plugins/panel/state-timeline/module.tsx @@ -3,8 +3,10 @@ import { StateTimelinePanel } from './StateTimelinePanel'; import { TimelineOptions, TimelineFieldConfig, defaultPanelOptions, defaultTimelineFieldConfig } from './types'; import { BarValueVisibility } from '@grafana/ui'; import { addLegendOptions } from '@grafana/ui/src/options/builder'; +import { timelinePanelChangedHandler } from './migrations'; export const plugin = new PanelPlugin(StateTimelinePanel) + .setPanelChangeHandler(timelinePanelChangedHandler) .useFieldConfig({ standardOptions: { [FieldConfigProperty.Color]: {