refactor: convert controlUtils to TypeScript (1 of 2) (#13401)
diff --git a/superset-frontend/src/explore/components/Control.tsx b/superset-frontend/src/explore/components/Control.tsx
index f1b6925..d3ae6bb 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 a53395a..8fafaa4 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
@@ -40,12 +40,13 @@
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 @@
export type ControlPanelsContainerProps = {
actions: ExploreActions;
datasource_type: DatasourceType;
- exploreState: Record<string, any>;
+ exploreState: ExplorePageState['explore'];
+ chart: ChartState;
controls: Record<string, ControlState>;
form_data: QueryFormData;
isDatasourceMetaLoading: boolean;
@@ -100,7 +102,7 @@
}
`;
-class ControlPanelsContainer extends React.Component<ControlPanelsContainerProps> {
+export class ControlPanelsContainer extends React.Component<ControlPanelsContainerProps> {
// trigger updates to the component when async plugins load
static contextType = PluginContext;
@@ -111,7 +113,7 @@
}
sectionsToRender(): ExpandedControlPanelSectionConfig[] {
- return sectionsToRender(
+ return getSectionsToRender(
this.props.form_data.viz_type,
this.props.datasource_type,
);
@@ -314,8 +316,6 @@
}
}
-export { ControlPanelsContainer };
-
export default connect(
function mapStateToProps(state: ExplorePageState) {
const { explore, charts } = state;
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 d69a69e..c86acbb 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 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 NVD3TimeSeries = [
+export const NVD3TimeSeries: ControlPanelSectionConfig[] = [
{
label: t('Query'),
expanded: true,
diff --git a/superset-frontend/src/explore/controlUtils/getControlConfig.ts b/superset-frontend/src/explore/controlUtils/getControlConfig.ts
new file mode 100644
index 0000000..6be87e4
--- /dev/null
+++ b/superset-frontend/src/explore/controlUtils/getControlConfig.ts
@@ -0,0 +1,68 @@
+/**
+ * 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 { getChartControlPanelRegistry } from '@superset-ui/core';
+import {
+ ControlPanelSectionConfig,
+ expandControlConfig,
+} from '@superset-ui/chart-controls';
+
+const getMemoizedControlConfig = memoizeOne(
+ (controlKey, controlPanelConfig) => {
+ 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 0000000..244114f
--- /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 4e1f0ac..b426754 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 @@
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 @@
);
}
-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) {