From ff10e80014d46742327ea070602443431ad944e1 Mon Sep 17 00:00:00 2001 From: Jesse Yang Date: Mon, 1 Mar 2021 15:34:50 -0800 Subject: [PATCH 1/3] refactor: convert controlUtils to TypeScript --- .../src/explore/components/Control.tsx | 2 +- .../components/ControlPanelsContainer.tsx | 12 +- .../explore/controlUtils/getControlConfig.ts | 68 +++++++++++ .../controlUtils/getSectionsToRender.ts | 95 ++++++++++++++++ .../src/explore/controlUtils/index.js | 107 +----------------- 5 files changed, 175 insertions(+), 109 deletions(-) create mode 100644 superset-frontend/src/explore/controlUtils/getControlConfig.ts create mode 100644 superset-frontend/src/explore/controlUtils/getSectionsToRender.ts diff --git a/superset-frontend/src/explore/components/Control.tsx b/superset-frontend/src/explore/components/Control.tsx index f1b6925bd693..d3ae6bb1c17f 100644 --- a/superset-frontend/src/explore/components/Control.tsx +++ b/superset-frontend/src/explore/components/Control.tsx @@ -19,7 +19,7 @@ import React, { ReactNode } from 'react'; import { ControlType } from '@superset-ui/chart-controls'; import { JsonValue, QueryFormData } from '@superset-ui/core'; -import { ExploreActions } from '../actions/exploreActions'; +import { ExploreActions } from 'src/explore/actions/exploreActions'; import controlMap from './controls'; import './Control.less'; diff --git a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx index a53395aa4d35..8fafaa4bbbfc 100644 --- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx +++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx @@ -40,12 +40,13 @@ import Collapse from 'src/common/components/Collapse'; import { PluginContext } from 'src/components/DynamicPlugins'; import Loading from 'src/components/Loading'; -import { sectionsToRender } from 'src/explore/controlUtils'; +import { getSectionsToRender } from 'src/explore/controlUtils'; import { ExploreActions, exploreActions, } from 'src/explore/actions/exploreActions'; import { ExplorePageState } from 'src/explore/reducers/getInitialState'; +import { ChartState } from 'src/explore/types'; import ControlRow from './ControlRow'; import Control from './Control'; @@ -53,7 +54,8 @@ import Control from './Control'; export type ControlPanelsContainerProps = { actions: ExploreActions; datasource_type: DatasourceType; - exploreState: Record; + exploreState: ExplorePageState['explore']; + chart: ChartState; controls: Record; form_data: QueryFormData; isDatasourceMetaLoading: boolean; @@ -100,7 +102,7 @@ const ControlPanelsTabs = styled(Tabs)` } `; -class ControlPanelsContainer extends React.Component { +export class ControlPanelsContainer extends React.Component { // trigger updates to the component when async plugins load static contextType = PluginContext; @@ -111,7 +113,7 @@ class ControlPanelsContainer extends React.Component { + const { + controlOverrides = {}, + controlPanelSections = [], + } = controlPanelConfig; + const control = expandControlConfig( + findControlItem(controlPanelSections, controlKey), + controlOverrides, + ); + return control && 'config' in control ? control.config : control; + }, +); + +/** + * Find control item from control panel config. + */ +export function findControlItem( + controlPanelSections: ControlPanelSectionConfig[], + controlKey: string, +) { + return ( + controlPanelSections + .map(section => section.controlSetRows) + .flat(2) + .find( + control => + controlKey === control || + (control !== null && + typeof control === 'object' && + 'name' in control && + control.name === controlKey), + ) ?? null + ); +} + +export const getControlConfig = function getControlConfig( + controlKey: string, + vizType: string, +) { + const controlPanelConfig = getChartControlPanelRegistry().get(vizType) || {}; + return getMemoizedControlConfig(controlKey, controlPanelConfig); +}; diff --git a/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts b/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts new file mode 100644 index 000000000000..5105b43073f3 --- /dev/null +++ b/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts @@ -0,0 +1,95 @@ +/** + * 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 memoizeOne from 'memoize-one'; +import { + DatasourceType, + getChartControlPanelRegistry, +} from '@superset-ui/core'; +import { + ControlPanelConfig, + expandControlConfig, +} from '@superset-ui/chart-controls'; + +import * as SECTIONS from 'src/explore/controlPanels/sections'; + +const getMemoizedSectionsToRender = memoizeOne( + (datasourceType: DatasourceType, controlPanelConfig: ControlPanelConfig) => { + const { + sectionOverrides = {}, + controlOverrides, + controlPanelSections = [], + } = controlPanelConfig; + + // default control panel sections + const sections = { ...SECTIONS }; + + // apply section overrides + Object.entries(sectionOverrides).forEach(([section, overrides]) => { + if (typeof overrides === 'object' && overrides.constructor === Object) { + sections[section] = { + ...sections[section], + ...overrides, + }; + } else { + sections[section] = overrides; + } + }); + + const { datasourceAndVizType } = sections; + + // list of datasource-specific controls that should be removed + const invalidControls = + datasourceType === 'table' + ? ['granularity', 'druid_time_origin'] + : ['granularity_sqla', 'time_grain_sqla']; + + return [datasourceAndVizType] + .concat(controlPanelSections) + .filter(section => !!section) + .map(section => { + const { controlSetRows } = section; + return { + ...section, + controlSetRows: + controlSetRows?.map(row => + row + .filter( + control => + typeof control === 'string' && + !invalidControls.includes(control), + ) + .map(item => expandControlConfig(item, controlOverrides)), + ) || [], + }; + }); + }, +); + +/** + * Get the clean and processed control panel sections + */ +export function getSectionsToRender( + vizType: string, + datasourceType: DatasourceType, +) { + const controlPanelConfig = + // TODO: update `chartControlPanelRegistry` type to use ControlPanelConfig + (getChartControlPanelRegistry().get(vizType) as ControlPanelConfig) || {}; + return getMemoizedSectionsToRender(datasourceType, controlPanelConfig); +} diff --git a/superset-frontend/src/explore/controlUtils/index.js b/superset-frontend/src/explore/controlUtils/index.js index 4e1f0ac6ccc2..b426754b7084 100644 --- a/superset-frontend/src/explore/controlUtils/index.js +++ b/superset-frontend/src/explore/controlUtils/index.js @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import memoizeOne from 'memoize-one'; -import { getChartControlPanelRegistry } from '@superset-ui/core'; -import { expandControlConfig } from '@superset-ui/chart-controls'; -import * as SECTIONS from '../controlPanels/sections'; +import { getSectionsToRender } from './getSectionsToRender'; +import { getControlConfig } from './getControlConfig'; export * from './getFormDataFromControls'; +export * from './getControlConfig'; +export * from './getSectionsToRender'; export function validateControl(control, processedState) { const { validators } = control; @@ -38,44 +38,6 @@ export function validateControl(control, processedState) { return { ...control, validationErrors }; } -/** - * Find control item from control panel config. - */ -export function findControlItem(controlPanelSections, controlKey) { - return ( - controlPanelSections - .map(section => section.controlSetRows) - .flat(2) - .find( - control => - controlKey === control || - (control !== null && - typeof control === 'object' && - control.name === controlKey), - ) ?? null - ); -} - -const getMemoizedControlConfig = memoizeOne( - (controlKey, controlPanelConfig) => { - const { - controlOverrides = {}, - controlPanelSections = [], - } = controlPanelConfig; - - const control = expandControlConfig( - findControlItem(controlPanelSections, controlKey), - controlOverrides, - ); - return control?.config || control; - }, -); - -export const getControlConfig = function getControlConfig(controlKey, vizType) { - const controlPanelConfig = getChartControlPanelRegistry().get(vizType) || {}; - return getMemoizedControlConfig(controlKey, controlPanelConfig); -}; - function handleMissingChoice(control) { // If the value is not valid anymore based on choices, clear it const { value } = control; @@ -160,68 +122,9 @@ export function getControlState(controlKey, vizType, state, value) { ); } -const getMemoizedSectionsToRender = memoizeOne( - (datasourceType, controlPanelConfig) => { - const { - sectionOverrides = {}, - controlOverrides, - controlPanelSections = [], - } = controlPanelConfig; - - // default control panel sections - const sections = { ...SECTIONS }; - - // apply section overrides - Object.entries(sectionOverrides).forEach(([section, overrides]) => { - if (typeof overrides === 'object' && overrides.constructor === Object) { - sections[section] = { - ...sections[section], - ...overrides, - }; - } else { - sections[section] = overrides; - } - }); - - const { datasourceAndVizType } = sections; - // list of datasource-specific controls that should be removed - const invalidControls = - datasourceType === 'table' - ? ['granularity', 'druid_time_origin'] - : ['granularity_sqla', 'time_grain_sqla']; - - return [] - .concat(datasourceAndVizType, controlPanelSections) - .filter(section => !!section) - .map(section => { - const { controlSetRows } = section; - return { - ...section, - controlSetRows: - controlSetRows?.map(row => - row - .filter(control => !invalidControls.includes(control)) - .map(item => expandControlConfig(item, controlOverrides)), - ) || [], - }; - }); - }, -); - -/** - * Get the clean and processed control panel sections - */ -export const sectionsToRender = function sectionsToRender( - vizType, - datasourceType, -) { - const controlPanelConfig = getChartControlPanelRegistry().get(vizType) || {}; - return getMemoizedSectionsToRender(datasourceType, controlPanelConfig); -}; - export function getAllControlsState(vizType, datasourceType, state, formData) { const controlsState = {}; - sectionsToRender(vizType, datasourceType).forEach(section => + getSectionsToRender(vizType, datasourceType).forEach(section => section.controlSetRows.forEach(fieldsetRow => fieldsetRow.forEach(field => { if (field && field.config && field.name) { From 731827ce1c6a5b36003025fe157de4c113a3ef3b Mon Sep 17 00:00:00 2001 From: Jesse Yang Date: Tue, 2 Mar 2021 19:58:18 -0800 Subject: [PATCH 2/3] Move sections to tsx --- .../controlPanels/{sections.jsx => sections.tsx} | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename superset-frontend/src/explore/controlPanels/{sections.jsx => sections.tsx} (94%) diff --git a/superset-frontend/src/explore/controlPanels/sections.jsx b/superset-frontend/src/explore/controlPanels/sections.tsx similarity index 94% rename from superset-frontend/src/explore/controlPanels/sections.jsx rename to superset-frontend/src/explore/controlPanels/sections.tsx index d69a69e79efd..c86acbb6e243 100644 --- a/superset-frontend/src/explore/controlPanels/sections.jsx +++ b/superset-frontend/src/explore/controlPanels/sections.tsx @@ -18,16 +18,17 @@ */ import React from 'react'; import { t } from '@superset-ui/core'; +import { ControlPanelSectionConfig } from '@superset-ui/chart-controls'; import { formatSelectOptions } from 'src/modules/utils'; -export const druidTimeSeries = { +export const druidTimeSeries: ControlPanelSectionConfig = { label: t('Time'), expanded: true, description: t('Time related form attributes'), controlSetRows: [['time_range']], }; -export const datasourceAndVizType = { +export const datasourceAndVizType: ControlPanelSectionConfig = { label: t('Chart type'), expanded: true, controlSetRows: [ @@ -74,19 +75,19 @@ export const datasourceAndVizType = { ], }; -export const colorScheme = { +export const colorScheme: ControlPanelSectionConfig = { label: t('Color scheme'), controlSetRows: [['color_scheme', 'label_colors']], }; -export const sqlaTimeSeries = { +export const sqlaTimeSeries: ControlPanelSectionConfig = { label: t('Time'), description: t('Time related form attributes'), expanded: true, controlSetRows: [['granularity_sqla'], ['time_range']], }; -export const annotations = { +export const annotations: ControlPanelSectionConfig = { label: t('Annotations and layers'), tabOverride: 'data', expanded: true, @@ -107,7 +108,7 @@ export const annotations = { ], }; -export const NVD3TimeSeries = [ +export const NVD3TimeSeries: ControlPanelSectionConfig[] = [ { label: t('Query'), expanded: true, From 91d5669898b2c6df9cba3dad913e5a5f21491e1f Mon Sep 17 00:00:00 2001 From: Jesse Yang Date: Wed, 3 Mar 2021 11:32:19 -0800 Subject: [PATCH 3/3] Add back missing controls --- .../src/explore/controlUtils/getSectionsToRender.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts b/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts index 5105b43073f3..244114fc4091 100644 --- a/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts +++ b/superset-frontend/src/explore/controlUtils/getSectionsToRender.ts @@ -71,7 +71,7 @@ const getMemoizedSectionsToRender = memoizeOne( row .filter( control => - typeof control === 'string' && + typeof control !== 'string' || !invalidControls.includes(control), ) .map(item => expandControlConfig(item, controlOverrides)),