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/10/22 08:03:10 UTC

(superset) branch msyavuz/fix/calculated-column-order created (now 9ab0f02e4e)

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

msyavuz pushed a change to branch msyavuz/fix/calculated-column-order
in repository https://gitbox.apache.org/repos/asf/superset.git


      at 9ab0f02e4e fix(DatasourceEditor): preserve calculated column order when editing sql

This branch includes the following new commits:

     new 9ab0f02e4e fix(DatasourceEditor): preserve calculated column order when editing sql

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: fix(DatasourceEditor): preserve calculated column order when editing sql

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

msyavuz pushed a commit to branch msyavuz/fix/calculated-column-order
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 9ab0f02e4eaf8dca66622606024201c7b0f4def4
Author: Mehmet Salih Yavuz <sa...@proton.me>
AuthorDate: Wed Oct 22 10:41:57 2025 +0300

    fix(DatasourceEditor): preserve calculated column order when editing sql
---
 .../components/CollectionTable/index.tsx           | 22 ++++++---
 .../DatasourceEditor/DatasourceEditor.jsx          | 54 +++++++++++++++++++++-
 2 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/superset-frontend/src/components/Datasource/components/CollectionTable/index.tsx b/superset-frontend/src/components/Datasource/components/CollectionTable/index.tsx
index a9bbda126a..9caa79ee59 100644
--- a/superset-frontend/src/components/Datasource/components/CollectionTable/index.tsx
+++ b/superset-frontend/src/components/Datasource/components/CollectionTable/index.tsx
@@ -53,9 +53,6 @@ const StyledButtonWrapper = styled.span`
 
 type CollectionItem = { id: string | number; [key: string]: any };
 
-function createCollectionArray(collection: Record<PropertyKey, any>) {
-  return Object.keys(collection).map(k => collection[k] as CollectionItem);
-}
 
 function createKeyedCollection(arr: Array<object>) {
   const collectionArray = arr.map(
@@ -109,6 +106,7 @@ export default class CRUDCollection extends PureComponent<
       const { collection, collectionArray } = createKeyedCollection(
         this.props.collection,
       );
+      
       this.setState(prevState => ({
         collection,
         collectionArray,
@@ -117,7 +115,7 @@ export default class CRUDCollection extends PureComponent<
     }
   }
 
-  onCellChange(id: number, col: string, val: boolean) {
+  onCellChange(id: number, col: string, val: any) {
     this.setState(prevState => {
       const updatedCollection = {
         ...prevState.collection,
@@ -197,11 +195,21 @@ export default class CRUDCollection extends PureComponent<
   }
 
   changeCollection(collection: any) {
-    const newCollectionArray = createCollectionArray(collection);
-    this.setState({ collection, collectionArray: newCollectionArray });
+    // Preserve existing order instead of recreating from Object.keys()
+    // Map existing items to their updated versions from the collection
+    const newCollectionArray = this.state.collectionArray
+      .map(existingItem => collection[existingItem.id] || existingItem)
+      .filter(item => collection[item.id]); // Remove deleted items
+    
+    // Add any new items that weren't in the existing array
+    const existingIds = new Set(this.state.collectionArray.map(item => item.id));
+    const newItems = Object.values(collection).filter((item: any) => !existingIds.has(item.id));
+    const finalCollectionArray = [...newCollectionArray, ...newItems];
+    
+    this.setState({ collection, collectionArray: finalCollectionArray });
 
     if (this.props.onChange) {
-      this.props.onChange(newCollectionArray);
+      this.props.onChange(finalCollectionArray);
     }
   }
 
diff --git a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.jsx b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.jsx
index 04f4f18ef3..e7a5459d67 100644
--- a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.jsx
+++ b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.jsx
@@ -278,7 +278,7 @@ function ColumnCollectionTable({
                 label={t('SQL expression')}
                 control={
                   <TextAreaControl
-                    language="markdown"
+                    language="sql"
                     offerEditInModal={false}
                     resize="vertical"
                   />
@@ -691,11 +691,13 @@ class DatasourceEditor extends PureComponent {
     const { datasourceType, datasource } = this.state;
     const sql =
       datasourceType === DATASOURCE_TYPES.physical.key ? '' : datasource.sql;
+    
     const newDatasource = {
       ...this.state.datasource,
       sql,
       columns: [...this.state.databaseColumns, ...this.state.calculatedColumns],
     };
+    
     this.props.onChange(newDatasource, this.state.errors);
   }
 
@@ -1704,6 +1706,7 @@ class DatasourceEditor extends PureComponent {
               minLines={5}
               textAreaStyles={{ minWidth: '200px', maxWidth: '450px' }}
               resize="both"
+              debounceDelay={300}
             />
           ),
           description: (v, onChange, label) => (
@@ -1888,6 +1891,55 @@ class DatasourceEditor extends PureComponent {
     );
   }
 
+  componentDidUpdate(prevProps) {
+    // Preserve calculated columns order when props change to prevent jumping
+    if (this.props.datasource !== prevProps.datasource) {
+      const newCalculatedColumns = this.props.datasource.columns.filter(col => !!col.expression);
+      const currentCalculatedColumns = this.state.calculatedColumns;
+      
+      // Check if this is just an update to existing calculated columns (not addition/removal)
+      if (newCalculatedColumns.length === currentCalculatedColumns.length) {
+        // Create a map of current calculated columns by their identifier
+        const currentMap = new Map();
+        currentCalculatedColumns.forEach((col, index) => {
+          const id = col.id || col.column_name;
+          currentMap.set(id, { ...col, originalIndex: index });
+        });
+        
+        // Try to preserve the order by matching with existing calculated columns
+        const orderedCalculatedColumns = [];
+        const usedIds = new Set();
+        
+        // First, add existing columns in their current order
+        currentCalculatedColumns.forEach(currentCol => {
+          const id = currentCol.id || currentCol.column_name;
+          const updatedCol = newCalculatedColumns.find(newCol => 
+            (newCol.id || newCol.column_name) === id
+          );
+          if (updatedCol) {
+            orderedCalculatedColumns.push(updatedCol);
+            usedIds.add(id);
+          }
+        });
+        
+        // Then add any new columns that weren't in the current list
+        newCalculatedColumns.forEach(newCol => {
+          const id = newCol.id || newCol.column_name;
+          if (!usedIds.has(id)) {
+            orderedCalculatedColumns.push(newCol);
+          }
+        });
+        
+        
+        // Update state with preserved order
+        this.setState({
+          calculatedColumns: orderedCalculatedColumns,
+          databaseColumns: this.props.datasource.columns.filter(col => !col.expression),
+        });
+      }
+    }
+  }
+
   componentDidMount() {
     Mousetrap.bind('ctrl+shift+f', e => {
       e.preventDefault();