Field color: handling color changes when switching panel types (#28875)

* FieldColor: Per panel settings to filter out supported modes

* Updates

* Updated solution

* Update panel plugin API for standard options support

* Update FieldColorConfigSettings interface

* Change color mode correctly when changing plugin type

* Render only applicable color modes in field color config editor

* Apply field config API changes

* TS fixes

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Dominik Prokop
2020-11-09 13:11:09 +01:00
committed by GitHub
parent 2887f3f68b
commit 2ea4a36bf7
16 changed files with 296 additions and 155 deletions

View File

@@ -8,6 +8,8 @@ import {
standardFieldConfigEditorRegistry,
PanelData,
DataSourceInstanceSettings,
FieldColorModeId,
FieldColorConfigSettings,
} from '@grafana/data';
import { ComponentClass } from 'react';
import { PanelQueryRunner } from './PanelQueryRunner';
@@ -55,7 +57,20 @@ export const mockStandardProperties = () => {
shouldApply: () => true,
};
return [unit, decimals, boolean];
const fieldColor = {
id: 'color',
path: 'color',
name: 'color',
description: '',
// @ts-ignore
editor: () => null,
// @ts-ignore
override: () => null,
process: identityOverrideProcessor,
shouldApply: () => true,
};
return [unit, decimals, boolean, fieldColor];
};
standardFieldConfigEditorRegistry.setInit(() => mockStandardProperties());
@@ -147,10 +162,13 @@ describe('PanelModel', () => {
});
panelPlugin.useFieldConfig({
standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals],
standardOptionsDefaults: {
[FieldConfigProperty.Unit]: 'flop',
[FieldConfigProperty.Decimals]: 2,
standardOptions: {
[FieldConfigProperty.Unit]: {
defaultValue: 'flop',
},
[FieldConfigProperty.Decimals]: {
defaultValue: 2,
},
},
});
model.pluginLoaded(panelPlugin);
@@ -239,6 +257,17 @@ describe('PanelModel', () => {
describe('when changing panel type', () => {
beforeEach(() => {
const newPlugin = getPanelPlugin({ id: 'graph' });
newPlugin.useFieldConfig({
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byThresholdsSupport: true,
},
},
},
});
newPlugin.setPanelOptions(builder => {
builder.addBooleanSwitch({
name: 'Show thresholds labels',
@@ -285,6 +314,66 @@ describe('PanelModel', () => {
});
});
describe('when changing panel type to one that does not support by value color mode', () => {
beforeEach(() => {
model.fieldConfig.defaults.color = { mode: FieldColorModeId.Thresholds };
const newPlugin = getPanelPlugin({ id: 'graph' });
newPlugin.useFieldConfig({
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: false,
},
},
},
});
model.editSourceId = 1001;
model.changePlugin(newPlugin);
model.alert = { id: 2 };
});
it('should change color mode', () => {
expect(model.fieldConfig.defaults.color.mode).toBe(FieldColorModeId.PaletteClassic);
});
});
describe('when changing panel type from one not supporting by value color mode to one that supports it', () => {
const prepareModel = (colorOptions?: FieldColorConfigSettings) => {
const newModel = new PanelModel(modelJson);
newModel.fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
const newPlugin = getPanelPlugin({ id: 'graph' });
newPlugin.useFieldConfig({
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: true,
...colorOptions,
},
},
},
});
newModel.editSourceId = 1001;
newModel.changePlugin(newPlugin);
newModel.alert = { id: 2 };
return newModel;
};
it('should keep supported mode', () => {
const testModel = prepareModel();
expect(testModel.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.PaletteClassic);
});
it('should change to thresholds mode when it prefers to', () => {
const testModel = prepareModel({ preferThresholdsMode: true });
expect(testModel.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.Thresholds);
});
});
describe('when changing to react panel from angular panel', () => {
let panelQueryRunner: any;

View File

@@ -12,6 +12,10 @@ import {
DataQueryResponseData,
DataTransformerConfig,
eventFactory,
FieldColorConfigSettings,
FieldColorModeId,
fieldColorModeRegistry,
FieldConfigProperty,
FieldConfigSource,
PanelEvents,
PanelPlugin,
@@ -322,7 +326,35 @@ export class PanelModel implements DataConfigSource {
}
});
this.fieldConfig = applyFieldConfigDefaults(this.fieldConfig, this.plugin!.fieldConfigDefaults);
this.fieldConfig = applyFieldConfigDefaults(this.fieldConfig, plugin.fieldConfigDefaults);
this.validateFieldColorMode(plugin);
}
private validateFieldColorMode(plugin: PanelPlugin) {
// adjust to prefered field color setting if needed
const color = plugin.fieldConfigRegistry.getIfExists(FieldConfigProperty.Color);
if (color && color.settings) {
const colorSettings = color.settings as FieldColorConfigSettings;
const mode = fieldColorModeRegistry.getIfExists(this.fieldConfig.defaults.color?.mode);
// When no support fo value colors, use classic palette
if (!colorSettings.byValueSupport) {
if (!mode || mode.isByValue) {
this.fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
return;
}
}
// When supporting value colors and prefering thresholds, use Thresholds mode.
// Otherwise keep current mode
if (colorSettings.byValueSupport && colorSettings.preferThresholdsMode) {
if (!mode || !mode.isByValue) {
this.fieldConfig.defaults.color = { mode: FieldColorModeId.Thresholds };
return;
}
}
}
}
pluginLoaded(plugin: PanelPlugin) {

View File

@@ -12,11 +12,11 @@ import { axesEditorComponent } from './axes_editor';
import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest';
import { PanelEvents, PanelPlugin, DataFrame, FieldConfigProperty, getColorForTheme } from '@grafana/data';
import { DataFrame, FieldConfigProperty, getColorForTheme, PanelEvents, PanelPlugin } from '@grafana/data';
import { GraphContextMenuCtrl } from './GraphContextMenuCtrl';
import { graphPanelMigrationHandler } from './GraphMigrations';
import { DataWarning, GraphPanelOptions, GraphFieldConfig } from './types';
import { DataWarning, GraphFieldConfig, GraphPanelOptions } from './types';
import { auto } from 'angular';
import { AnnotationsSrv } from 'app/features/annotations/all';
@@ -388,10 +388,14 @@ export class GraphCtrl extends MetricsPanelCtrl {
// Use new react style configuration
export const plugin = new PanelPlugin<GraphPanelOptions, GraphFieldConfig>(null)
.useFieldConfig({
standardOptions: [
FieldConfigProperty.DisplayName,
FieldConfigProperty.Unit,
FieldConfigProperty.Links, // previously saved as dataLinks on options
disableStandardOptions: [
FieldConfigProperty.NoValue,
FieldConfigProperty.Thresholds,
FieldConfigProperty.Max,
FieldConfigProperty.Min,
FieldConfigProperty.Decimals,
FieldConfigProperty.Color,
FieldConfigProperty.Mappings,
],
})
.setMigrationHandler(graphPanelMigrationHandler);

View File

@@ -1,63 +1,61 @@
import { FieldConfigProperty, PanelPlugin } from '@grafana/data';
import { PanelPlugin } from '@grafana/data';
import { GraphPanel } from './GraphPanel';
import { Options } from './types';
export const plugin = new PanelPlugin<Options>(GraphPanel)
.useFieldConfig({ standardOptions: [FieldConfigProperty.Unit, FieldConfigProperty.Decimals] })
.setPanelOptions(builder => {
builder
.addBooleanSwitch({
path: 'graph.showBars',
name: 'Show bars',
description: '',
defaultValue: false,
})
.addBooleanSwitch({
path: 'graph.showLines',
name: 'Show lines',
description: '',
defaultValue: true,
})
.addBooleanSwitch({
path: 'graph.showPoints',
name: 'Show poins',
description: '',
defaultValue: false,
})
.addBooleanSwitch({
path: 'legend.isVisible',
name: 'Show legend',
description: '',
defaultValue: true,
})
.addBooleanSwitch({
path: 'legend.asTable',
name: 'Display legend as table',
description: '',
defaultValue: false,
})
.addRadio({
path: 'legend.placement',
name: 'Legend placement',
description: '',
defaultValue: 'under',
settings: {
options: [
{ value: 'under', label: 'Below graph' },
{ value: 'right', label: 'Right to the graph' },
],
},
})
.addRadio({
path: 'tooltipOptions.mode',
name: 'Tooltip mode',
description: '',
defaultValue: 'single',
settings: {
options: [
{ value: 'single', label: 'Single series' },
{ value: 'multi', label: 'All series' },
],
},
});
});
export const plugin = new PanelPlugin<Options>(GraphPanel).useFieldConfig().setPanelOptions(builder => {
builder
.addBooleanSwitch({
path: 'graph.showBars',
name: 'Show bars',
description: '',
defaultValue: false,
})
.addBooleanSwitch({
path: 'graph.showLines',
name: 'Show lines',
description: '',
defaultValue: true,
})
.addBooleanSwitch({
path: 'graph.showPoints',
name: 'Show poins',
description: '',
defaultValue: false,
})
.addBooleanSwitch({
path: 'legend.isVisible',
name: 'Show legend',
description: '',
defaultValue: true,
})
.addBooleanSwitch({
path: 'legend.asTable',
name: 'Display legend as table',
description: '',
defaultValue: false,
})
.addRadio({
path: 'legend.placement',
name: 'Legend placement',
description: '',
defaultValue: 'under',
settings: {
options: [
{ value: 'under', label: 'Below graph' },
{ value: 'right', label: 'Right to the graph' },
],
},
})
.addRadio({
path: 'tooltipOptions.mode',
name: 'Tooltip mode',
description: '',
defaultValue: 'single',
settings: {
options: [
{ value: 'single', label: 'Single series' },
{ value: 'multi', label: 'All series' },
],
},
});
});

View File

@@ -1,21 +1,20 @@
import { FieldConfigProperty, PanelPlugin } from '@grafana/data';
import { FieldColorModeId, FieldConfigProperty, PanelPlugin } from '@grafana/data';
import { AxisSide, GraphCustomFieldConfig } from '@grafana/ui';
import { GraphPanel } from './GraphPanel';
import { Options } from './types';
export const plugin = new PanelPlugin<Options, GraphCustomFieldConfig>(GraphPanel)
.useFieldConfig({
standardOptions: [
// FieldConfigProperty.Min,
// FieldConfigProperty.Max,
FieldConfigProperty.Color,
FieldConfigProperty.Unit,
FieldConfigProperty.DisplayName,
FieldConfigProperty.Decimals,
// NOT: FieldConfigProperty.Thresholds,
FieldConfigProperty.Mappings,
],
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: false,
},
defaultValue: {
mode: FieldColorModeId.PaletteClassic,
},
},
},
useCustomConfig: builder => {
builder
.addBooleanSwitch({

View File

@@ -1,6 +1,6 @@
import { sharedSingleStatMigrationHandler, BigValueTextMode } from '@grafana/ui';
import { BigValueTextMode, sharedSingleStatMigrationHandler } from '@grafana/ui';
import { PanelPlugin } from '@grafana/data';
import { StatPanelOptions, addStandardDataReduceOptions } from './types';
import { addStandardDataReduceOptions, StatPanelOptions } from './types';
import { StatPanel } from './StatPanel';
import { statPanelChangedHandler } from './StatMigrations';