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) {