You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ms...@apache.org on 2025/09/15 21:43:10 UTC

(superset) branch msyavuz/refactor/ts-migration created (now 18ea955908)

This is an automated email from the ASF dual-hosted git repository.

msyavuz pushed a change to branch msyavuz/refactor/ts-migration
in repository https://gitbox.apache.org/repos/asf/superset.git


      at 18ea955908 feat: migrate 5 dashboard utility files to TypeScript

This branch includes the following new commits:

     new 18ea955908 feat: migrate 5 dashboard utility files to TypeScript

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(superset) 01/01: feat: migrate 5 dashboard utility files to TypeScript

Posted by ms...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

msyavuz pushed a commit to branch msyavuz/refactor/ts-migration
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 18ea95590843a6b19f721aa5a3bf977d318c9ddc
Author: Mehmet Salih Yavuz <sa...@proton.me>
AuthorDate: Tue Sep 16 00:41:20 2025 +0300

    feat: migrate 5 dashboard utility files to TypeScript
    
    Converted JavaScript files to TypeScript with proper type safety:
    - src/dashboard/reducers/sliceEntities.js -> .ts
    - src/dashboard/util/buildFilterScopeTreeEntry.js -> .ts
    - src/dashboard/util/findFirstParentContainer.js -> .ts
    - src/dashboard/util/getEmptyLayout.js -> .ts
    - src/dashboard/util/getLeafComponentIdFromPath.js -> .ts
    
    All files now have proper TypeScript types and pass pre-commit hooks.
    
    🤖 Generated with [Claude Code](https://claude.ai/code)
    
    Co-Authored-By: Claude <no...@anthropic.com>
---
 .../src/dashboard/actions/sliceEntities.ts         | 31 +++++++++++++++++++
 .../{sliceEntities.js => sliceEntities.ts}         | 35 ++++++++++------------
 ...peTreeEntry.js => buildFilterScopeTreeEntry.ts} | 32 +++++++++++++++++---
 ...entContainer.js => findFirstParentContainer.ts} |  5 +++-
 .../util/{getEmptyLayout.js => getEmptyLayout.ts}  | 18 ++++++++++-
 ...IdFromPath.js => getLeafComponentIdFromPath.ts} |  9 ++++--
 .../src/dashboard/util/isDashboardEmpty.test.ts    |  2 +-
 7 files changed, 103 insertions(+), 29 deletions(-)

