From c348a095b9de78917bce6f70ea06de1760fb9996 Mon Sep 17 00:00:00 2001 From: Yongjie Zhao Date: Tue, 28 Jun 2022 21:09:42 +0800 Subject: [PATCH] refactor(standardized form data): refine interface and improve code smells (#20518) --- .../superset-ui-chart-controls/src/types.ts | 28 ++-- .../src/utils/getStandardizedControls.ts | 64 +++++++++ .../src/utils/index.ts | 1 + .../utils/getStandardizedControls.test.ts | 80 +++++++++++ .../src/query/types/Column.ts | 17 ++- .../src/query/types/Metric.ts | 24 +++- .../src/query/types/QueryFormData.ts | 4 - .../test/query/types/Column.test.ts | 63 +++++++++ .../test/query/types/Metric.test.ts | 86 ++++++++++++ .../src/controlPanel.ts | 5 +- .../src/Bar/controlPanel.ts | 7 +- .../src/DistBar/controlPanel.ts | 15 ++- .../src/Line/controlPanel.ts | 12 +- .../BigNumber/BigNumberTotal/controlPanel.ts | 9 +- .../BigNumberWithTrendline/controlPanel.tsx | 9 +- .../src/BoxPlot/controlPanel.ts | 15 ++- .../src/Funnel/controlPanel.tsx | 11 +- .../src/Gauge/controlPanel.tsx | 11 +- .../src/Graph/controlPanel.tsx | 9 +- .../src/MixedTimeseries/controlPanel.tsx | 23 +++- .../src/Pie/controlPanel.tsx | 11 +- .../src/Radar/controlPanel.tsx | 7 +- .../src/Timeseries/Area/controlPanel.tsx | 7 +- .../Timeseries/Regular/Bar/controlPanel.tsx | 7 +- .../Timeseries/Regular/Line/controlPanel.tsx | 7 +- .../Regular/Scatter/controlPanel.tsx | 7 +- .../Regular/SmoothLine/controlPanel.tsx | 7 +- .../src/Timeseries/Step/controlPanel.tsx | 7 +- .../src/Tree/controlPanel.tsx | 9 +- .../src/Treemap/controlPanel.tsx | 11 +- .../src/plugin/controlPanel.tsx | 14 +- .../plugin-chart-table/src/controlPanel.tsx | 7 +- .../controlUtils/standardizedFormData.test.ts | 85 +++++++++--- .../controlUtils/standardizedFormData.ts | 125 ++++++++---------- 34 files changed, 580 insertions(+), 224 deletions(-) create mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/utils/getStandardizedControls.ts create mode 100644 superset-frontend/packages/superset-ui-chart-controls/test/utils/getStandardizedControls.test.ts create mode 100644 superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts create mode 100644 superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts index cdf3e441d7..f02ad52cff 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts @@ -344,30 +344,36 @@ export interface ControlPanelSectionConfig { controlSetRows: ControlSetRow[]; } -export interface StandardizedState { +export interface StandardizedControls { metrics: QueryFormMetric[]; columns: QueryFormColumn[]; } export interface StandardizedFormDataInterface { - standardizedState: StandardizedState; + // Controls not used in the current viz + controls: StandardizedControls; + // Transformation history memorizedFormData: Map; } +export type QueryStandardizedFormData = QueryFormData & { + standardizedFormData: StandardizedFormDataInterface; +}; + +export const isStandardizedFormData = ( + formData: QueryFormData, +): formData is QueryStandardizedFormData => + formData?.standardizedFormData?.controls && + formData?.standardizedFormData?.memorizedFormData && + Array.isArray(formData.standardizedFormData.controls.metrics) && + Array.isArray(formData.standardizedFormData.controls.columns); + export interface ControlPanelConfig { controlPanelSections: (ControlPanelSectionConfig | null)[]; controlOverrides?: ControlOverrides; sectionOverrides?: SectionOverrides; onInit?: (state: ControlStateMapping) => void; - denormalizeFormData?: ( - formData: QueryFormData & { - standardizedFormData: StandardizedFormDataInterface; - }, - ) => QueryFormData; - updateStandardizedState?: ( - prevState: StandardizedState, - currState: StandardizedState, - ) => StandardizedState; + formDataOverrides?: (formData: QueryFormData) => QueryFormData; } export type ControlOverrides = { diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/getStandardizedControls.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/getStandardizedControls.ts new file mode 100644 index 0000000000..650e063d60 --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/getStandardizedControls.ts @@ -0,0 +1,64 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { makeSingleton, QueryFormData } from '@superset-ui/core'; +import { isStandardizedFormData, StandardizedControls } from '../types'; + +class StandardizedControlsManager { + controls: StandardizedControls; + + constructor() { + this.controls = { + metrics: [], + columns: [], + }; + } + + setStandardizedControls(formData: QueryFormData) { + if (isStandardizedFormData(formData)) { + const { controls } = formData.standardizedFormData; + this.controls = { + metrics: controls.metrics, + columns: controls.columns, + }; + } + } + + shiftMetric() { + return this.controls.metrics.shift(); + } + + popAllMetrics() { + return this.controls.metrics.splice(0, this.controls.metrics.length); + } + + popAllColumns() { + return this.controls.columns.splice(0, this.controls.columns.length); + } + + clear() { + this.controls = { + metrics: [], + columns: [], + }; + } +} + +export const getStandardizedControls = makeSingleton( + StandardizedControlsManager, +); diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts index 11c03e4ca1..bd5fae8030 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts @@ -23,3 +23,4 @@ export * from './getColorFormatters'; export { default as mainMetric } from './mainMetric'; export { default as columnChoices } from './columnChoices'; export * from './defineSavedMetrics'; +export * from './getStandardizedControls'; diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/getStandardizedControls.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/getStandardizedControls.test.ts new file mode 100644 index 0000000000..549c56237b --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/getStandardizedControls.test.ts @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { QueryFormData } from '@superset-ui/core'; +import { getStandardizedControls } from '../../src'; + +const formData: QueryFormData = { + datasource: '30__table', + viz_type: 'table', + standardizedFormData: { + controls: { + metrics: ['count(*)', 'sum(sales)'], + columns: ['gender', 'gender'], + }, + memorizedFormData: [], + }, +}; + +test('without standardizedFormData', () => { + getStandardizedControls().setStandardizedControls({ + datasource: '30__table', + viz_type: 'table', + }); + expect(getStandardizedControls().controls).toEqual({ + metrics: [], + columns: [], + }); +}); + +test('getStandardizedControls', () => { + expect(getStandardizedControls().controls).toEqual({ + metrics: [], + columns: [], + }); + getStandardizedControls().setStandardizedControls(formData); + expect(getStandardizedControls().controls).toEqual({ + metrics: ['count(*)', 'sum(sales)'], + columns: ['gender', 'gender'], + }); + expect(getStandardizedControls().shiftMetric()).toEqual('count(*)'); + expect(getStandardizedControls().controls).toEqual({ + metrics: ['sum(sales)'], + columns: ['gender', 'gender'], + }); + expect(getStandardizedControls().popAllMetrics()).toEqual(['sum(sales)']); + expect(getStandardizedControls().controls).toEqual({ + metrics: [], + columns: ['gender', 'gender'], + }); + expect(getStandardizedControls().popAllColumns()).toEqual([ + 'gender', + 'gender', + ]); + expect(getStandardizedControls().controls).toEqual({ + metrics: [], + columns: [], + }); + + getStandardizedControls().setStandardizedControls(formData); + getStandardizedControls().clear(); + expect(getStandardizedControls().controls).toEqual({ + metrics: [], + columns: [], + }); +}); diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts index 78e4a301c9..cbb9a23f64 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts @@ -19,6 +19,7 @@ */ import { GenericDataType } from './QueryResponse'; +import { QueryFormColumn } from './QueryFormData'; export interface AdhocColumn { hasCustomLabel?: boolean; @@ -53,12 +54,18 @@ export interface Column { export default {}; -export function isPhysicalColumn( - column?: AdhocColumn | PhysicalColumn, -): column is PhysicalColumn { +export function isPhysicalColumn(column?: any): column is PhysicalColumn { return typeof column === 'string'; } -export function isAdhocColumn(column?: AdhocColumn | PhysicalColumn) { - return (column as AdhocColumn)?.sqlExpression !== undefined; +export function isAdhocColumn(column?: any): column is AdhocColumn { + return ( + typeof column !== 'string' && + column?.sqlExpression !== undefined && + column?.expressionType === 'SQL' + ); +} + +export function isQueryFormColumn(column: any): column is QueryFormColumn { + return isPhysicalColumn(column) || isAdhocColumn(column); } diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts index 396ccd4c5b..8d3e37ccfe 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts @@ -17,7 +17,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Maybe } from '../../types'; +import { Maybe, QueryFormMetric } from '../../types'; import { Column } from './Column'; export type Aggregate = @@ -74,8 +74,22 @@ export interface Metric { export default {}; -export function isAdhocMetricSimple( - metric: AdhocMetric, -): metric is AdhocMetricSimple { - return metric.expressionType === 'SIMPLE'; +export function isSavedMetric(metric: any): metric is SavedMetric { + return typeof metric === 'string'; +} + +export function isAdhocMetricSimple(metric: any): metric is AdhocMetricSimple { + return typeof metric !== 'string' && metric?.expressionType === 'SIMPLE'; +} + +export function isAdhocMetricSQL(metric: any): metric is AdhocMetricSQL { + return typeof metric !== 'string' && metric?.expressionType === 'SQL'; +} + +export function isQueryFormMetric(metric: any): metric is QueryFormMetric { + return ( + isSavedMetric(metric) || + isAdhocMetricSimple(metric) || + isAdhocMetricSQL(metric) + ); } diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts b/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts index ac57561d5d..27555f849d 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts @@ -220,8 +220,4 @@ export function isDruidFormData( return 'granularity' in formData; } -export function isSavedMetric(metric: QueryFormMetric): metric is SavedMetric { - return typeof metric === 'string'; -} - export default {}; diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts new file mode 100644 index 0000000000..d4391cfd01 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + isAdhocColumn, + isPhysicalColumn, + isQueryFormColumn, +} from '@superset-ui/core'; + +const adhocColumn = { + expressionType: 'SQL', + label: 'country', + optionName: 'country', + sqlExpression: 'country', +}; + +test('isPhysicalColumn returns true', () => { + expect(isPhysicalColumn('gender')).toEqual(true); +}); + +test('isPhysicalColumn returns false', () => { + expect(isPhysicalColumn(adhocColumn)).toEqual(false); +}); + +test('isAdhocColumn returns true', () => { + expect(isAdhocColumn(adhocColumn)).toEqual(true); +}); + +test('isAdhocColumn returns false', () => { + expect(isAdhocColumn('hello')).toEqual(false); + expect(isAdhocColumn({})).toEqual(false); + expect( + isAdhocColumn({ + expressionType: 'SQL', + label: 'country', + optionName: 'country', + }), + ).toEqual(false); +}); + +test('isQueryFormColumn returns true', () => { + expect(isQueryFormColumn('gender')).toEqual(true); + expect(isQueryFormColumn(adhocColumn)).toEqual(true); +}); + +test('isQueryFormColumn returns false', () => { + expect(isQueryFormColumn({})).toEqual(false); +}); diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts new file mode 100644 index 0000000000..25ea6c80a7 --- /dev/null +++ b/superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts @@ -0,0 +1,86 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + isSavedMetric, + isAdhocMetricSimple, + isAdhocMetricSQL, + isQueryFormMetric, +} from '@superset-ui/core'; + +const adhocMetricSimple = { + expressionType: 'SIMPLE', + column: { + id: 1, + column_name: 'sales', + columnName: 'sales', + verbose_name: 'sales', + }, + aggregate: 'SUM', + label: 'count', + optionName: 'count', +}; + +const adhocMetricSQL = { + expressionType: 'SQL', + label: 'count', + optionName: 'count', + sqlExpression: 'count(*)', +}; + +test('isSavedMetric returns true', () => { + expect(isSavedMetric('count(*)')).toEqual(true); +}); + +test('isSavedMetric returns false', () => { + expect(isSavedMetric(adhocMetricSQL)).toEqual(false); + expect(isSavedMetric(null)).toEqual(false); + expect(isSavedMetric(undefined)).toEqual(false); +}); + +test('isAdhocMetricSimple returns true', () => { + expect(isAdhocMetricSimple(adhocMetricSimple)).toEqual(true); +}); + +test('isAdhocMetricSimple returns false', () => { + expect(isAdhocMetricSimple('hello')).toEqual(false); + expect(isAdhocMetricSimple({})).toEqual(false); + expect(isAdhocMetricSimple(adhocMetricSQL)).toEqual(false); +}); + +test('isAdhocMetricSQL returns true', () => { + expect(isAdhocMetricSQL(adhocMetricSQL)).toEqual(true); +}); + +test('isAdhocMetricSQL returns false', () => { + expect(isAdhocMetricSQL('hello')).toEqual(false); + expect(isAdhocMetricSQL({})).toEqual(false); + expect(isAdhocMetricSQL(adhocMetricSimple)).toEqual(false); +}); + +test('isQueryFormMetric returns true', () => { + expect(isQueryFormMetric(adhocMetricSQL)).toEqual(true); + expect(isQueryFormMetric(adhocMetricSimple)).toEqual(true); + expect(isQueryFormMetric('count(*)')).toEqual(true); +}); + +test('isQueryFormMetric returns false', () => { + expect(isQueryFormMetric({})).toEqual(false); + expect(isQueryFormMetric(undefined)).toEqual(false); + expect(isQueryFormMetric(null)).toEqual(false); +}); diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts index f9f7dfb09d..455ee09820 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanel.ts @@ -20,6 +20,7 @@ import { t } from '@superset-ui/core'; import { ControlPanelConfig, formatSelectOptions, + getStandardizedControls, sections, } from '@superset-ui/chart-controls'; import { ColorBy } from './utils'; @@ -152,9 +153,9 @@ const config: ControlPanelConfig = { Boolean(controls?.color_by.value === ColorBy.country), }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, + metrics: getStandardizedControls().popAllMetrics(), }), }; diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Bar/controlPanel.ts b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Bar/controlPanel.ts index 91af47f1f7..45c145d82b 100644 --- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Bar/controlPanel.ts +++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Bar/controlPanel.ts @@ -19,6 +19,7 @@ import { t } from '@superset-ui/core'; import { ControlPanelConfig, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -122,10 +123,10 @@ const config: ControlPanelConfig = { timeSeriesSection[1], sections.annotations, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/controlPanel.ts b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/controlPanel.ts index 5d526b43e3..58033938f1 100644 --- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/controlPanel.ts +++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/controlPanel.ts @@ -22,6 +22,7 @@ import { ControlPanelConfig, sections, sharedControls, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { showLegend, @@ -133,14 +134,18 @@ const config: ControlPanelConfig = { rerender: ['groupby'], }, }, - denormalizeFormData: formData => { - const columns = - formData.standardizedFormData.standardizedState.columns.filter( - col => !ensureIsArray(formData.groupby).includes(col), + formDataOverrides: formData => { + const columns = getStandardizedControls().controls.columns.filter( + col => !ensureIsArray(formData.groupby).includes(col), + ); + getStandardizedControls().controls.columns = + getStandardizedControls().controls.columns.filter( + col => !columns.includes(col), ); + return { ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, + metrics: getStandardizedControls().popAllMetrics(), columns, }; }, diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Line/controlPanel.ts b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Line/controlPanel.ts index fa4738ebfd..9662cc11d8 100644 --- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Line/controlPanel.ts +++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/Line/controlPanel.ts @@ -17,7 +17,11 @@ * under the License. */ import { t } from '@superset-ui/core'; -import { ControlPanelConfig, sections } from '@superset-ui/chart-controls'; +import { + ControlPanelConfig, + sections, + getStandardizedControls, +} from '@superset-ui/chart-controls'; import { lineInterpolation, showBrush, @@ -96,10 +100,10 @@ const config: ControlPanelConfig = { default: 50000, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts index 94dd458305..f179c6299b 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.ts @@ -21,6 +21,7 @@ import { ControlPanelConfig, D3_FORMAT_DOCS, D3_TIME_FORMAT_OPTIONS, + getStandardizedControls, sections, } from '@superset-ui/chart-controls'; import { headerFontSize, subheaderFontSize } from '../sharedControls'; @@ -96,12 +97,8 @@ export default { label: t('Number format'), }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().shiftMetric(), }), } as ControlPanelConfig; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx index 0ea0a96229..d88d105f27 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/controlPanel.tsx @@ -22,6 +22,7 @@ import { D3_FORMAT_DOCS, D3_TIME_FORMAT_OPTIONS, formatSelectOptions, + getStandardizedControls, sections, } from '@superset-ui/chart-controls'; import React from 'react'; @@ -270,13 +271,9 @@ const config: ControlPanelConfig = { label: t('Number format'), }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().shiftMetric(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BoxPlot/controlPanel.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BoxPlot/controlPanel.ts index b92c289bb4..8a85c42bfa 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BoxPlot/controlPanel.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BoxPlot/controlPanel.ts @@ -25,6 +25,7 @@ import { sections, emitFilterControl, ControlPanelConfig, + getStandardizedControls, } from '@superset-ui/chart-controls'; const config: ControlPanelConfig = { @@ -136,14 +137,18 @@ const config: ControlPanelConfig = { ), }, }, - denormalizeFormData: formData => { - const groupby = - formData.standardizedFormData.standardizedState.columns.filter( - col => !ensureIsArray(formData.columns).includes(col), + formDataOverrides: formData => { + const groupby = getStandardizedControls().controls.columns.filter( + col => !ensureIsArray(formData.columns).includes(col), + ); + getStandardizedControls().controls.columns = + getStandardizedControls().controls.columns.filter( + col => !groupby.includes(col), ); + return { ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, + metrics: getStandardizedControls().popAllMetrics(), groupby, }; }, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx index b23cf2ed97..a1d3cf79f7 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Funnel/controlPanel.tsx @@ -25,6 +25,7 @@ import { sharedControls, ControlStateMapping, emitFilterControl, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { DEFAULT_FORM_DATA, EchartsFunnelLabelTypeType } from './types'; import { legendSection } from '../controls'; @@ -143,14 +144,10 @@ const config: ControlPanelConfig = { }, }; }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - groupby: formData.standardizedFormData.standardizedState.columns, - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().shiftMetric(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx index bb727888e5..14f5460c8a 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Gauge/controlPanel.tsx @@ -24,6 +24,7 @@ import { D3_FORMAT_OPTIONS, sections, emitFilterControl, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { DEFAULT_FORM_DATA } from './types'; @@ -308,14 +309,10 @@ const config: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - groupby: formData.standardizedFormData.standardizedState.columns, - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().shiftMetric(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Graph/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Graph/controlPanel.tsx index 218b1d0335..fef948f5a6 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Graph/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Graph/controlPanel.tsx @@ -20,6 +20,7 @@ import React from 'react'; import { t } from '@superset-ui/core'; import { ControlPanelConfig, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -320,13 +321,9 @@ const controlPanel: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().popAllMetrics(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx index 4ff8831800..7f5e1c8c28 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/controlPanel.tsx @@ -30,6 +30,7 @@ import { ControlSetRow, CustomControlItem, emitFilterControl, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -449,15 +450,23 @@ const config: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => { - const groupby = - formData.standardizedFormData.standardizedState.columns.filter( - col => !ensureIsArray(formData.groupby_b).includes(col), + formDataOverrides: formData => { + const groupby = getStandardizedControls().controls.columns.filter( + col => !ensureIsArray(formData.groupby_b).includes(col), + ); + getStandardizedControls().controls.columns = + getStandardizedControls().controls.columns.filter( + col => !groupby.includes(col), ); - const metrics = - formData.standardizedFormData.standardizedState.metrics.filter( - metric => !ensureIsArray(formData.metrics_b).includes(metric), + + const metrics = getStandardizedControls().controls.metrics.filter( + metric => !ensureIsArray(formData.metrics_b).includes(metric), + ); + getStandardizedControls().controls.metrics = + getStandardizedControls().controls.metrics.filter( + col => !metrics.includes(col), ); + return { ...formData, metrics, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx index 7b948208a4..5468a3a94c 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx @@ -26,6 +26,7 @@ import { D3_TIME_FORMAT_OPTIONS, sections, emitFilterControl, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { DEFAULT_FORM_DATA } from './types'; import { legendSection } from '../controls'; @@ -253,17 +254,13 @@ const config: ControlPanelConfig = { default: 100, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - groupby: formData.standardizedFormData.standardizedState.columns, + metric: getStandardizedControls().shiftMetric(), + groupby: getStandardizedControls().popAllColumns(), row_limit: ensureIsInt(formData.row_limit, 100) >= 100 ? 100 : formData.row_limit, }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], - }), }; export default config; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx index d78d935a79..4d03d07373 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx @@ -33,6 +33,7 @@ import { sharedControls, emitFilterControl, ControlFormItemSpec, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { DEFAULT_FORM_DATA } from './types'; import { LABEL_POSITION } from '../constants'; @@ -210,10 +211,10 @@ const config: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx index 751191c4df..520772375e 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Area/controlPanel.tsx @@ -22,6 +22,7 @@ import { ControlPanelConfig, ControlPanelsContainerProps, D3_TIME_FORMAT_DOCS, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -276,10 +277,10 @@ const config: ControlPanelConfig = { default: rowLimit, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx index 85d631d719..5d30ee37e0 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx @@ -25,6 +25,7 @@ import { ControlStateMapping, D3_TIME_FORMAT_DOCS, formatSelectOptions, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -328,10 +329,10 @@ const config: ControlPanelConfig = { default: rowLimit, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx index c12d516836..736d8b1054 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Line/controlPanel.tsx @@ -22,6 +22,7 @@ import { ControlPanelConfig, ControlPanelsContainerProps, D3_TIME_FORMAT_DOCS, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -263,10 +264,10 @@ const config: ControlPanelConfig = { default: rowLimit, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx index 2d65d35dea..3ffdb4489e 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx @@ -22,6 +22,7 @@ import { ControlPanelConfig, ControlPanelsContainerProps, D3_TIME_FORMAT_DOCS, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -207,10 +208,10 @@ const config: ControlPanelConfig = { default: rowLimit, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx index a0e6f2c40c..88a8b1a2fe 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/SmoothLine/controlPanel.tsx @@ -22,6 +22,7 @@ import { ControlPanelConfig, ControlPanelsContainerProps, D3_TIME_FORMAT_DOCS, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -207,10 +208,10 @@ const config: ControlPanelConfig = { default: rowLimit, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx index 1beac8c235..5e02cbc59b 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Step/controlPanel.tsx @@ -22,6 +22,7 @@ import { ControlPanelConfig, ControlPanelsContainerProps, D3_TIME_FORMAT_DOCS, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -260,10 +261,10 @@ const config: ControlPanelConfig = { default: rowLimit, }, }, - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx index c115f5c5c2..79e0639277 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Tree/controlPanel.tsx @@ -20,6 +20,7 @@ import React from 'react'; import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; import { ControlPanelConfig, + getStandardizedControls, sections, sharedControls, } from '@superset-ui/chart-controls'; @@ -285,13 +286,9 @@ const controlPanel: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().shiftMetric(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx index e7cca1af26..2b816b3f25 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Treemap/controlPanel.tsx @@ -25,6 +25,7 @@ import { D3_TIME_FORMAT_OPTIONS, sections, emitFilterControl, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { DEFAULT_FORM_DATA } from './types'; @@ -137,14 +138,10 @@ const config: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metric: formData.standardizedFormData.standardizedState.metrics[0], - groupby: formData.standardizedFormData.standardizedState.columns, - }), - updateStandardizedState: (prevState, currState) => ({ - ...currState, - metrics: [currState.metrics[0], ...prevState.metrics.slice(1)], + metric: getStandardizedControls().shiftMetric(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx index ce09ae1345..5427410b16 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx @@ -32,6 +32,7 @@ import { sharedControls, emitFilterControl, Dataset, + getStandardizedControls, } from '@superset-ui/chart-controls'; import { MetricsLayoutEnum } from '../types'; @@ -373,14 +374,17 @@ const config: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => { - const groupbyColumns = - formData.standardizedFormData.standardizedState.columns.filter( - col => !ensureIsArray(formData.groupbyRows).includes(col), + formDataOverrides: formData => { + const groupbyColumns = getStandardizedControls().controls.columns.filter( + col => !ensureIsArray(formData.groupbyRows).includes(col), + ); + getStandardizedControls().controls.columns = + getStandardizedControls().controls.columns.filter( + col => !groupbyColumns.includes(col), ); return { ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, + metrics: getStandardizedControls().popAllMetrics(), groupbyColumns, }; }, diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index 427f671c1a..8ef66e8a9c 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -47,6 +47,7 @@ import { Dataset, ColumnMeta, defineSavedMetrics, + getStandardizedControls, } from '@superset-ui/chart-controls'; import i18n from './i18n'; @@ -544,10 +545,10 @@ const config: ControlPanelConfig = { ], }, ], - denormalizeFormData: formData => ({ + formDataOverrides: formData => ({ ...formData, - metrics: formData.standardizedFormData.standardizedState.metrics, - groupby: formData.standardizedFormData.standardizedState.columns, + metrics: getStandardizedControls().popAllMetrics(), + groupby: getStandardizedControls().popAllColumns(), }), }; diff --git a/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts b/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts index 59333077ec..b9e2911e51 100644 --- a/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts +++ b/superset-frontend/src/explore/controlUtils/standardizedFormData.test.ts @@ -16,27 +16,65 @@ * specific language governing permissions and limitations * under the License. */ -import { getChartControlPanelRegistry, QueryFormData } from '@superset-ui/core'; +import { + AdhocColumn, + AdhocMetricSimple, + AdhocMetricSQL, + getChartControlPanelRegistry, + QueryFormData, +} from '@superset-ui/core'; import TableChartPlugin from '@superset-ui/plugin-chart-table'; import { BigNumberTotalChartPlugin } from '@superset-ui/plugin-chart-echarts'; import { sections } from '@superset-ui/chart-controls'; import { StandardizedFormData, - sharedControls, + sharedMetricsKey, + sharedColumnsKey, publicControls, } from './standardizedFormData'; +const adhocColumn: AdhocColumn = { + expressionType: 'SQL', + label: 'country', + optionName: 'country', + sqlExpression: 'country', +}; + +const adhocMetricSQL: AdhocMetricSQL = { + expressionType: 'SQL', + label: 'count', + optionName: 'count', + sqlExpression: 'count(*)', +}; + +const adhocMetricSimple: AdhocMetricSimple = { + expressionType: 'SIMPLE', + column: { + id: 1, + column_name: 'sales', + columnName: 'sales', + verbose_name: 'sales', + }, + aggregate: 'SUM', + label: 'count', + optionName: 'count', +}; + describe('should collect control values and create SFD', () => { + const sharedKey = [...sharedMetricsKey, ...sharedColumnsKey]; const sharedControlsFormData = { // metrics metric: 'm1', metrics: ['m2'], metric_2: 'm3', + size: 'm4', // columns groupby: ['c1'], columns: ['c2'], groupbyColumns: ['c3'], groupbyRows: ['c4'], + series: 'c5', + entity: 'c6', }; const publicControlsFormData = { // time section @@ -112,16 +150,16 @@ describe('should collect control values and create SFD', () => { controlSetRows: [['x_axis']], }, ], - denormalizeFormData: (formData: QueryFormData) => ({ + formDataOverrides: (formData: QueryFormData) => ({ ...formData, - columns: formData.standardizedFormData.standardizedState.columns, - metrics: formData.standardizedFormData.standardizedState.metrics, + columns: formData.standardizedFormData.controls.columns, + metrics: formData.standardizedFormData.controls.metrics, }), }); }); test('should avoid to overlap', () => { - const sharedControlsSet = new Set(Object.keys(sharedControls)); + const sharedControlsSet = new Set(Object.keys(sharedKey)); const publicControlsSet = new Set(publicControls); expect( [...sharedControlsSet].filter((x: string) => publicControlsSet.has(x)), @@ -130,19 +168,17 @@ describe('should collect control values and create SFD', () => { test('should collect all sharedControls', () => { expect(Object.entries(sharedControlsFormData).length).toBe( - Object.entries(sharedControls).length, + Object.entries(sharedKey).length, ); const sfd = new StandardizedFormData(sourceMockFormData); - expect(sfd.serialize().standardizedState.metrics).toEqual([ - 'm1', - 'm2', - 'm3', - ]); - expect(sfd.serialize().standardizedState.columns).toEqual([ + expect(sfd.serialize().controls.metrics).toEqual(['m1', 'm2', 'm3', 'm4']); + expect(sfd.serialize().controls.columns).toEqual([ 'c1', 'c2', 'c3', 'c4', + 'c5', + 'c6', ]); }); @@ -157,8 +193,8 @@ describe('should collect control values and create SFD', () => { expect(formData).toHaveProperty(key); expect(value).toEqual(publicControlsFormData[key]); }); - expect(formData.columns).toEqual(['c1', 'c2', 'c3', 'c4']); - expect(formData.metrics).toEqual(['m1', 'm2', 'm3']); + expect(formData.columns).toEqual(['c1', 'c2', 'c3', 'c4', 'c5', 'c6']); + expect(formData.metrics).toEqual(['m1', 'm2', 'm3', 'm4']); }); test('should inherit standardizedFormData and memorizedFormData is LIFO', () => { @@ -210,8 +246,8 @@ describe('should transform form_data between table and bigNumberTotal', () => { time_grain_sqla: 'P1D', time_range: 'No filter', query_mode: 'aggregate', - groupby: ['name'], - metrics: ['count'], + groupby: ['name', 'gender', adhocColumn], + metrics: ['count', 'avg(sales)', adhocMetricSimple, adhocMetricSQL], all_columns: [], percent_metrics: [], adhoc_filters: [], @@ -259,10 +295,10 @@ describe('should transform form_data between table and bigNumberTotal', () => { value: 'aggregate', }, groupby: { - value: ['name'], + value: ['name', 'gender', adhocColumn], }, metrics: { - value: ['count'], + value: ['count', 'avg(sales)', adhocMetricSimple, adhocMetricSQL], }, all_columns: { value: [], @@ -349,7 +385,7 @@ describe('should transform form_data between table and bigNumberTotal', () => { expect(bntFormData.viz_type).toBe('big_number_total'); expect(bntFormData.metric).toBe('count'); - // change control values + // change control values on bigNumber bntFormData.metric = 'sum(sales)'; bntFormData.time_range = '2021 : 2022'; bntControlsState.metric.value = 'sum(sales)'; @@ -367,8 +403,13 @@ describe('should transform form_data between table and bigNumberTotal', () => { [...Object.keys(tblControlsState), 'standardizedFormData'].sort(), ); expect(tblFormData.viz_type).toBe('table'); - expect(tblFormData.metrics).toEqual(['sum(sales)']); - expect(tblFormData.groupby).toEqual(['name']); + expect(tblFormData.metrics).toEqual([ + 'sum(sales)', + 'avg(sales)', + adhocMetricSimple, + adhocMetricSQL, + ]); + expect(tblFormData.groupby).toEqual(['name', 'gender', adhocColumn]); expect(tblFormData.time_range).toBe('2021 : 2022'); }); }); diff --git a/superset-frontend/src/explore/controlUtils/standardizedFormData.ts b/superset-frontend/src/explore/controlUtils/standardizedFormData.ts index 99b0220da3..50c12230b2 100644 --- a/superset-frontend/src/explore/controlUtils/standardizedFormData.ts +++ b/superset-frontend/src/explore/controlUtils/standardizedFormData.ts @@ -16,38 +16,38 @@ * specific language governing permissions and limitations * under the License. */ -import { isEmpty, intersection } from 'lodash'; import { ensureIsArray, getChartControlPanelRegistry, + QueryFormColumn, QueryFormData, + QueryFormMetric, } from '@superset-ui/core'; import { ControlStateMapping, - StandardizedState, + getStandardizedControls, + isStandardizedFormData, + StandardizedControls, StandardizedFormDataInterface, } from '@superset-ui/chart-controls'; import { getControlsState } from 'src/explore/store'; import { getFormDataFromControls } from './getFormDataFromControls'; -export const sharedControls: Record = { - // metrics - metric: 'metrics', // via sharedControls, scalar - metrics: 'metrics', // via sharedControls, array - metric_2: 'metrics', // via sharedControls, scalar - // columns - groupby: 'columns', // via sharedControls, array - columns: 'columns', // via sharedControls, array - groupbyColumns: 'columns', // via pivot table v2, array - groupbyRows: 'columns', // via pivot table v2, array -}; -const sharedControlsMap: Record = { - metrics: [], - columns: [], -}; -Object.entries(sharedControls).forEach(([key, value]) => - sharedControlsMap[value].push(key), -); +export const sharedMetricsKey = [ + 'metric', // via sharedControls, scalar + 'metrics', // via sharedControls, array + 'metric_2', // via sharedControls, scalar + 'size', // via sharedControls, scalar +]; +export const sharedColumnsKey = [ + 'groupby', // via sharedControls, array + 'columns', // via sharedControls, array + 'groupbyColumns', // via pivot table v2, array + 'groupbyRows', // via pivot table v2, array + 'entity', // via sharedControls, scalar + 'series', // via sharedControls, scalar +]; + export const publicControls = [ // time section 'granularity_sqla', // via sharedControls @@ -100,65 +100,41 @@ export class StandardizedFormData { memorizedFormData.set(vizType, formData); // calculate sharedControls - const standardizedState = - StandardizedFormData.getStandardizedState(formData); + const controls = StandardizedFormData.getStandardizedControls(formData); this.sfd = { - standardizedState, + controls, memorizedFormData, }; } - static getStandardizedState(formData: QueryFormData): StandardizedState { - // 1. collect current sharedControls - let currState: StandardizedState = { + static getStandardizedControls( + formData: QueryFormData, + ): StandardizedControls { + // 1. initial StandardizedControls + const controls: StandardizedControls = { metrics: [], columns: [], }; + + // 2. collect current sharedControls Object.entries(formData).forEach(([key, value]) => { - if (key in sharedControls) { - currState[sharedControls[key]].push(...ensureIsArray(value)); + if (sharedMetricsKey.includes(key)) { + controls.metrics.push(...ensureIsArray(value)); + } + if (sharedColumnsKey.includes(key)) { + controls.columns.push(...ensureIsArray(value)); } }); - // 2. get previous StandardizedState - let prevState: StandardizedState = { - metrics: [], - columns: [], - }; - if ( - formData?.standardizedFormData?.standardizedState && - Array.isArray(formData.standardizedFormData.standardizedState.metrics) && - Array.isArray(formData.standardizedFormData.standardizedState.columns) - ) { - prevState = formData.standardizedFormData.standardizedState; - } - // the initial prevState should equal to currentState - if (isEmpty(prevState.metrics) && isEmpty(prevState.columns)) { - prevState = currState; + // 3. append inherit sharedControls + if (isStandardizedFormData(formData)) { + const { metrics, columns } = formData.standardizedFormData.controls; + controls.metrics.push(...metrics); + controls.columns.push(...columns); } - // 3. inherit SS from previous state if current viz hasn't columns-like controls or metrics-like controls - Object.keys(sharedControlsMap).forEach(key => { - if ( - isEmpty(intersection(Object.keys(formData), sharedControlsMap[key])) - ) { - currState[key] = prevState[key]; - } - }); - - // 4. update hook - const controlPanel = getChartControlPanelRegistry().get(formData.viz_type); - if (controlPanel?.updateStandardizedState) { - currState = controlPanel.updateStandardizedState(prevState, currState); - } - - // 5. clear up - Object.entries(currState).forEach(([key, value]) => { - currState[key] = value.filter(Boolean); - }); - - return currState; + return controls; } private getLatestFormData(vizType: string): QueryFormData { @@ -169,8 +145,8 @@ export class StandardizedFormData { return this.memorizedFormData.slice(-1)[0][1]; } - private get standardizedState() { - return this.sfd.standardizedState; + private get standardizedControls() { + return this.sfd.controls; } private get memorizedFormData() { @@ -179,7 +155,7 @@ export class StandardizedFormData { serialize() { return { - standardizedState: this.standardizedState, + controls: this.standardizedControls, memorizedFormData: this.memorizedFormData, }; } @@ -205,7 +181,7 @@ export class StandardizedFormData { * 2. collect public control values * 3. generate initial targetControlsState * 4. attach `standardizedFormData` to the initial form_data - * 5. call denormalizeFormData to transform initial form_data if the plugin was defined + * 5. call formDataOverrides to transform initial form_data if the plugin was defined * 6. use final form_data to generate controlsState * */ const latestFormData = this.getLatestFormData(targetVizType); @@ -226,8 +202,17 @@ export class StandardizedFormData { }; const controlPanel = getChartControlPanelRegistry().get(targetVizType); - if (controlPanel?.denormalizeFormData) { - const transformed = controlPanel.denormalizeFormData(targetFormData); + if (controlPanel?.formDataOverrides) { + getStandardizedControls().setStandardizedControls(targetFormData); + const transformed = { + ...controlPanel.formDataOverrides(targetFormData), + standardizedFormData: { + controls: { ...getStandardizedControls().controls }, + memorizedFormData: this.memorizedFormData, + }, + }; + getStandardizedControls().clear(); + return { formData: transformed, controlsState: getControlsState(exploreState, transformed),