mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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' },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user