mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GraphNG: simple settings migration from flot panel (#29599)
This commit is contained in:
parent
3ce93050e0
commit
e8ae6fd01f
@ -0,0 +1,122 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Graph Migrations simple bars 1`] = `
|
||||||
|
Object {
|
||||||
|
"fieldConfig": Object {
|
||||||
|
"defaults": Object {
|
||||||
|
"custom": Object {
|
||||||
|
"drawStyle": "bars",
|
||||||
|
"fillOpacity": 1,
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"overrides": Array [],
|
||||||
|
},
|
||||||
|
"options": Object {
|
||||||
|
"graph": Object {},
|
||||||
|
"legend": Object {
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
},
|
||||||
|
"tooltipOptions": Object {
|
||||||
|
"mode": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Graph Migrations stairscase 1`] = `
|
||||||
|
Object {
|
||||||
|
"fieldConfig": Object {
|
||||||
|
"defaults": Object {
|
||||||
|
"custom": Object {
|
||||||
|
"axisPlacement": "hidden",
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 0.5,
|
||||||
|
"lineInterpolation": "stepAfter",
|
||||||
|
"pointSize": 2,
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
},
|
||||||
|
"displayName": "DISPLAY NAME",
|
||||||
|
"nullValueMode": "null",
|
||||||
|
"unit": "short",
|
||||||
|
},
|
||||||
|
"overrides": Array [],
|
||||||
|
},
|
||||||
|
"options": Object {
|
||||||
|
"graph": Object {},
|
||||||
|
"legend": Object {
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
},
|
||||||
|
"tooltipOptions": Object {
|
||||||
|
"mode": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Graph Migrations twoYAxis 1`] = `
|
||||||
|
Object {
|
||||||
|
"fieldConfig": Object {
|
||||||
|
"defaults": Object {
|
||||||
|
"custom": Object {
|
||||||
|
"axisLabel": "Y111",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 0.1,
|
||||||
|
"pointSize": 2,
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
},
|
||||||
|
"decimals": 3,
|
||||||
|
"max": 1000,
|
||||||
|
"min": 0,
|
||||||
|
"nullValueMode": "null",
|
||||||
|
"unit": "areaMI2",
|
||||||
|
},
|
||||||
|
"overrides": Array [
|
||||||
|
Object {
|
||||||
|
"matcher": Object {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "B-series",
|
||||||
|
},
|
||||||
|
"properties": Array [
|
||||||
|
Object {
|
||||||
|
"id": "unit",
|
||||||
|
"value": "degree",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "decimals",
|
||||||
|
"value": 2,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "min",
|
||||||
|
"value": -10,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "max",
|
||||||
|
"value": 25,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "custom.axisLabel",
|
||||||
|
"value": "Y222",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"options": Object {
|
||||||
|
"graph": Object {},
|
||||||
|
"legend": Object {
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
},
|
||||||
|
"tooltipOptions": Object {
|
||||||
|
"mode": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
203
public/app/plugins/panel/graph3/migrations.test.ts
Normal file
203
public/app/plugins/panel/graph3/migrations.test.ts
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import { PanelModel } from '@grafana/data';
|
||||||
|
import { graphPanelChangedHandler } from './migrations';
|
||||||
|
|
||||||
|
describe('Graph Migrations', () => {
|
||||||
|
it('simple bars', () => {
|
||||||
|
const old: any = {
|
||||||
|
angular: {
|
||||||
|
bars: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const panel = {} as PanelModel;
|
||||||
|
panel.options = graphPanelChangedHandler(panel, 'graph', old);
|
||||||
|
expect(panel).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stairscase', () => {
|
||||||
|
const old: any = {
|
||||||
|
angular: stairscase,
|
||||||
|
};
|
||||||
|
const panel = {} as PanelModel;
|
||||||
|
panel.options = graphPanelChangedHandler(panel, 'graph', old);
|
||||||
|
expect(panel).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('twoYAxis', () => {
|
||||||
|
const old: any = {
|
||||||
|
angular: twoYAxis,
|
||||||
|
};
|
||||||
|
const panel = {} as PanelModel;
|
||||||
|
panel.options = graphPanelChangedHandler(panel, 'graph', old);
|
||||||
|
expect(panel).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const stairscase = {
|
||||||
|
fieldConfig: {
|
||||||
|
defaults: {
|
||||||
|
custom: {},
|
||||||
|
unit: 'areaF2',
|
||||||
|
displayName: 'DISPLAY NAME',
|
||||||
|
},
|
||||||
|
overrides: [],
|
||||||
|
},
|
||||||
|
aliasColors: {},
|
||||||
|
dashLength: 10,
|
||||||
|
fill: 5,
|
||||||
|
fillGradient: 6,
|
||||||
|
legend: {
|
||||||
|
avg: true,
|
||||||
|
current: true,
|
||||||
|
max: true,
|
||||||
|
min: true,
|
||||||
|
show: true,
|
||||||
|
total: true,
|
||||||
|
values: true,
|
||||||
|
alignAsTable: true,
|
||||||
|
},
|
||||||
|
lines: true,
|
||||||
|
linewidth: 1,
|
||||||
|
nullPointMode: 'null',
|
||||||
|
options: {
|
||||||
|
alertThreshold: true,
|
||||||
|
},
|
||||||
|
pointradius: 2,
|
||||||
|
seriesOverrides: [],
|
||||||
|
spaceLength: 10,
|
||||||
|
steppedLine: true,
|
||||||
|
thresholds: [],
|
||||||
|
timeRegions: [],
|
||||||
|
title: 'Panel Title',
|
||||||
|
tooltip: {
|
||||||
|
shared: true,
|
||||||
|
sort: 0,
|
||||||
|
value_type: 'individual',
|
||||||
|
},
|
||||||
|
type: 'graph',
|
||||||
|
xaxis: {
|
||||||
|
buckets: null,
|
||||||
|
mode: 'time',
|
||||||
|
name: null,
|
||||||
|
show: true,
|
||||||
|
values: [],
|
||||||
|
},
|
||||||
|
yaxes: [
|
||||||
|
{
|
||||||
|
$$hashKey: 'object:42',
|
||||||
|
format: 'short',
|
||||||
|
label: null,
|
||||||
|
logBase: 1,
|
||||||
|
max: null,
|
||||||
|
min: null,
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$$hashKey: 'object:43',
|
||||||
|
format: 'short',
|
||||||
|
label: null,
|
||||||
|
logBase: 1,
|
||||||
|
max: null,
|
||||||
|
min: null,
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yaxis: {
|
||||||
|
align: false,
|
||||||
|
alignLevel: null,
|
||||||
|
},
|
||||||
|
timeFrom: null,
|
||||||
|
timeShift: null,
|
||||||
|
bars: false,
|
||||||
|
dashes: false,
|
||||||
|
hiddenSeries: false,
|
||||||
|
percentage: false,
|
||||||
|
points: false,
|
||||||
|
stack: false,
|
||||||
|
decimals: 1,
|
||||||
|
datasource: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const twoYAxis = {
|
||||||
|
yaxes: [
|
||||||
|
{
|
||||||
|
label: 'Y111',
|
||||||
|
show: true,
|
||||||
|
logBase: 10,
|
||||||
|
min: '0',
|
||||||
|
max: '1000',
|
||||||
|
format: 'areaMI2',
|
||||||
|
$$hashKey: 'object:19',
|
||||||
|
decimals: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Y222',
|
||||||
|
show: true,
|
||||||
|
logBase: 1,
|
||||||
|
min: '-10',
|
||||||
|
max: '25',
|
||||||
|
format: 'degree',
|
||||||
|
$$hashKey: 'object:20',
|
||||||
|
decimals: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xaxis: {
|
||||||
|
show: true,
|
||||||
|
mode: 'time',
|
||||||
|
name: null,
|
||||||
|
values: [],
|
||||||
|
buckets: null,
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
align: false,
|
||||||
|
alignLevel: null,
|
||||||
|
},
|
||||||
|
lines: true,
|
||||||
|
fill: 1,
|
||||||
|
linewidth: 1,
|
||||||
|
dashLength: 10,
|
||||||
|
spaceLength: 10,
|
||||||
|
pointradius: 2,
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
values: false,
|
||||||
|
min: false,
|
||||||
|
max: false,
|
||||||
|
current: false,
|
||||||
|
total: false,
|
||||||
|
avg: false,
|
||||||
|
},
|
||||||
|
nullPointMode: 'null',
|
||||||
|
tooltip: {
|
||||||
|
value_type: 'individual',
|
||||||
|
shared: true,
|
||||||
|
sort: 0,
|
||||||
|
},
|
||||||
|
aliasColors: {},
|
||||||
|
seriesOverrides: [
|
||||||
|
{
|
||||||
|
alias: 'B-series',
|
||||||
|
yaxis: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
thresholds: [],
|
||||||
|
timeRegions: [],
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
refId: 'A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
refId: 'B',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fillGradient: 0,
|
||||||
|
dashes: false,
|
||||||
|
hiddenSeries: false,
|
||||||
|
points: false,
|
||||||
|
bars: false,
|
||||||
|
stack: false,
|
||||||
|
percentage: false,
|
||||||
|
steppedLine: false,
|
||||||
|
timeFrom: null,
|
||||||
|
timeShift: null,
|
||||||
|
datasource: null,
|
||||||
|
};
|
266
public/app/plugins/panel/graph3/migrations.ts
Normal file
266
public/app/plugins/panel/graph3/migrations.ts
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
import {
|
||||||
|
FieldConfig,
|
||||||
|
FieldConfigSource,
|
||||||
|
NullValueMode,
|
||||||
|
PanelModel,
|
||||||
|
fieldReducers,
|
||||||
|
ConfigOverrideRule,
|
||||||
|
FieldMatcherID,
|
||||||
|
DynamicConfigValue,
|
||||||
|
} from '@grafana/data';
|
||||||
|
import { GraphFieldConfig, LegendDisplayMode } from '@grafana/ui';
|
||||||
|
import { AxisPlacement, DrawStyle, LineInterpolation, PointVisibility } from '@grafana/ui/src/components/uPlot/config';
|
||||||
|
import { Options } from './types';
|
||||||
|
import omitBy from 'lodash/omitBy';
|
||||||
|
import isNil from 'lodash/isNil';
|
||||||
|
import { isNumber, isString } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called when the panel changes from another panel
|
||||||
|
*/
|
||||||
|
export const graphPanelChangedHandler = (
|
||||||
|
panel: PanelModel<Partial<Options>> | any,
|
||||||
|
prevPluginId: string,
|
||||||
|
prevOptions: any
|
||||||
|
) => {
|
||||||
|
// Changing from angular/flot panel to react/uPlot
|
||||||
|
if (prevPluginId === 'graph' && prevOptions.angular) {
|
||||||
|
const { fieldConfig, options } = flotToGraphOptions(prevOptions.angular);
|
||||||
|
panel.fieldConfig = fieldConfig; // Mutates the incoming panel
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSource; options: Options } {
|
||||||
|
const overrides: ConfigOverrideRule[] = angular.fieldConfig?.overrides ?? [];
|
||||||
|
const yaxes = angular.yaxes ?? [];
|
||||||
|
let y1 = getFieldConfigFromOldAxis(yaxes[0]);
|
||||||
|
if (angular.fieldConfig?.defaults) {
|
||||||
|
y1 = {
|
||||||
|
...angular.fieldConfig?.defaults,
|
||||||
|
...y1, // Keep the y-axis unit and custom
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// "seriesOverrides": [
|
||||||
|
// {
|
||||||
|
// "$$hashKey": "object:183",
|
||||||
|
// "alias": "B-series",
|
||||||
|
// "fill": 3,
|
||||||
|
// "nullPointMode": "null as zero",
|
||||||
|
// "lines": true,
|
||||||
|
// "linewidth": 2
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
if (angular.seriesOverrides?.length) {
|
||||||
|
for (const seriesOverride of angular.seriesOverrides) {
|
||||||
|
if (!seriesOverride.alias) {
|
||||||
|
continue; // the matcher config
|
||||||
|
}
|
||||||
|
const rule: ConfigOverrideRule = {
|
||||||
|
matcher: {
|
||||||
|
id: FieldMatcherID.byName,
|
||||||
|
options: seriesOverride.alias,
|
||||||
|
},
|
||||||
|
properties: [],
|
||||||
|
};
|
||||||
|
for (const p of Object.keys(seriesOverride)) {
|
||||||
|
const v = seriesOverride[p];
|
||||||
|
switch (p) {
|
||||||
|
// Ignore
|
||||||
|
case 'alias':
|
||||||
|
case '$$hashKey':
|
||||||
|
break;
|
||||||
|
// Link to y axis settings
|
||||||
|
case 'yaxis':
|
||||||
|
if (2 === v) {
|
||||||
|
const y2 = getFieldConfigFromOldAxis(yaxes[1]);
|
||||||
|
fillY2DynamicValues(y1, y2, rule.properties);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'fill':
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.fillOpacity',
|
||||||
|
value: v / 10.0, // was 0-10
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'points':
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.showPoints',
|
||||||
|
value: v ? PointVisibility.Always : PointVisibility.Never,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'bars':
|
||||||
|
if (v) {
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.drawStyle',
|
||||||
|
value: DrawStyle.Bars,
|
||||||
|
});
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.fillOpacity',
|
||||||
|
value: 1, // solid bars
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.drawStyle',
|
||||||
|
value: DrawStyle.Line, // Change from bars
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'lineWidth':
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.lineWidth',
|
||||||
|
value: v,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'pointradius':
|
||||||
|
rule.properties.push({
|
||||||
|
id: 'custom.pointSize',
|
||||||
|
value: v,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log('Ignore override migration:', seriesOverride.alias, p, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rule.properties.length) {
|
||||||
|
overrides.push(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const graph = y1.custom ?? ({} as GraphFieldConfig);
|
||||||
|
graph.drawStyle = angular.bars ? DrawStyle.Bars : angular.lines ? DrawStyle.Line : DrawStyle.Points;
|
||||||
|
if (angular.points) {
|
||||||
|
graph.showPoints = PointVisibility.Always;
|
||||||
|
} else if (graph.drawStyle !== DrawStyle.Points) {
|
||||||
|
graph.showPoints = PointVisibility.Never;
|
||||||
|
}
|
||||||
|
if (graph.drawStyle === DrawStyle.Bars) {
|
||||||
|
graph.fillOpacity = 1.0; // bars were always
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.lineWidth = angular.lineWidth;
|
||||||
|
graph.pointSize = angular.pointradius;
|
||||||
|
if (isNumber(angular.fill)) {
|
||||||
|
graph.fillOpacity = angular.fill / 10; // fill is 0-10
|
||||||
|
}
|
||||||
|
graph.spanNulls = angular.nullPointMode === NullValueMode.Null;
|
||||||
|
if (angular.steppedLine) {
|
||||||
|
graph.lineInterpolation = LineInterpolation.StepAfter;
|
||||||
|
}
|
||||||
|
y1.custom = omitBy(graph, isNil);
|
||||||
|
y1.nullValueMode = angular.nullPointMode as NullValueMode;
|
||||||
|
|
||||||
|
const options: Options = {
|
||||||
|
graph: {},
|
||||||
|
legend: {
|
||||||
|
displayMode: LegendDisplayMode.List,
|
||||||
|
placement: 'bottom',
|
||||||
|
},
|
||||||
|
tooltipOptions: {
|
||||||
|
mode: 'single',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (angular.legend?.values) {
|
||||||
|
const show = getReducersFromLegend(angular.legend?.values);
|
||||||
|
console.log('Migrate Legend', show);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fieldConfig: {
|
||||||
|
defaults: omitBy(y1, isNil),
|
||||||
|
overrides,
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "label": "Y111",
|
||||||
|
// "show": true,
|
||||||
|
// "logBase": 10,
|
||||||
|
// "min": "0",
|
||||||
|
// "max": "1000",
|
||||||
|
// "format": "areaMI2",
|
||||||
|
// "$$hashKey": "object:19",
|
||||||
|
// "decimals": 3
|
||||||
|
// },
|
||||||
|
function getFieldConfigFromOldAxis(obj: any): FieldConfig<GraphFieldConfig> {
|
||||||
|
if (!obj) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const graph: GraphFieldConfig = {
|
||||||
|
axisPlacement: obj.show ? AxisPlacement.Auto : AxisPlacement.Hidden,
|
||||||
|
};
|
||||||
|
if (obj.label) {
|
||||||
|
graph.axisLabel = obj.label;
|
||||||
|
}
|
||||||
|
return omitBy(
|
||||||
|
{
|
||||||
|
unit: obj.format,
|
||||||
|
decimals: validNumber(obj.decimals),
|
||||||
|
min: validNumber(obj.min),
|
||||||
|
max: validNumber(obj.max),
|
||||||
|
custom: graph,
|
||||||
|
},
|
||||||
|
isNil
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillY2DynamicValues(
|
||||||
|
y1: FieldConfig<GraphFieldConfig>,
|
||||||
|
y2: FieldConfig<GraphFieldConfig>,
|
||||||
|
props: DynamicConfigValue[]
|
||||||
|
) {
|
||||||
|
// The standard properties
|
||||||
|
for (const key of Object.keys(y2)) {
|
||||||
|
const value = (y2 as any)[key];
|
||||||
|
if (key !== 'custom' && value !== (y1 as any)[key]) {
|
||||||
|
props.push({
|
||||||
|
id: key,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any custom property
|
||||||
|
const y1G = y1.custom ?? {};
|
||||||
|
const y2G = y2.custom ?? {};
|
||||||
|
for (const key of Object.keys(y2G)) {
|
||||||
|
const value = (y2G as any)[key];
|
||||||
|
if (value !== (y1G as any)[key]) {
|
||||||
|
props.push({
|
||||||
|
id: `custom.${key}`,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validNumber(val: any): number | undefined {
|
||||||
|
if (isNumber(val)) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
if (isString(val)) {
|
||||||
|
const num = Number(val);
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReducersFromLegend(obj: Record<string, any>): string[] {
|
||||||
|
const ids: string[] = [];
|
||||||
|
for (const key of Object.keys(obj)) {
|
||||||
|
const r = fieldReducers.getIfExists(key);
|
||||||
|
if (r) {
|
||||||
|
ids.push(r.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
@ -8,9 +8,11 @@ import {
|
|||||||
graphFieldOptions,
|
graphFieldOptions,
|
||||||
} from '@grafana/ui/src/components/uPlot/config';
|
} from '@grafana/ui/src/components/uPlot/config';
|
||||||
import { GraphPanel } from './GraphPanel';
|
import { GraphPanel } from './GraphPanel';
|
||||||
|
import { graphPanelChangedHandler } from './migrations';
|
||||||
import { Options } from './types';
|
import { Options } from './types';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<Options, GraphFieldConfig>(GraphPanel)
|
export const plugin = new PanelPlugin<Options, GraphFieldConfig>(GraphPanel)
|
||||||
|
.setPanelChangeHandler(graphPanelChangedHandler)
|
||||||
.useFieldConfig({
|
.useFieldConfig({
|
||||||
standardOptions: {
|
standardOptions: {
|
||||||
[FieldConfigProperty.Color]: {
|
[FieldConfigProperty.Color]: {
|
||||||
|
Loading…
Reference in New Issue
Block a user