This is an automated email from the ASF dual-hosted git repository. maximebeauchemin pushed a commit to branch ts_migrate_claude in repository https://gitbox.apache.org/repos/asf/superset.git
commit 04ba0a00b37b837558e9a10a47339b32f95f5561 Author: Maxime Beauchemin <maximebeauche...@gmail.com> AuthorDate: Sun Jul 13 11:22:22 2025 -0700 src/utils --- ...cedMessageQueue.js => DebouncedMessageQueue.ts} | 26 ++++++++-- .../src/utils/{common.test.jsx => common.test.tsx} | 0 .../src/utils/{common.js => common.ts} | 27 ++++++---- .../{datasourceUtils.js => datasourceUtils.ts} | 16 +++++- .../src/utils/getControlsForVizType.js | 52 ------------------- ...zType.test.js => getControlsForVizType.test.ts} | 0 .../src/utils/getControlsForVizType.ts | 58 ++++++++++++++++++++++ .../{hostNamesConfig.js => hostNamesConfig.ts} | 6 +-- .../src/utils/{reducerUtils.js => reducerUtils.ts} | 38 ++++++++++++-- 9 files changed, 147 insertions(+), 76 deletions(-) diff --git a/superset-frontend/src/utils/DebouncedMessageQueue.js b/superset-frontend/src/utils/DebouncedMessageQueue.ts similarity index 73% rename from superset-frontend/src/utils/DebouncedMessageQueue.js rename to superset-frontend/src/utils/DebouncedMessageQueue.ts index 6fd6c5b778..cf1550ad75 100644 --- a/superset-frontend/src/utils/DebouncedMessageQueue.js +++ b/superset-frontend/src/utils/DebouncedMessageQueue.ts @@ -18,33 +18,49 @@ */ import { debounce } from 'lodash'; +interface DebouncedMessageQueueConfig { + callback?: (events: any[]) => void; + sizeThreshold?: number; + delayThreshold?: number; +} + class DebouncedMessageQueue { + private queue: any[]; + + private sizeThreshold: number; + + private delayThreshold: number; + + private callback: (events: any[]) => void; + + private trigger: () => void; + constructor({ callback = () => {}, sizeThreshold = 1000, delayThreshold = 1000, - }) { + }: DebouncedMessageQueueConfig = {}) { this.queue = []; this.sizeThreshold = sizeThreshold; this.delayThreshold = delayThreshold; - this.trigger = debounce(this.trigger.bind(this), this.delayThreshold); + this.trigger = debounce(this.triggerQueue.bind(this), this.delayThreshold); this.callback = callback; } - append(eventData) { + append(eventData: any): void { this.queue.push(eventData); this.trigger(); } - trigger() { + private triggerQueue(): void { if (this.queue.length > 0) { const events = this.queue.splice(0, this.sizeThreshold); this.callback.call(null, events); // If there are remaining items, call it again. if (this.queue.length > 0) { - this.trigger(); + this.triggerQueue(); } } } diff --git a/superset-frontend/src/utils/common.test.jsx b/superset-frontend/src/utils/common.test.tsx similarity index 100% rename from superset-frontend/src/utils/common.test.jsx rename to superset-frontend/src/utils/common.test.tsx diff --git a/superset-frontend/src/utils/common.js b/superset-frontend/src/utils/common.ts similarity index 85% rename from superset-frontend/src/utils/common.js rename to superset-frontend/src/utils/common.ts index 4ceceb0b41..ad51161565 100644 --- a/superset-frontend/src/utils/common.js +++ b/superset-frontend/src/utils/common.ts @@ -21,6 +21,7 @@ import { getTimeFormatter, TimeFormats, ensureIsArray, + JsonObject, } from '@superset-ui/core'; // ATTENTION: If you change any constants, make sure to also change constants.py @@ -36,7 +37,7 @@ export const SHORT_TIME = 'h:m a'; const DATETIME_FORMATTER = getTimeFormatter(TimeFormats.DATABASE_DATETIME); -export function storeQuery(query) { +export function storeQuery(query: JsonObject): Promise<string> { return SupersetClient.post({ endpoint: '/kv/store/', postPayload: { data: query }, @@ -47,7 +48,7 @@ export function storeQuery(query) { }); } -export function optionLabel(opt) { +export function optionLabel(opt: any): string { if (opt === null) { return NULL_STRING; } @@ -66,23 +67,26 @@ export function optionLabel(opt) { return opt; } -export function optionValue(opt) { +export function optionValue(opt: any): any { if (opt === null) { return NULL_STRING; } return opt; } -export function optionFromValue(opt) { +export function optionFromValue(opt: any): { value: any; label: string } { // From a list of options, handles special values & labels return { value: optionValue(opt), label: optionLabel(opt) }; } -function getColumnName(column) { +function getColumnName(column: any): string { return column.name || column; } -export function prepareCopyToClipboardTabularData(data, columns) { +export function prepareCopyToClipboardTabularData( + data: JsonObject[], + columns: any[], +): string { let result = columns.length ? `${columns.map(getColumnName).join('\t')}\n` : ''; @@ -103,7 +107,10 @@ export function prepareCopyToClipboardTabularData(data, columns) { return result; } -export function applyFormattingToTabularData(data, timeFormattedColumns) { +export function applyFormattingToTabularData( + data: JsonObject[], + timeFormattedColumns: string[], +): JsonObject[] { if ( !data || data.length === 0 || @@ -124,10 +131,10 @@ export function applyFormattingToTabularData(data, timeFormattedColumns) { })); } -export const noOp = () => undefined; +export const noOp = (): undefined => undefined; // Detects the user's OS through the browser -export const detectOS = () => { +export const detectOS = (): string => { const { appVersion } = navigator; // Leveraging this condition because of stackOverflow @@ -140,7 +147,7 @@ export const detectOS = () => { return 'Unknown OS'; }; -export const isSafari = () => { +export const isSafari = (): boolean => { const { userAgent } = navigator; return userAgent && /^((?!chrome|android).)*safari/i.test(userAgent); diff --git a/superset-frontend/src/utils/datasourceUtils.js b/superset-frontend/src/utils/datasourceUtils.ts similarity index 76% rename from superset-frontend/src/utils/datasourceUtils.js rename to superset-frontend/src/utils/datasourceUtils.ts index 144a3ff88b..20ab4329bb 100644 --- a/superset-frontend/src/utils/datasourceUtils.js +++ b/superset-frontend/src/utils/datasourceUtils.ts @@ -16,7 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -export const getDatasourceAsSaveableDataset = source => ({ +import { Dataset } from '@superset-ui/chart-controls'; + +interface SaveableDataset { + columns: any[]; + name: string; + dbId?: number; + sql: string; + catalog?: string | null; + schema?: string; + templateParams?: string; +} + +export const getDatasourceAsSaveableDataset = ( + source: Partial<Dataset>, +): SaveableDataset => ({ columns: source.columns, name: source?.datasource_name || source?.name || 'Untitled', dbId: source?.database?.id || source?.dbId, diff --git a/superset-frontend/src/utils/getControlsForVizType.js b/superset-frontend/src/utils/getControlsForVizType.js deleted file mode 100644 index 8771d91dc7..0000000000 --- a/superset-frontend/src/utils/getControlsForVizType.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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 { isControlPanelSectionConfig } from '@superset-ui/chart-controls'; -import { getChartControlPanelRegistry } from '@superset-ui/core'; -import { controls } from '../explore/controls'; - -const memoizedControls = memoizeOne((vizType, controlPanel) => { - const controlsMap = {}; - (controlPanel?.controlPanelSections || []) - .filter(isControlPanelSectionConfig) - .forEach(section => { - section.controlSetRows.forEach(row => { - row.forEach(control => { - if (!control) return; - if (typeof control === 'string') { - // For now, we have to look in controls.jsx to get the config for some controls. - // Once everything is migrated out, delete this if statement. - controlsMap[control] = controls[control]; - } else if (control.name && control.config) { - // condition needed because there are elements, e.g. <hr /> in some control configs (I'm looking at you, FilterBox!) - controlsMap[control.name] = control.config; - } - }); - }); - }); - return controlsMap; -}); - -const getControlsForVizType = vizType => { - const controlPanel = getChartControlPanelRegistry().get(vizType); - return memoizedControls(vizType, controlPanel); -}; - -export default getControlsForVizType; diff --git a/superset-frontend/src/utils/getControlsForVizType.test.js b/superset-frontend/src/utils/getControlsForVizType.test.ts similarity index 100% rename from superset-frontend/src/utils/getControlsForVizType.test.js rename to superset-frontend/src/utils/getControlsForVizType.test.ts diff --git a/superset-frontend/src/utils/getControlsForVizType.ts b/superset-frontend/src/utils/getControlsForVizType.ts new file mode 100644 index 0000000000..c77ca77d2e --- /dev/null +++ b/superset-frontend/src/utils/getControlsForVizType.ts @@ -0,0 +1,58 @@ +/** + * 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 { + isControlPanelSectionConfig, + ControlStateMapping, + ControlPanelConfig, +} from '@superset-ui/chart-controls'; +import { getChartControlPanelRegistry } from '@superset-ui/core'; +import { controls } from '../explore/controls'; + +const memoizedControls = memoizeOne( + (vizType: string, controlPanel: ControlPanelConfig): ControlStateMapping => { + const controlsMap: ControlStateMapping = {}; + (controlPanel?.controlPanelSections || []) + .filter(isControlPanelSectionConfig) + .forEach(section => { + section.controlSetRows.forEach(row => { + row.forEach(control => { + if (!control) return; + if (typeof control === 'string') { + // For now, we have to look in controls.jsx to get the config for some controls. + // Once everything is migrated out, delete this if statement. + controlsMap[control] = controls[control]; + } else if (control.name && control.config) { + // condition needed because there are elements, e.g. <hr /> in some control configs (I'm looking at you, FilterBox!) + controlsMap[control.name] = control.config; + } + }); + }); + }); + return controlsMap; + }, +); + +const getControlsForVizType = (vizType: string): ControlStateMapping => { + const controlPanel = getChartControlPanelRegistry().get(vizType); + return memoizedControls(vizType, controlPanel); +}; + +export default getControlsForVizType; diff --git a/superset-frontend/src/utils/hostNamesConfig.js b/superset-frontend/src/utils/hostNamesConfig.ts similarity index 91% rename from superset-frontend/src/utils/hostNamesConfig.js rename to superset-frontend/src/utils/hostNamesConfig.ts index 2ba9b7555c..f2359f2e84 100644 --- a/superset-frontend/src/utils/hostNamesConfig.js +++ b/superset-frontend/src/utils/hostNamesConfig.ts @@ -19,7 +19,7 @@ import { initFeatureFlags } from '@superset-ui/core'; import getBootstrapData from './getBootstrapData'; -function getDomainsConfig() { +function getDomainsConfig(): string[] { const appContainer = document.getElementById('app'); if (!appContainer) { return []; @@ -49,6 +49,6 @@ function getDomainsConfig() { return Array.from(availableDomains); } -export const availableDomains = getDomainsConfig(); +export const availableDomains: string[] = getDomainsConfig(); -export const allowCrossDomain = availableDomains.length > 1; +export const allowCrossDomain: boolean = availableDomains.length > 1; diff --git a/superset-frontend/src/utils/reducerUtils.js b/superset-frontend/src/utils/reducerUtils.ts similarity index 72% rename from superset-frontend/src/utils/reducerUtils.js rename to superset-frontend/src/utils/reducerUtils.ts index 199c8aa292..300f878d94 100644 --- a/superset-frontend/src/utils/reducerUtils.js +++ b/superset-frontend/src/utils/reducerUtils.ts @@ -18,7 +18,16 @@ */ import { nanoid } from 'nanoid'; -export function addToObject(state, arrKey, obj) { +interface StateWithId { + id?: string; + [key: string]: any; +} + +export function addToObject<T extends Record<string, any>>( + state: T, + arrKey: keyof T, + obj: StateWithId, +): T { const newObject = { ...state[arrKey] }; const copiedObject = { ...obj }; @@ -29,13 +38,23 @@ export function addToObject(state, arrKey, obj) { return { ...state, [arrKey]: newObject }; } -export function alterInObject(state, arrKey, obj, alterations) { +export function alterInObject<T extends Record<string, any>>( + state: T, + arrKey: keyof T, + obj: StateWithId, + alterations: Record<string, any>, +): T { const newObject = { ...state[arrKey] }; newObject[obj.id] = { ...newObject[obj.id], ...alterations }; return { ...state, [arrKey]: newObject }; } -export function alterInArr(state, arrKey, obj, alterations) { +export function alterInArr<T extends Record<string, any>>( + state: T, + arrKey: keyof T, + obj: StateWithId, + alterations: Record<string, any>, +): T { // Finds an item in an array in the state and replaces it with a // new object with an altered property const idKey = 'id'; @@ -50,7 +69,12 @@ export function alterInArr(state, arrKey, obj, alterations) { return { ...state, [arrKey]: newArr }; } -export function removeFromArr(state, arrKey, obj, idKey = 'id') { +export function removeFromArr<T extends Record<string, any>>( + state: T, + arrKey: keyof T, + obj: StateWithId, + idKey: string = 'id', +): T { const newArr = []; state[arrKey].forEach(arrItem => { if (!(obj[idKey] === arrItem[idKey])) { @@ -60,7 +84,11 @@ export function removeFromArr(state, arrKey, obj, idKey = 'id') { return { ...state, [arrKey]: newArr }; } -export function addToArr(state, arrKey, obj) { +export function addToArr<T extends Record<string, any>>( + state: T, + arrKey: keyof T, + obj: StateWithId, +): T { const newObj = { ...obj }; if (!newObj.id) { newObj.id = nanoid();