123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- import { mergeWith, isArray, isObject, unset, isEqual } from 'lodash';
- import {
- ConfigOverrideRule,
- DynamicConfigValue,
- FieldColorConfigSettings,
- FieldColorModeId,
- fieldColorModeRegistry,
- FieldConfigOptionsRegistry,
- FieldConfigProperty,
- FieldConfigSource,
- PanelPlugin,
- ThresholdsConfig,
- ThresholdsMode,
- } from '@grafana/data';
- export interface Props {
- plugin: PanelPlugin;
- currentFieldConfig: FieldConfigSource;
- currentOptions: Record<string, any>;
- isAfterPluginChange: boolean;
- }
- export interface OptionDefaults {
- options: any;
- fieldConfig: FieldConfigSource;
- }
- export function getPanelOptionsWithDefaults({
- plugin,
- currentOptions,
- currentFieldConfig,
- isAfterPluginChange,
- }: Props): OptionDefaults {
- const optionsWithDefaults = mergeWith(
- {},
- plugin.defaults,
- currentOptions || {},
- (objValue: any, srcValue: any): any => {
- if (isArray(srcValue)) {
- return srcValue;
- }
- }
- );
- const fieldConfigWithDefaults = applyFieldConfigDefaults(currentFieldConfig, plugin);
- const fieldConfigWithOptimalColorMode = adaptFieldColorMode(plugin, fieldConfigWithDefaults, isAfterPluginChange);
- return { options: optionsWithDefaults, fieldConfig: fieldConfigWithOptimalColorMode };
- }
- function applyFieldConfigDefaults(existingFieldConfig: FieldConfigSource, plugin: PanelPlugin): FieldConfigSource {
- const pluginDefaults = plugin.fieldConfigDefaults;
- const result: FieldConfigSource = {
- defaults: mergeWith(
- {},
- pluginDefaults.defaults,
- existingFieldConfig ? existingFieldConfig.defaults : {},
- (objValue: any, srcValue: any): any => {
- if (isArray(srcValue)) {
- return srcValue;
- }
- }
- ),
- overrides: existingFieldConfig?.overrides ?? [],
- };
- cleanProperties(result.defaults, '', plugin.fieldConfigRegistry);
- // Thresholds base values are null in JSON but need to be converted to -Infinity
- if (result.defaults.thresholds) {
- fixThresholds(result.defaults.thresholds);
- }
- // Filter out overrides for properties that cannot be found in registry
- result.overrides = filterFieldConfigOverrides(result.overrides, (prop) => {
- return plugin.fieldConfigRegistry.getIfExists(prop.id) !== undefined;
- });
- for (const override of result.overrides) {
- for (const property of override.properties) {
- if (property.id === 'thresholds') {
- fixThresholds(property.value as ThresholdsConfig);
- }
- }
- }
- return result;
- }
- export function filterFieldConfigOverrides(
- overrides: ConfigOverrideRule[],
- condition: (value: DynamicConfigValue) => boolean
- ): ConfigOverrideRule[] {
- return overrides
- .map((x) => {
- const properties = x.properties.filter(condition);
- return {
- ...x,
- properties,
- };
- })
- .filter((x) => x.properties.length > 0);
- }
- function cleanProperties(obj: any, parentPath: string, fieldConfigRegistry: FieldConfigOptionsRegistry) {
- let found = false;
- for (const propName of Object.keys(obj)) {
- const value = obj[propName];
- const fullPath = `${parentPath}${propName}`;
- const existsInRegistry = !!fieldConfigRegistry.getIfExists(fullPath);
- // need to check early here as some standard properties have nested properies
- if (existsInRegistry) {
- found = true;
- continue;
- }
- if (isArray(value) || !isObject(value)) {
- if (!existsInRegistry) {
- unset(obj, propName);
- }
- } else {
- const childPropFound = cleanProperties(value, `${fullPath}.`, fieldConfigRegistry);
- // If no child props found unset the main object
- if (!childPropFound) {
- unset(obj, propName);
- }
- }
- }
- return found;
- }
- function adaptFieldColorMode(
- plugin: PanelPlugin,
- fieldConfig: FieldConfigSource,
- isAfterPluginChange: boolean
- ): FieldConfigSource {
- if (!isAfterPluginChange) {
- return fieldConfig;
- }
- // 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(fieldConfig.defaults.color?.mode);
- // When no support fo value colors, use classic palette
- if (!colorSettings.byValueSupport) {
- if (!mode || mode.isByValue) {
- fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
- return fieldConfig;
- }
- }
- // When supporting value colors and prefering thresholds, use Thresholds mode.
- // Otherwise keep current mode
- if (colorSettings.byValueSupport && colorSettings.preferThresholdsMode && mode?.id !== FieldColorModeId.Fixed) {
- if (!mode || !mode.isByValue) {
- fieldConfig.defaults.color = { mode: FieldColorModeId.Thresholds };
- return fieldConfig;
- }
- }
- // If panel support bySeries then we should default to that when switching to this panel as that is most likely
- // what users will expect. Example scenario a user who has a graph panel (time series) and switches to Gauge and
- // then back to time series we want the graph panel color mode to reset to classic palette and not preserve the
- // Gauge prefered thresholds mode.
- if (colorSettings.bySeriesSupport && mode?.isByValue) {
- fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
- return fieldConfig;
- }
- }
- return fieldConfig;
- }
- function fixThresholds(thresholds: ThresholdsConfig) {
- if (!thresholds.mode) {
- thresholds.mode = ThresholdsMode.Absolute;
- }
- if (!thresholds.steps) {
- thresholds.steps = [];
- } else if (thresholds.steps.length) {
- // First value is always -Infinity
- // JSON saves it as null
- thresholds.steps[0].value = -Infinity;
- }
- }
- export function restoreCustomOverrideRules(current: FieldConfigSource, old: FieldConfigSource): FieldConfigSource {
- const result = {
- defaults: {
- ...current.defaults,
- custom: old.defaults.custom,
- },
- overrides: [...current.overrides],
- };
- for (const override of old.overrides) {
- for (const prop of override.properties) {
- if (isCustomFieldProp(prop)) {
- const currentOverride = result.overrides.find((o) => isEqual(o.matcher, override.matcher));
- if (currentOverride) {
- if (currentOverride !== override) {
- currentOverride.properties.push(prop);
- }
- } else {
- result.overrides.push(override);
- }
- }
- }
- }
- return result;
- }
- export function isCustomFieldProp(prop: DynamicConfigValue): boolean {
- return prop.id.startsWith('custom.');
- }
- export function isStandardFieldProp(prop: DynamicConfigValue): boolean {
- return !isCustomFieldProp(prop);
- }
|