mirror of
https://github.com/grafana/grafana.git
synced 2024-11-21 16:38:03 -06:00
AngularMigration: Migrate series Graph to BarGauge (#92609)
This commit is contained in:
parent
1cbe8772c2
commit
2b94a82baa
@ -389,9 +389,9 @@
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 3600000\n }\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {\n \"pod\": \"A-pod\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1727107111901,\n 1727110711901,\n 1727114311901,\n 1727117911901,\n 1727121511901,\n 1727125111901\n ],\n [\n 1.907286825122581,\n 2.260951647569786,\n 1.887442338051216,\n 2.1526144400893514,\n 1.7287721375237766,\n 1.7262902137793208\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 3600000\n }\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {\n \"pod\": \"A-pod1\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1727107111901,\n 1727110711901,\n 1727114311901,\n 1727117911901,\n 1727121511901,\n 1727125111901\n ],\n [\n 1.907286825122581,\n 1.589539045095202,\n 1.5914283506847613,\n 1.8976990616650726,\n 1.758223085999124,\n 2.2294649594813816\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 3600000\n }\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {\n \"pod\": \"A-pod2\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1727107111901,\n 1727110711901,\n 1727114311901,\n 1727117911901,\n 1727121511901,\n 1727125111901\n ],\n [\n 1.907286825122581,\n 2.0914263380328766,\n 1.8164545521094575,\n 1.621111084665713,\n 1.3902653996444705,\n 1.482803315949775\n ]\n ]\n }\n }\n]",
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk",
|
||||
"seriesCount": 3
|
||||
"scenarioId": "raw_frame"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
@ -462,6 +462,145 @@
|
||||
"title": "Status + Notes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"default": false,
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "short"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 16,
|
||||
"x": 0,
|
||||
"y": 22
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 32,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"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.3.0-pre",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Dashboard --"
|
||||
},
|
||||
"panelId": 28,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeRegions": [],
|
||||
"title": "Flot graph - x axis series mode (with legend calcs)",
|
||||
"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": 22
|
||||
},
|
||||
"id": 33,
|
||||
"options": {
|
||||
"code": {
|
||||
"language": "plaintext",
|
||||
"showLineNumbers": false,
|
||||
"showMiniMap": false
|
||||
},
|
||||
"content": "# Graph panel >> Bar gauge panel\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "11.3.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Status + Notes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
@ -1183,4 +1322,4 @@
|
||||
"uid": "cdd412c4",
|
||||
"version": 68,
|
||||
"weekStart": ""
|
||||
}
|
||||
}
|
||||
|
@ -249,3 +249,29 @@ describe('sharedSingleStatMigrationHandler', () => {
|
||||
expect(panel.fieldConfig.defaults.max).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BarGauge migrations', () => {
|
||||
it('Should migrate from old graph', () => {
|
||||
const old = {
|
||||
angular: {
|
||||
xaxis: {
|
||||
mode: 'series',
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
values: true,
|
||||
min: false,
|
||||
max: true,
|
||||
current: true,
|
||||
total: false,
|
||||
avg: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const panel = {} as PanelModel;
|
||||
panel.options = sharedSingleStatPanelChangedHandler(panel, 'graph', old);
|
||||
expect(panel.options.legend.showLegend).toBe(true);
|
||||
expect(panel.options.legend.calcs).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { cloneDeep, isNumber, omit } from 'lodash';
|
||||
import { cloneDeep, identity, isNumber, omit, pickBy } from 'lodash';
|
||||
|
||||
import {
|
||||
convertOldAngularValueMappings,
|
||||
@ -16,7 +16,7 @@ import {
|
||||
ValueMapping,
|
||||
VizOrientation,
|
||||
} from '@grafana/data';
|
||||
import { OptionsWithTextFormatting } from '@grafana/schema';
|
||||
import { LegendDisplayMode, OptionsWithLegend, OptionsWithTextFormatting } from '@grafana/schema';
|
||||
|
||||
export interface SingleStatBaseOptions extends OptionsWithTextFormatting {
|
||||
reduceOptions: ReduceDataOptions;
|
||||
@ -25,6 +25,7 @@ export interface SingleStatBaseOptions extends OptionsWithTextFormatting {
|
||||
|
||||
const optionsToKeep = ['reduceOptions', 'orientation'];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function sharedSingleStatPanelChangedHandler(
|
||||
panel: PanelModel<Partial<SingleStatBaseOptions>> | any,
|
||||
prevPluginId: string,
|
||||
@ -40,6 +41,9 @@ export function sharedSingleStatPanelChangedHandler(
|
||||
// Migrating from angular singlestat
|
||||
if (prevPluginId === 'singlestat' && prevOptions.angular) {
|
||||
return migrateFromAngularSinglestat(panel, prevOptions);
|
||||
} else if (prevPluginId === 'graph') {
|
||||
// Migrating from Graph panel
|
||||
return migrateFromGraphPanel(panel, prevOptions);
|
||||
}
|
||||
|
||||
for (const k of optionsToKeep) {
|
||||
@ -51,6 +55,65 @@ export function sharedSingleStatPanelChangedHandler(
|
||||
return options;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function migrateFromGraphPanel(panel: PanelModel<Partial<SingleStatBaseOptions>> | any, prevOptions: any) {
|
||||
const graphOptions: GraphOptions = prevOptions.angular;
|
||||
|
||||
const options: SingleStatBaseOptions & OptionsWithLegend = {
|
||||
orientation: VizOrientation.Auto,
|
||||
reduceOptions: {
|
||||
values: false,
|
||||
calcs: [],
|
||||
},
|
||||
legend: {
|
||||
displayMode: LegendDisplayMode.List,
|
||||
showLegend: true,
|
||||
placement: 'bottom',
|
||||
calcs: [],
|
||||
},
|
||||
};
|
||||
|
||||
if (graphOptions.xaxis?.mode === 'series') {
|
||||
panel.fieldConfig = {
|
||||
...panel.fieldConfig,
|
||||
defaults: {
|
||||
...panel.fieldConfig.defaults,
|
||||
color: { mode: 'palette-classic' },
|
||||
},
|
||||
};
|
||||
|
||||
// Value options calculation migration
|
||||
if (graphOptions.xaxis.values) {
|
||||
options.reduceOptions.calcs = getReducerForMigration(graphOptions.xaxis.values);
|
||||
}
|
||||
|
||||
// Legend migration
|
||||
const legendConfig = graphOptions.legend;
|
||||
if (legendConfig) {
|
||||
if (legendConfig.show) {
|
||||
options.legend.displayMode = legendConfig.alignAsTable ? LegendDisplayMode.Table : LegendDisplayMode.List;
|
||||
} else {
|
||||
options.legend.showLegend = false;
|
||||
}
|
||||
|
||||
if (legendConfig.rightSide) {
|
||||
options.legend.placement = 'right';
|
||||
}
|
||||
|
||||
if (legendConfig.values) {
|
||||
const enabledLegendValues = pickBy(legendConfig, identity);
|
||||
options.legend.calcs = getReducersFromLegend(enabledLegendValues);
|
||||
}
|
||||
|
||||
if (legendConfig.sideWidth) {
|
||||
options.legend.width = legendConfig.sideWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function migrateFromAngularSinglestat(panel: PanelModel<Partial<SingleStatBaseOptions>> | any, prevOptions: any) {
|
||||
const prevPanel = prevOptions.angular;
|
||||
const reducer = fieldReducers.getIfExists(prevPanel.valueName);
|
||||
@ -331,3 +394,55 @@ export function migrateOldThresholds(thresholds?: any[]): Threshold[] | undefine
|
||||
export function convertOldAngularValueMapping(panel: any): ValueMapping[] {
|
||||
return convertOldAngularValueMappings(panel);
|
||||
}
|
||||
|
||||
interface GraphOptions {
|
||||
xaxis: {
|
||||
mode: 'series' | 'time' | 'histogram';
|
||||
values?: string[];
|
||||
};
|
||||
legend: {
|
||||
show: boolean;
|
||||
alignAsTable: boolean;
|
||||
rightSide: boolean;
|
||||
values: boolean;
|
||||
min?: boolean;
|
||||
max?: boolean;
|
||||
avg?: boolean;
|
||||
current?: boolean;
|
||||
total?: boolean;
|
||||
sideWidth?: number;
|
||||
};
|
||||
}
|
||||
|
||||
function getReducersFromLegend(obj: Record<string, unknown>): string[] {
|
||||
const ids: string[] = [];
|
||||
for (const key in obj) {
|
||||
const reducer = fieldReducers.getIfExists(key);
|
||||
if (reducer) {
|
||||
ids.push(reducer.id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// same as public/app/plugins/panel/barchart/migrations.ts
|
||||
function getReducerForMigration(reducers: string[] | undefined) {
|
||||
const transformReducers: string[] = [];
|
||||
|
||||
reducers?.forEach((reducer) => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
if (!Object.values(ReducerID).includes(reducer as ReducerID)) {
|
||||
if (reducer === 'current') {
|
||||
transformReducers.push(ReducerID.lastNotNull);
|
||||
} else if (reducer === 'total') {
|
||||
transformReducers.push(ReducerID.sum);
|
||||
} else if (reducer === 'avg') {
|
||||
transformReducers.push(ReducerID.mean);
|
||||
}
|
||||
} else {
|
||||
transformReducers.push(reducer);
|
||||
}
|
||||
});
|
||||
|
||||
return reducers ? transformReducers : [ReducerID.sum];
|
||||
}
|
||||
|
@ -32,6 +32,10 @@ export function getPanelPluginToMigrateTo(panel: any, forceMigration?: boolean):
|
||||
isUrlFeatureFlagEnabled('autoMigrateGraphPanel'))
|
||||
) {
|
||||
if (panel.xaxis?.mode === 'series') {
|
||||
if (panel.legend?.values) {
|
||||
return 'bargauge';
|
||||
}
|
||||
|
||||
return 'barchart';
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FieldConfigSource, PanelModel } from '@grafana/data';
|
||||
import { FieldConfigSource, FieldMatcherID, PanelModel } from '@grafana/data';
|
||||
|
||||
import { changeToBarChartPanelMigrationHandler } from './migrations';
|
||||
|
||||
@ -17,16 +17,41 @@ describe('Bar chart Migrations', () => {
|
||||
angular: {
|
||||
xaxis: {
|
||||
mode: 'series',
|
||||
values: 'avg',
|
||||
values: ['avg'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const panel = {} as PanelModel;
|
||||
panel.options = changeToBarChartPanelMigrationHandler(panel, 'graph', old, prevFieldConfig);
|
||||
const transformations = panel.transformations || [];
|
||||
expect(transformations).toHaveLength(2);
|
||||
|
||||
const transform = panel.transformations![0];
|
||||
expect(transform.id).toBe('reduce');
|
||||
expect(transform.options.reducers).toBe('avg');
|
||||
const reduceTransform = transformations[0];
|
||||
expect(reduceTransform.id).toBe('reduce');
|
||||
expect(reduceTransform.options.reducers).toHaveLength(1);
|
||||
expect(reduceTransform.options.reducers[0]).toBe('mean');
|
||||
|
||||
const transposeTransform = transformations[1];
|
||||
expect(transposeTransform.id).toBe('transpose');
|
||||
|
||||
expect(panel.fieldConfig.overrides).toHaveLength(1);
|
||||
expect(panel.fieldConfig.overrides[0].matcher.id).toBe(FieldMatcherID.byName);
|
||||
expect(panel.fieldConfig.overrides).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "Field",
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "custom.axisPlacement",
|
||||
"value": "hidden",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
@ -1,33 +1,79 @@
|
||||
import { PanelTypeChangedHandler } from '@grafana/data';
|
||||
import { FieldMatcherID, PanelTypeChangedHandler, ReducerID } from '@grafana/data';
|
||||
import { AxisPlacement } from '@grafana/ui';
|
||||
|
||||
/*
|
||||
* This is called when the panel changes from another panel
|
||||
*/
|
||||
export const changeToBarChartPanelMigrationHandler: PanelTypeChangedHandler = (
|
||||
panel,
|
||||
prevPluginId,
|
||||
prevOptions,
|
||||
prevFieldConfig
|
||||
) => {
|
||||
export const changeToBarChartPanelMigrationHandler: PanelTypeChangedHandler = (panel, prevPluginId, prevOptions) => {
|
||||
if (prevPluginId === 'graph') {
|
||||
const graphOptions: GraphOptions = prevOptions.angular;
|
||||
|
||||
const fieldConfig = panel.fieldConfig ?? { defaults: {}, overrides: [] };
|
||||
|
||||
if (graphOptions.xaxis?.mode === 'series') {
|
||||
const tranformations = panel.transformations || [];
|
||||
tranformations.push({
|
||||
id: 'reduce',
|
||||
options: {
|
||||
reducers: graphOptions.xaxis?.values ?? ['sum'],
|
||||
const transformations = panel.transformations || [];
|
||||
transformations.push(
|
||||
{
|
||||
id: 'reduce',
|
||||
options: {
|
||||
reducers: getReducer(graphOptions.xaxis?.values),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'transpose',
|
||||
options: {},
|
||||
}
|
||||
);
|
||||
|
||||
panel.transformations = transformations;
|
||||
|
||||
// temporary, until we have a bar chart with per bar labels
|
||||
fieldConfig.overrides.push({
|
||||
matcher: {
|
||||
id: FieldMatcherID.byName,
|
||||
options: 'Field',
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'custom.axisPlacement',
|
||||
value: AxisPlacement.Hidden,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
panel.transformations = tranformations;
|
||||
panel.fieldConfig = fieldConfig;
|
||||
panel.options = {
|
||||
...panel.options,
|
||||
groupWidth: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
// same as grafana-ui/src/components/SingleStatShared/SingleStatBaseOptions.ts
|
||||
const getReducer = (reducers: string[] | undefined) => {
|
||||
const transformReducers: string[] = [];
|
||||
|
||||
reducers?.forEach((reducer) => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
if (!Object.values(ReducerID).includes(reducer as ReducerID)) {
|
||||
if (reducer === 'current') {
|
||||
transformReducers.push(ReducerID.lastNotNull);
|
||||
} else if (reducer === 'total') {
|
||||
transformReducers.push(ReducerID.sum);
|
||||
} else if (reducer === 'avg') {
|
||||
transformReducers.push(ReducerID.mean);
|
||||
}
|
||||
} else {
|
||||
transformReducers.push(reducer);
|
||||
}
|
||||
});
|
||||
|
||||
return reducers ? transformReducers : [ReducerID.sum];
|
||||
};
|
||||
|
||||
interface GraphOptions {
|
||||
xaxis: {
|
||||
mode: 'series' | 'time' | 'histogram';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import { DataFrame, FieldType, getFieldSeriesColor } from '@grafana/data';
|
||||
import { cacheFieldDisplayNames, DataFrame, FieldType, getFieldSeriesColor } from '@grafana/data';
|
||||
import { Field } from '@grafana/data/';
|
||||
import { AxisPlacement, VizLegendOptions } from '@grafana/schema';
|
||||
import { useTheme2, VizLayout, VizLayoutLegendProps, VizLegend, VizLegendItem } from '@grafana/ui';
|
||||
@ -16,6 +16,8 @@ export const BarGaugeLegend = memo(
|
||||
const theme = useTheme2();
|
||||
let legendItems: VizLegendItem[] = [];
|
||||
|
||||
cacheFieldDisplayNames(data);
|
||||
|
||||
data.forEach((series, frameIndex) => {
|
||||
series.fields.forEach((field, i) => {
|
||||
const fieldIndex = i + 1;
|
||||
|
Loading…
Reference in New Issue
Block a user