From 01deae2105aa66eba8e4143a5d3db41c68e22abe Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Thu, 23 Sep 2021 09:28:16 -0700 Subject: [PATCH] Options: support array value paths for panel options (#39499) --- .../src/panel/PanelPlugin.test.tsx | 21 +++++++++++++++++++ .../src/panel/registryFactories.ts | 7 +++++++ .../components/PanelEditor/utils.test.ts | 17 ++++++++------- .../dashboard/components/PanelEditor/utils.ts | 15 +++++++++++++ 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/packages/grafana-data/src/panel/PanelPlugin.test.tsx b/packages/grafana-data/src/panel/PanelPlugin.test.tsx index 2e238e6af57..9b705d01fae 100644 --- a/packages/grafana-data/src/panel/PanelPlugin.test.tsx +++ b/packages/grafana-data/src/panel/PanelPlugin.test.tsx @@ -182,6 +182,27 @@ describe('PanelPlugin', () => { expect(panel.fieldConfigDefaults.defaults.custom).toEqual(expectedDefaults); }); + test('throw error with array fieldConfigs', () => { + const panel = new PanelPlugin(() => { + return
Panel
; + }); + + panel.useFieldConfig({ + useCustomConfig: (builder) => { + builder.addCustomEditor({ + id: 'somethingUnique', + path: 'numericOption[0]', + name: 'Option editor', + description: 'Option editor description', + defaultValue: 10, + } as any); + }, + }); + expect(() => panel.fieldConfigRegistry).toThrowErrorMatchingInlineSnapshot( + `"[undefined] Field config paths do not support arrays: custom.somethingUnique"` + ); + }); + test('default values for nested paths', () => { const panel = new PanelPlugin(() => { return
Panel
; diff --git a/packages/grafana-data/src/panel/registryFactories.ts b/packages/grafana-data/src/panel/registryFactories.ts index 7583df57e82..df811a35e20 100644 --- a/packages/grafana-data/src/panel/registryFactories.ts +++ b/packages/grafana-data/src/panel/registryFactories.ts @@ -75,6 +75,13 @@ export function createFieldConfigRegistry( } } + // assert that field configs do not use array path syntax + for (const item of registry.list()) { + if (item.path.indexOf('[') > 0) { + throw new Error(`[${pluginName}] Field config paths do not support arrays: ${item.id}`); + } + } + return registry; } diff --git a/public/app/features/dashboard/components/PanelEditor/utils.test.ts b/public/app/features/dashboard/components/PanelEditor/utils.test.ts index c495c4c84e3..6c3df98517c 100644 --- a/public/app/features/dashboard/components/PanelEditor/utils.test.ts +++ b/public/app/features/dashboard/components/PanelEditor/utils.test.ts @@ -89,13 +89,16 @@ describe('updateDefaultFieldConfigValue', () => { describe('setOptionImmutably', () => { it.each` - source | path | value | expected - ${{}} | ${'a'} | ${1} | ${{ a: 1 }} - ${{}} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } } }} - ${{ a: {} }} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } } }} - ${{ b: {} }} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } }, b: {} }} - ${{ a: { b: { c: 3 } } }} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } } }} - `('numeric-like text mapping, value:${value', ({ source, path, value, expected }) => { + source | path | value | expected + ${{}} | ${'a'} | ${1} | ${{ a: 1 }} + ${{}} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } } }} + ${{ a: {} }} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } } }} + ${{ b: {} }} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } }, b: {} }} + ${{ a: { b: { c: 3 } } }} | ${'a.b.c'} | ${[1, 2]} | ${{ a: { b: { c: [1, 2] } } }} + ${{}} | ${'a.b[2]'} | ${'x'} | ${{ a: { b: [undefined, undefined, 'x'] } }} + ${{}} | ${'a[0]'} | ${1} | ${{ a: [1] }} + ${{}} | ${'a[0].b.c'} | ${1} | ${{ a: [{ b: { c: 1 } }] }} + `('property value:${value', ({ source, path, value, expected }) => { expect(setOptionImmutably(source, path, value)).toEqual(expected); }); diff --git a/public/app/features/dashboard/components/PanelEditor/utils.ts b/public/app/features/dashboard/components/PanelEditor/utils.ts index 74574949800..f7febcd196f 100644 --- a/public/app/features/dashboard/components/PanelEditor/utils.ts +++ b/public/app/features/dashboard/components/PanelEditor/utils.ts @@ -68,6 +68,21 @@ export function setOptionImmutably(options: T, path: string | const splat = !Array.isArray(path) ? path.split('.') : path; const key = splat.shift()!; + if (key.endsWith(']')) { + const idx = key.lastIndexOf('['); + const index = +key.substring(idx + 1, key.length - 1); + const propKey = key.substr(0, idx); + let current = (options as Record)[propKey]; + const arr = Array.isArray(current) ? [...current] : []; + if (splat.length) { + if (current == null || typeof current !== 'object') { + current = {}; + } + value = setOptionImmutably(current, splat, value); + } + arr[index] = value; + return { ...options, [propKey]: arr }; + } if (!splat.length) { return { ...options, [key]: value };