From e04e3e7d4666093ab53f9e47ee8d83bcb405adf8 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 15 Sep 2020 10:17:58 +0200 Subject: [PATCH] Field config: Add support for paths in default field config setup (#27570) * Add support for paths in default field config setup * Typecheck fix --- .../PanelEditor/FieldConfigEditor.tsx | 35 ++++------------ .../components/PanelEditor/utils.test.ts | 40 ++++++++++++++++++- .../dashboard/components/PanelEditor/utils.ts | 34 +++++++++++++++- 3 files changed, 78 insertions(+), 31 deletions(-) diff --git a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx index 80341118fc0..8cac0fc5d65 100644 --- a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx @@ -1,13 +1,13 @@ -import React, { useCallback, ReactNode } from 'react'; +import React, { ReactNode, useCallback } from 'react'; import cloneDeep from 'lodash/cloneDeep'; import { DataFrame, + DocsId, FieldConfigPropertyItem, FieldConfigSource, PanelPlugin, SelectableValue, VariableSuggestionsScope, - DocsId, } from '@grafana/data'; import { Container, Counter, FeatureInfoBox, Field, fieldMatchersUI, Label, useTheme, ValuePicker } from '@grafana/ui'; import { getDataLinksVariableSuggestions } from '../../../panel/panellinks/link_srv'; @@ -17,6 +17,7 @@ import { OptionsGroup } from './OptionsGroup'; import { selectors } from '@grafana/e2e-selectors'; import { css } from 'emotion'; import { getDocsLink } from 'app/core/utils/docsLinks'; +import { updateDefaultFieldConfigValue } from './utils'; interface Props { plugin: PanelPlugin; @@ -32,6 +33,7 @@ interface Props { export const OverrideFieldConfigEditor: React.FC = props => { const theme = useTheme(); const { config } = props; + const onOverrideChange = (index: number, override: any) => { const { config } = props; let overrides = cloneDeep(config.overrides); @@ -128,32 +130,9 @@ export const OverrideFieldConfigEditor: React.FC = props => { }; export const DefaultFieldConfigEditor: React.FC = ({ data, onChange, config, plugin }) => { - const setDefaultValue = useCallback( + const onDefaultValueChange = useCallback( (name: string, value: any, isCustom: boolean | undefined) => { - const defaults = { ...config.defaults }; - const remove = value === undefined || value === null || ''; - - if (isCustom) { - if (defaults.custom) { - if (remove) { - defaults.custom = { ...defaults.custom }; - delete defaults.custom[name]; - } else { - defaults.custom = { ...defaults.custom, [name]: value }; - } - } else if (!remove) { - defaults.custom = { [name]: value }; - } - } else if (remove) { - delete (defaults as any)[name]; - } else { - (defaults as any)[name] = value; - } - - onChange({ - ...config, - defaults, - }); + onChange(updateDefaultFieldConfigValue(config, name, value, isCustom)); }, [config, onChange] ); @@ -187,7 +166,7 @@ export const DefaultFieldConfigEditor: React.FC = ({ data, onChange, conf setDefaultValue(item.path, v, item.isCustom)} + onChange={v => onDefaultValueChange(item.path, v, item.isCustom)} context={{ data, getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope), diff --git a/public/app/features/dashboard/components/PanelEditor/utils.test.ts b/public/app/features/dashboard/components/PanelEditor/utils.test.ts index 97c420d29f7..2daebfc3493 100644 --- a/public/app/features/dashboard/components/PanelEditor/utils.test.ts +++ b/public/app/features/dashboard/components/PanelEditor/utils.test.ts @@ -1,5 +1,5 @@ -import { FieldConfig, PanelPlugin, standardFieldConfigEditorRegistry } from '@grafana/data'; -import { supportsDataQuery } from './utils'; +import { FieldConfig, FieldConfigSource, PanelPlugin, standardFieldConfigEditorRegistry } from '@grafana/data'; +import { supportsDataQuery, updateDefaultFieldConfigValue } from './utils'; describe('standardFieldConfigEditorRegistry', () => { const dummyConfig: FieldConfig = { @@ -50,3 +50,39 @@ describe('supportsDataQuery', () => { }); }); }); + +describe('updateDefaultFieldConfigValue', () => { + it.each` + property | isCustom | newValue | expected + ${'a'} | ${false} | ${2} | ${{ a: 2, b: { c: 'nested default' }, custom: { d: 1, e: { f: 'nested custom' } } }} + ${'b.c'} | ${false} | ${'nested default updated'} | ${{ a: 1, b: { c: 'nested default updated' }, custom: { d: 1, e: { f: 'nested custom' } } }} + ${'a'} | ${false} | ${undefined} | ${{ b: { c: 'nested default' }, custom: { d: 1, e: { f: 'nested custom' } } }} + ${'b'} | ${false} | ${undefined} | ${{ a: 1, custom: { d: 1, e: { f: 'nested custom' } } }} + ${'b.c'} | ${false} | ${undefined} | ${{ a: 1, b: {}, custom: { d: 1, e: { f: 'nested custom' } } }} + ${'d'} | ${true} | ${2} | ${{ a: 1, b: { c: 'nested default' }, custom: { d: 2, e: { f: 'nested custom' } } }} + ${'e.f'} | ${true} | ${'nested custom updated'} | ${{ a: 1, b: { c: 'nested default' }, custom: { d: 1, e: { f: 'nested custom updated' } } }} + ${'d'} | ${true} | ${undefined} | ${{ a: 1, b: { c: 'nested default' }, custom: { e: { f: 'nested custom' } } }} + ${'e'} | ${true} | ${undefined} | ${{ a: 1, b: { c: 'nested default' }, custom: { d: 1 } }} + ${'e.f'} | ${true} | ${undefined} | ${{ a: 1, b: { c: 'nested default' }, custom: { d: 1, e: {} } }} + `( + 'when updating property:$property (is custom: $isCustom) with $newValue', + ({ property, isCustom, newValue, expected }) => { + const config = { + defaults: { + a: 1, + b: { + c: 'nested default', + }, + custom: { + d: 1, + e: { f: 'nested custom' }, + }, + }, + overrides: [], + }; + expect(updateDefaultFieldConfigValue(config as FieldConfigSource, property, newValue, isCustom).defaults).toEqual( + expected + ); + } + ); +}); diff --git a/public/app/features/dashboard/components/PanelEditor/utils.ts b/public/app/features/dashboard/components/PanelEditor/utils.ts index dae7960332e..0c5ceeacfd1 100644 --- a/public/app/features/dashboard/components/PanelEditor/utils.ts +++ b/public/app/features/dashboard/components/PanelEditor/utils.ts @@ -1,8 +1,9 @@ import { CSSProperties } from 'react'; +import { set as lodashSet, omit } from 'lodash'; +import { FieldConfigSource, PanelPlugin } from '@grafana/data'; import { PanelModel } from '../../state/PanelModel'; import { DisplayMode } from './types'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants'; -import { PanelPlugin } from '@grafana/data'; export function calculatePanelSize(mode: DisplayMode, width: number, height: number, panel: PanelModel): CSSProperties { if (mode === DisplayMode.Fill) { @@ -29,3 +30,34 @@ export function calculatePanelSize(mode: DisplayMode, width: number, height: num export function supportsDataQuery(plugin: PanelPlugin | undefined): boolean { return plugin?.meta.skipDataQuery === false; } + +export const updateDefaultFieldConfigValue = ( + config: FieldConfigSource, + name: string, + value: any, + isCustom?: boolean +) => { + let defaults = { ...config.defaults }; + const remove = value === undefined || value === null || ''; + + if (isCustom) { + if (defaults.custom) { + if (remove) { + defaults.custom = omit(defaults.custom, name); + } else { + defaults.custom = lodashSet({ ...defaults.custom }, name, value); + } + } else if (!remove) { + defaults.custom = lodashSet({ ...defaults.custom }, name, value); + } + } else if (remove) { + defaults = omit(defaults, name); + } else { + defaults = lodashSet({ ...defaults }, name, value); + } + + return { + ...config, + defaults, + }; +};