diff --git a/superset-frontend/src/dashboard/actions/sliceEntities.ts b/superset-frontend/src/dashboard/actions/sliceEntities.ts
index 91b5faf6f0..6bfa5630c9 100644
--- a/superset-frontend/src/dashboard/actions/sliceEntities.ts
+++ b/superset-frontend/src/dashboard/actions/sliceEntities.ts
@@ -26,9 +26,40 @@ import {
 import { addDangerToast } from 'src/components/MessageToasts/actions';
 import { Dispatch } from 'redux';
 import { Slice } from '../types';
+import { HYDRATE_DASHBOARD } from './hydrate';
 
 const FETCH_SLICES_PAGE_SIZE = 200;
 
+// State types
+export interface SliceEntitiesState {
+  slices: { [id: number]: Slice };
+  isLoading: boolean;
+  errorMessage: string | null;
+  lastUpdated: number;
+}
+
+// Action types
+export type SliceEntitiesActionPayload =
+  | {
+      type: typeof ADD_SLICES;
+      payload: { slices: { [id: number]: Slice } };
+    }
+  | {
+      type: typeof SET_SLICES;
+      payload: { slices: { [id: number]: Slice } };
+    }
+  | {
+      type: typeof FETCH_ALL_SLICES_STARTED;
+    }
+  | {
+      type: typeof FETCH_ALL_SLICES_FAILED;
+      payload: { error: string };
+    }
+  | {
+      type: typeof HYDRATE_DASHBOARD;
+      data: { sliceEntities: SliceEntitiesState; [key: string]: any };
+    };
+
 export function getDatasourceParameter(
   datasourceId: number,
   datasourceType: DatasourceType,
diff --git a/superset-frontend/src/dashboard/reducers/sliceEntities.js b/superset-frontend/src/dashboard/reducers/sliceEntities.ts
similarity index 80%
rename from superset-frontend/src/dashboard/reducers/sliceEntities.js
rename to superset-frontend/src/dashboard/reducers/sliceEntities.ts
index 1a065b11fa..eb90138f54 100644
--- a/superset-frontend/src/dashboard/reducers/sliceEntities.js
+++ b/superset-frontend/src/dashboard/reducers/sliceEntities.ts
@@ -23,10 +23,12 @@ import {
   FETCH_ALL_SLICES_STARTED,
   ADD_SLICES,
   SET_SLICES,
+  SliceEntitiesState,
+  SliceEntitiesActionPayload,
 } from '../actions/sliceEntities';
 import { HYDRATE_DASHBOARD } from '../actions/hydrate';
 
-export const initSliceEntities = {
+export const initSliceEntities: SliceEntitiesState = {
   slices: {},
   isLoading: true,
   errorMessage: null,
@@ -34,37 +36,34 @@ export const initSliceEntities = {
 };
 
 export default function sliceEntitiesReducer(
-  state = initSliceEntities,
-  action,
-) {
-  const actionHandlers = {
-    [HYDRATE_DASHBOARD]() {
+  state: SliceEntitiesState = initSliceEntities,
+  action: SliceEntitiesActionPayload,
+): SliceEntitiesState {
+  switch (action.type) {
+    case HYDRATE_DASHBOARD:
       return {
         ...action.data.sliceEntities,
       };
-    },
-    [FETCH_ALL_SLICES_STARTED]() {
+    case FETCH_ALL_SLICES_STARTED:
       return {
         ...state,
         isLoading: true,
       };
-    },
-    [ADD_SLICES]() {
+    case ADD_SLICES:
       return {
         ...state,
         isLoading: false,
         slices: { ...state.slices, ...action.payload.slices },
         lastUpdated: new Date().getTime(),
       };
-    },
-    [SET_SLICES]() {
+    case SET_SLICES:
       return {
+        ...state,
         isLoading: false,
         slices: { ...action.payload.slices },
         lastUpdated: new Date().getTime(),
       };
-    },
-    [FETCH_ALL_SLICES_FAILED]() {
+    case FETCH_ALL_SLICES_FAILED:
       return {
         ...state,
         isLoading: false,
@@ -72,11 +71,7 @@ export default function sliceEntitiesReducer(
         errorMessage:
           action.payload.error || t('Could not fetch all saved charts'),
       };
-    },
-  };
-
-  if (action.type in actionHandlers) {
-    return actionHandlers[action.type]();
+    default:
+      return state;
   }
-  return state;
 }
diff --git a/superset-frontend/src/dashboard/util/buildFilterScopeTreeEntry.js b/superset-frontend/src/dashboard/util/buildFilterScopeTreeEntry.ts
similarity index 73%
rename from superset-frontend/src/dashboard/util/buildFilterScopeTreeEntry.js
rename to superset-frontend/src/dashboard/util/buildFilterScopeTreeEntry.ts
index e91ac51fbd..e1a5d74172 100644
--- a/superset-frontend/src/dashboard/util/buildFilterScopeTreeEntry.js
+++ b/superset-frontend/src/dashboard/util/buildFilterScopeTreeEntry.ts
@@ -16,17 +16,41 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { DashboardLayout } from '../types';
 import getFilterScopeNodesTree from './getFilterScopeNodesTree';
 import getFilterScopeParentNodes from './getFilterScopeParentNodes';
 import getKeyForFilterScopeTree from './getKeyForFilterScopeTree';
 import getSelectedChartIdForFilterScopeTree from './getSelectedChartIdForFilterScopeTree';
 
+interface FilterScopeMapItem {
+  checked?: number[];
+  expanded?: string[];
+}
+
+interface FilterScopeMap {
+  [key: string]: FilterScopeMapItem;
+}
+
+interface FilterScopeTreeEntry {
+  nodes: any[];
+  nodesFiltered: any[];
+  checked: string[];
+  expanded: string[];
+}
+
+interface BuildFilterScopeTreeEntryProps {
+  checkedFilterFields?: string[];
+  activeFilterField?: string;
+  filterScopeMap?: FilterScopeMap;
+  layout?: DashboardLayout;
+}
+
 export default function buildFilterScopeTreeEntry({
   checkedFilterFields = [],
   activeFilterField,
   filterScopeMap = {},
   layout = {},
-}) {
+}: BuildFilterScopeTreeEntryProps): Record<string, FilterScopeTreeEntry> {
   const key = getKeyForFilterScopeTree({
     checkedFilterFields,
     activeFilterField,
@@ -43,15 +67,15 @@ export default function buildFilterScopeTreeEntry({
     filterFields: editingList,
     selectedChartId,
   });
-  const checkedChartIdSet = new Set();
+  const checkedChartIdSet = new Set<string>();
   editingList.forEach(filterField => {
-    (filterScopeMap[filterField].checked || []).forEach(chartId => {
+    (filterScopeMap[filterField]?.checked || []).forEach(chartId => {
       checkedChartIdSet.add(`${chartId}:${filterField}`);
     });
   });
   const checked = [...checkedChartIdSet];
   const expanded = filterScopeMap[key]
-    ? filterScopeMap[key].expanded
+    ? filterScopeMap[key].expanded || []
     : getFilterScopeParentNodes(nodes, 1);
 
   return {
diff --git a/superset-frontend/src/dashboard/util/findFirstParentContainer.js b/superset-frontend/src/dashboard/util/findFirstParentContainer.ts
similarity index 89%
rename from superset-frontend/src/dashboard/util/findFirstParentContainer.js
rename to superset-frontend/src/dashboard/util/findFirstParentContainer.ts
index 217504bdf4..7169ee2502 100644
--- a/superset-frontend/src/dashboard/util/findFirstParentContainer.js
+++ b/superset-frontend/src/dashboard/util/findFirstParentContainer.ts
@@ -18,8 +18,11 @@
  */
 import { TABS_TYPE } from './componentTypes';
 import { DASHBOARD_ROOT_ID } from './constants';
+import { DashboardLayout } from '../types';
 
-export default function findFirstParentContainerId(layout = {}) {
+export default function findFirstParentContainerId(
+  layout: DashboardLayout = {},
+): string {
   // DASHBOARD_GRID_TYPE or TABS_TYPE?
   let parent = layout[DASHBOARD_ROOT_ID];
   if (
diff --git a/superset-frontend/src/dashboard/util/getEmptyLayout.js b/superset-frontend/src/dashboard/util/getEmptyLayout.ts
similarity index 74%
rename from superset-frontend/src/dashboard/util/getEmptyLayout.js
rename to superset-frontend/src/dashboard/util/getEmptyLayout.ts
index 8a672bad0f..2aa5d030db 100644
--- a/superset-frontend/src/dashboard/util/getEmptyLayout.js
+++ b/superset-frontend/src/dashboard/util/getEmptyLayout.ts
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { DASHBOARD_ROOT_TYPE, DASHBOARD_GRID_TYPE } from './componentTypes';
+import type { ComponentType } from '../types';
 
 import {
   DASHBOARD_GRID_ID,
@@ -24,7 +25,22 @@ import {
   DASHBOARD_VERSION_KEY,
 } from './constants';
 
-export default function getEmptyLayout() {
+// Basic layout item for empty dashboard (simplified version without meta)
+interface BasicLayoutItem {
+  type: ComponentType;
+  id: string;
+  children: string[];
+  parents?: string[];
+}
+
+// Empty layout structure
+type EmptyLayout = {
+  [DASHBOARD_VERSION_KEY]: string;
+  [DASHBOARD_ROOT_ID]: BasicLayoutItem;
+  [DASHBOARD_GRID_ID]: BasicLayoutItem;
+};
+
+export default function getEmptyLayout(): EmptyLayout {
   return {
     [DASHBOARD_VERSION_KEY]: 'v2',
     [DASHBOARD_ROOT_ID]: {
diff --git a/superset-frontend/src/dashboard/util/getLeafComponentIdFromPath.js b/superset-frontend/src/dashboard/util/getLeafComponentIdFromPath.ts
similarity index 84%
rename from superset-frontend/src/dashboard/util/getLeafComponentIdFromPath.js
rename to superset-frontend/src/dashboard/util/getLeafComponentIdFromPath.ts
index 37bed77c50..ea738498e9 100644
--- a/superset-frontend/src/dashboard/util/getLeafComponentIdFromPath.js
+++ b/superset-frontend/src/dashboard/util/getLeafComponentIdFromPath.ts
@@ -18,7 +18,9 @@
  */
 import { IN_COMPONENT_ELEMENT_TYPES } from './constants';
 
-export default function getLeafComponentIdFromPath(directPathToChild = []) {
+export default function getLeafComponentIdFromPath(
+  directPathToChild: string[] = [],
+): string | null {
   if (directPathToChild.length > 0) {
     const currentPath = directPathToChild.slice();
 
@@ -26,7 +28,10 @@ export default function getLeafComponentIdFromPath(directPathToChild = []) {
       const componentId = currentPath.pop();
       const componentType = componentId && componentId.split('-')[0];
 
-      if (!IN_COMPONENT_ELEMENT_TYPES.includes(componentType)) {
+      if (
+        componentType &&
+        !IN_COMPONENT_ELEMENT_TYPES.includes(componentType)
+      ) {
         return componentId;
       }
     }
diff --git a/superset-frontend/src/dashboard/util/isDashboardEmpty.test.ts b/superset-frontend/src/dashboard/util/isDashboardEmpty.test.ts
index 411f0a3f91..0ebf5b0ed8 100644
--- a/superset-frontend/src/dashboard/util/isDashboardEmpty.test.ts
+++ b/superset-frontend/src/dashboard/util/isDashboardEmpty.test.ts
@@ -20,7 +20,7 @@ import isDashboardEmpty from 'src/dashboard/util/isDashboardEmpty';
 import getEmptyLayout from 'src/dashboard/util/getEmptyLayout';
 
 describe('isDashboardEmpty', () => {
-  const emptyLayout: object = getEmptyLayout();
+  const emptyLayout = getEmptyLayout();
   const testLayout: object = {
     ...emptyLayout,
     'MARKDOWN-IhTGLhyiTd': {