diff --git a/.betterer.results b/.betterer.results index 65afa3201e8..3116e9fa592 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3008,6 +3008,9 @@ exports[`better eslint`] = { "public/app/features/dashboard/state/actions.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "public/app/features/dashboard/state/getPanelPluginToMigrateTo.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "public/app/features/dashboard/state/initDashboard.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], diff --git a/devenv/dev-dashboards/migrations/migrations.json b/devenv/dev-dashboards/migrations/migrations.json index 27979e11172..3d13fe327f6 100644 --- a/devenv/dev-dashboards/migrations/migrations.json +++ b/devenv/dev-dashboards/migrations/migrations.json @@ -241,7 +241,7 @@ } }, "percentage": false, - "pluginVersion": "10.4.0-pre", + "pluginVersion": "11.0.0-pre", "pointradius": 2, "points": false, "renderer": "flot", @@ -311,7 +311,281 @@ "content": "# Graph panel >> Timeseries panel\n\nKnown issues:\n* hiding null/empty series\n* time regions", "mode": "markdown" }, - "pluginVersion": "10.4.0-pre", + "pluginVersion": "11.0.0-pre", + "targets": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A" + } + ], + "title": "Status + Notes", + "type": "text" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "unit": "short" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 16, + "x": 0, + "y": 11 + }, + "hiddenSeries": false, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "percentage": false, + "pluginVersion": "11.0.0-pre", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 3 + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Flot graph - x axis series mode", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "series", + "show": true, + "values": [ + "total" + ] + }, + "yaxes": [ + { + "$$hashKey": "object:88", + "format": "short", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:89", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 16, + "y": 11 + }, + "id": 29, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "# Graph panel >> Bar chart panel\n", + "mode": "markdown" + }, + "pluginVersion": "11.0.0-pre", + "targets": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A" + } + ], + "title": "Status + Notes", + "type": "text" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "unit": "short" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 16, + "x": 0, + "y": 22 + }, + "hiddenSeries": false, + "id": 30, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": false, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "percentage": false, + "pluginVersion": "11.0.0-pre", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 3 + } + ], + "thresholds": [], + "timeRegions": [], + "title": "Flot graph - x axis histogram mode", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "mode": "histogram", + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:193", + "format": "short", + "logBase": 1, + "show": true + }, + { + "$$hashKey": "object:194", + "format": "short", + "logBase": 1, + "show": true + } + ], + "yaxis": { + "align": false + } + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "gridPos": { + "h": 11, + "w": 8, + "x": 16, + "y": 22 + }, + "id": 31, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "# Graph panel >> Histogram panel\n", + "mode": "markdown" + }, + "pluginVersion": "11.0.0-pre", "targets": [ { "datasource": { @@ -335,7 +609,7 @@ "h": 10, "w": 16, "x": 0, - "y": 11 + "y": 33 }, "id": 2, "options": { @@ -411,7 +685,7 @@ "h": 10, "w": 8, "x": 16, - "y": 11 + "y": 33 }, "id": 7, "options": { @@ -423,7 +697,7 @@ "content": "# Table (old) >> Table\n\nKnown issues:\n* wrapping text\n* style changes", "mode": "markdown" }, - "pluginVersion": "10.4.0-pre", + "pluginVersion": "11.0.0-pre", "targets": [ { "datasource": { @@ -460,7 +734,7 @@ "h": 8, "w": 8, "x": 0, - "y": 21 + "y": 43 }, "id": 9, "mappingType": 1, @@ -517,64 +791,65 @@ "valueName": "avg" }, { - "colorBackground": false, - "colorValue": true, - "colors": [ - "#299c46", - "#73BF69", - "#d44a3a" - ], "datasource": { "type": "testdata", "uid": "PD8C576611E62080A" }, - "format": "ms", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true + "fieldConfig": { + "defaults": { + "mappings": [ + { + "options": { + "match": "null", + "result": { + "text": "N/A" + } + }, + "type": "special" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] }, "gridPos": { "h": 8, "w": 8, "x": 8, - "y": 21 + "y": 43 }, "id": 23, - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], "maxDataPoints": 100, - "nullPointMode": "connected", - "pluginVersion": "6.2.0-pre", - "postfix": "", - "postfixFontSize": "50%", - "prefix": "p95", - "prefixFontSize": "80%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true }, - "tableColumn": "", + "pluginVersion": "11.0.0-pre", "targets": [ { "datasource": { @@ -584,18 +859,8 @@ "refId": "A" } ], - "thresholds": "", "title": "singlestat (old, internal. Migrated if schema < 28)", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "avg" + "type": "stat" }, { "datasource": { @@ -606,7 +871,7 @@ "h": 8, "w": 8, "x": 16, - "y": 21 + "y": 43 }, "id": 10, "options": { @@ -618,7 +883,7 @@ "content": "# Singlestat >> Stat\n\nKnown issues:\n* limited options", "mode": "markdown" }, - "pluginVersion": "10.4.0-pre", + "pluginVersion": "11.0.0-pre", "targets": [ { "datasource": { @@ -640,7 +905,7 @@ "h": 10, "w": 16, "x": 0, - "y": 29 + "y": 51 }, "id": 24, "options": { @@ -693,7 +958,7 @@ "h": 10, "w": 8, "x": 16, - "y": 29 + "y": 51 }, "id": 25, "options": { @@ -705,7 +970,7 @@ "content": "# grafana-piechart-panel >> piechart\n\nKnown issues:\n* TBD", "mode": "markdown" }, - "pluginVersion": "10.4.0-pre", + "pluginVersion": "11.0.0-pre", "targets": [ { "datasource": { @@ -719,47 +984,35 @@ "type": "text" }, { + "circleMaxSize": 30, + "circleMinSize": 2, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], "datasource": { "type": "grafana-testdata-datasource", "uid": "PD8C576611E62080A" }, - "fieldConfig": { - "defaults": { - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)" - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 0 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - }, - "overrides": [] - }, + "decimals": 0, + "esMetric": "Count", "gridPos": { "h": 10, "w": 16, "x": 0, - "y": 39 + "y": 61 }, + "hideEmpty": false, + "hideZero": false, "id": 26, + "initialZoom": 1, + "locationData": "countries", + "mapCenter": "(0°, 0°)", + "mapCenterLatitude": 0, + "mapCenterLongitude": 0, "maxDataPoints": 1, + "mouseWheelZoom": false, "options": { "basemap": { "name": "Basemap", @@ -831,6 +1084,15 @@ } }, "pluginVersion": "10.4.0-pre", + "showLegend": true, + "stickyLabels": false, + "tableQueryOptions": { + "geohashField": "geohash", + "latitudeField": "latitude", + "longitudeField": "longitude", + "metricField": "metric", + "queryType": "geohash" + }, "targets": [ { "csvFileName": "flight_info_by_state.csv", @@ -842,6 +1104,7 @@ "scenarioId": "csv_file" } ], + "thresholds": "0,10", "title": "grafana-worldmap-panel", "transformations": [ { @@ -859,7 +1122,10 @@ } } ], - "type": "grafana-worldmap-panel" + "type": "grafana-worldmap-panel", + "unitPlural": "", + "unitSingle": "", + "valueName": "total" }, { "datasource": { @@ -870,7 +1136,7 @@ "h": 10, "w": 8, "x": 16, - "y": 39 + "y": 61 }, "id": 27, "options": { @@ -882,7 +1148,7 @@ "content": "# grafana-worldmap-panel >> geomap\n\nKnown issues:\n* TBD", "mode": "markdown" }, - "pluginVersion": "10.4.0-pre", + "pluginVersion": "11.0.0-pre", "targets": [ { "datasource": { @@ -910,10 +1176,11 @@ "from": "now-6h", "to": "now" }, + "timeRangeUpdatedDuringEditOrView": false, "timepicker": {}, "timezone": "", "title": "Devenv - Panel migrations", "uid": "cdd412c4", - "version": 67, + "version": 68, "weekStart": "" } \ No newline at end of file diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index 2ecd10085d7..4cb91f0c17f 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -71,9 +71,7 @@ export interface SaveModelToSceneOptions { export function transformSaveModelToScene(rsp: DashboardDTO): DashboardScene { // Just to have migrations run - const oldModel = new DashboardModel(rsp.dashboard, rsp.meta, { - autoMigrateOldPanels: false, - }); + const oldModel = new DashboardModel(rsp.dashboard, rsp.meta); const scene = createDashboardSceneFromDashboardModel(oldModel); // TODO: refactor createDashboardSceneFromDashboardModel to work on Dashboard schema model diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index f0afaba7660..35969d751c1 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -127,9 +127,6 @@ export class DashboardModel implements TimeModel { options?: { // By default this uses variables from redux state getVariablesFromState?: GetVariables; - - // Force the loader to migrate panels - autoMigrateOldPanels?: boolean; } ) { this.getVariablesFromState = options?.getVariablesFromState ?? getVariablesByKey; @@ -169,59 +166,6 @@ export class DashboardModel implements TimeModel { this.initMeta(meta); this.updateSchema(data); - // Auto-migrate old angular panels - const shouldMigrateAllAngularPanels = - options?.autoMigrateOldPanels || !config.angularSupportEnabled || config.featureToggles.autoMigrateOldPanels; - - const shouldMigrateExplicitAngularPanels = - config.featureToggles.autoMigrateGraphPanel || - config.featureToggles.autoMigrateTablePanel || - config.featureToggles.autoMigratePiechartPanel || - config.featureToggles.autoMigrateWorldmapPanel || - config.featureToggles.autoMigrateStatPanel; - - // Handles both granular and all angular panel migration - if (shouldMigrateAllAngularPanels || shouldMigrateExplicitAngularPanels) { - for (const panel of this.panelIterator()) { - if ( - !panel.autoMigrateFrom && - panel.type === 'graph' && - (config.featureToggles.autoMigrateGraphPanel || shouldMigrateAllAngularPanels) - ) { - panel.autoMigrateFrom = panel.type; - panel.type = 'timeseries'; - } else if ( - !panel.autoMigrateFrom && - panel.type === 'table-old' && - (config.featureToggles.autoMigrateTablePanel || shouldMigrateAllAngularPanels) - ) { - panel.autoMigrateFrom = panel.type; - panel.type = 'table'; - } else if ( - !panel.autoMigrateFrom && - panel.type === 'grafana-piechart-panel' && - (config.featureToggles.autoMigratePiechartPanel || shouldMigrateAllAngularPanels) - ) { - panel.autoMigrateFrom = panel.type; - panel.type = 'piechart'; - } else if ( - !panel.autoMigrateFrom && - panel.type === 'grafana-worldmap-panel' && - (config.featureToggles.autoMigrateWorldmapPanel || shouldMigrateAllAngularPanels) - ) { - panel.autoMigrateFrom = panel.type; - panel.type = 'geomap'; - } else if ( - !panel.autoMigrateFrom && - (panel.type === 'singlestat' || panel.type === 'grafana-singlestat-panel') && - (config.featureToggles.autoMigrateStatPanel || shouldMigrateAllAngularPanels) - ) { - panel.autoMigrateFrom = panel.type; - panel.type = 'stat'; - } - } - } - this.addBuiltInAnnotationQuery(); this.sortPanelsByGridPos(); this.panelsAffectedByVariableChange = null; diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index fcf1307a284..1b3a6b2fbba 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -36,6 +36,8 @@ import { import { PanelQueryRunner } from '../../query/state/PanelQueryRunner'; import { TimeOverrideResult } from '../utils/panel'; +import { getPanelPluginToMigrateTo } from './getPanelPluginToMigrateTo'; + export interface GridPos { x: number; y: number; @@ -148,7 +150,7 @@ export const autoMigrateAngular: Record = { 'grafana-worldmap-panel': 'geomap', }; -const autoMigratePanelType: Record = { +export const autoMigrateRemovedPanelPlugins: Record = { 'heatmap-new': 'heatmap', // this was a temporary development panel that is now standard }; @@ -257,7 +259,7 @@ export class PanelModel implements DataConfigSource, IPanelModel { (this as any)[property] = model[property]; } - const newType = autoMigratePanelType[this.type]; + const newType = getPanelPluginToMigrateTo(this); if (newType) { this.autoMigrateFrom = this.type; this.type = newType; diff --git a/public/app/features/dashboard/state/getPanelPluginToMigrateTo.ts b/public/app/features/dashboard/state/getPanelPluginToMigrateTo.ts new file mode 100644 index 00000000000..bfc5c32b13b --- /dev/null +++ b/public/app/features/dashboard/state/getPanelPluginToMigrateTo.ts @@ -0,0 +1,51 @@ +import config from 'app/core/config'; + +import { autoMigrateRemovedPanelPlugins, autoMigrateAngular } from './PanelModel'; + +export function getPanelPluginToMigrateTo(panel: any, forceMigration?: boolean): string | undefined { + if (autoMigrateRemovedPanelPlugins[panel.type]) { + return autoMigrateRemovedPanelPlugins[panel.type]; + } + + // Auto-migrate old angular panels + const shouldMigrateAllAngularPanels = + forceMigration || !config.angularSupportEnabled || config.featureToggles.autoMigrateOldPanels; + + // Graph needs special logic as it can be migrated to multiple panels + if (panel.type === 'graph' && (shouldMigrateAllAngularPanels || config.featureToggles.autoMigrateGraphPanel)) { + if (panel.xaxis?.mode === 'series') { + return 'barchart'; + } + + if (panel.xaxis?.mode === 'histogram') { + return 'histogram'; + } + + return 'timeseries'; + } + + if (shouldMigrateAllAngularPanels) { + return autoMigrateAngular[panel.type]; + } + + if (panel.type === 'table-old' && config.featureToggles.autoMigrateTablePanel) { + return 'table'; + } + + if (panel.type === 'grafana-piechart-panel' && config.featureToggles.autoMigratePiechartPanel) { + return 'piechart'; + } + + if (panel.type === 'grafana-worldmap-panel' && config.featureToggles.autoMigrateWorldmapPanel) { + return 'geomap'; + } + + if ( + (panel.type === 'singlestat' || panel.type === 'grafana-singlestat-panel') && + config.featureToggles.autoMigrateStatPanel + ) { + return 'stat'; + } + + return undefined; +} diff --git a/public/app/plugins/panel/barchart/migrations.test.ts b/public/app/plugins/panel/barchart/migrations.test.ts new file mode 100644 index 00000000000..00af18aec7b --- /dev/null +++ b/public/app/plugins/panel/barchart/migrations.test.ts @@ -0,0 +1,32 @@ +import { FieldConfigSource, PanelModel } from '@grafana/data'; + +import { changeToBarChartPanelMigrationHandler } from './migrations'; + +describe('Bar chart Migrations', () => { + let prevFieldConfig: FieldConfigSource; + + beforeEach(() => { + prevFieldConfig = { + defaults: {}, + overrides: [], + }; + }); + + it('From old graph', () => { + const old = { + angular: { + xaxis: { + mode: 'series', + values: 'avg', + }, + }, + }; + + const panel = {} as PanelModel; + panel.options = changeToBarChartPanelMigrationHandler(panel, 'graph', old, prevFieldConfig); + + const transform = panel.transformations![0]; + expect(transform.id).toBe('reduce'); + expect(transform.options.reducers).toBe('avg'); + }); +}); diff --git a/public/app/plugins/panel/barchart/migrations.ts b/public/app/plugins/panel/barchart/migrations.ts new file mode 100644 index 00000000000..4dba05de840 --- /dev/null +++ b/public/app/plugins/panel/barchart/migrations.ts @@ -0,0 +1,36 @@ +import { PanelTypeChangedHandler } from '@grafana/data'; + +/* + * This is called when the panel changes from another panel + */ +export const changeToBarChartPanelMigrationHandler: PanelTypeChangedHandler = ( + panel, + prevPluginId, + prevOptions, + prevFieldConfig +) => { + if (prevPluginId === 'graph') { + const graphOptions: GraphOptions = prevOptions.angular; + + if (graphOptions.xaxis?.mode === 'series') { + const tranformations = panel.transformations || []; + tranformations.push({ + id: 'reduce', + options: { + reducers: graphOptions.xaxis?.values ?? ['sum'], + }, + }); + + panel.transformations = tranformations; + } + } + + return {}; +}; + +interface GraphOptions { + xaxis: { + mode: 'series' | 'time' | 'histogram'; + values?: string[]; + }; +} diff --git a/public/app/plugins/panel/barchart/module.tsx b/public/app/plugins/panel/barchart/module.tsx index 21e23f6f60d..21bc79e7037 100644 --- a/public/app/plugins/panel/barchart/module.tsx +++ b/public/app/plugins/panel/barchart/module.tsx @@ -16,11 +16,13 @@ import { ThresholdsStyleEditor } from '../timeseries/ThresholdsStyleEditor'; import { BarChartPanel } from './BarChartPanel'; import { TickSpacingEditor } from './TickSpacingEditor'; +import { changeToBarChartPanelMigrationHandler } from './migrations'; import { FieldConfig, Options, defaultFieldConfig, defaultOptions } from './panelcfg.gen'; import { BarChartSuggestionsSupplier } from './suggestions'; import { prepareBarChartDisplayValues } from './utils'; export const plugin = new PanelPlugin(BarChartPanel) + .setPanelChangeHandler(changeToBarChartPanelMigrationHandler) .useFieldConfig({ standardOptions: { [FieldConfigProperty.Color]: { diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 0ba8b430085..14930db4f18 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -14,6 +14,7 @@ import { MetricsPanelCtrl } from 'app/angular/panel/metrics_panel_ctrl'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import { ThresholdMapper } from 'app/features/alerting/state/ThresholdMapper'; +import { getPanelPluginToMigrateTo } from 'app/features/dashboard/state/getPanelPluginToMigrateTo'; import { changePanelPlugin } from 'app/features/panel/state/actions'; import { dispatch } from 'app/store/store'; @@ -356,7 +357,8 @@ export class GraphCtrl extends MetricsPanelCtrl { }; migrateToReact() { - this.onPluginTypeChange(config.panels['timeseries']); + const panelType = getPanelPluginToMigrateTo(this.panel, true); + this.onPluginTypeChange(config.panels[panelType!]); } } diff --git a/public/app/plugins/panel/histogram/migrations.test.ts b/public/app/plugins/panel/histogram/migrations.test.ts new file mode 100644 index 00000000000..a0d2c5517cc --- /dev/null +++ b/public/app/plugins/panel/histogram/migrations.test.ts @@ -0,0 +1,28 @@ +import { FieldConfigSource, PanelModel } from '@grafana/data'; + +import { changeToHistogramPanelMigrationHandler } from './migrations'; + +describe('Histogram migrations', () => { + let prevFieldConfig: FieldConfigSource; + + beforeEach(() => { + prevFieldConfig = { + defaults: {}, + overrides: [], + }; + }); + + it('From old graph', () => { + const old = { + angular: { + xaxis: { + mode: 'histogram', + }, + }, + }; + + const panel = {} as PanelModel; + panel.options = changeToHistogramPanelMigrationHandler(panel, 'graph', old, prevFieldConfig); + expect(panel.options.combine).toBe(true); + }); +}); diff --git a/public/app/plugins/panel/histogram/migrations.ts b/public/app/plugins/panel/histogram/migrations.ts new file mode 100644 index 00000000000..215d85a9f56 --- /dev/null +++ b/public/app/plugins/panel/histogram/migrations.ts @@ -0,0 +1,30 @@ +import { PanelTypeChangedHandler } from '@grafana/data'; + +/* + * This is called when the panel changes from another panel + */ +export const changeToHistogramPanelMigrationHandler: PanelTypeChangedHandler = ( + panel, + prevPluginId, + prevOptions, + prevFieldConfig +) => { + if (prevPluginId === 'graph') { + const graphOptions: GraphOptions = prevOptions.angular; + + if (graphOptions.xaxis?.mode === 'histogram') { + return { + combine: true, + }; + } + } + + return {}; +}; + +interface GraphOptions { + xaxis: { + mode: 'series' | 'time' | 'histogram'; + values?: string[]; + }; +} diff --git a/public/app/plugins/panel/histogram/module.tsx b/public/app/plugins/panel/histogram/module.tsx index 918462032ce..c2b26f6283f 100644 --- a/public/app/plugins/panel/histogram/module.tsx +++ b/public/app/plugins/panel/histogram/module.tsx @@ -3,10 +3,12 @@ import { histogramFieldInfo } from '@grafana/data/src/transformations/transforme import { commonOptionsBuilder, graphFieldOptions } from '@grafana/ui'; import { HistogramPanel } from './HistogramPanel'; +import { changeToHistogramPanelMigrationHandler } from './migrations'; import { FieldConfig, Options, defaultFieldConfig, defaultOptions } from './panelcfg.gen'; import { originalDataHasHistogram } from './utils'; export const plugin = new PanelPlugin(HistogramPanel) + .setPanelChangeHandler(changeToHistogramPanelMigrationHandler) .setPanelOptions((builder) => { builder .addCustomEditor({