(superset) branch template_less updated: refactor(Table): Use Table instead of html in CollectionTable (#33159)

Fri, 25 Apr 2025 09:52:37 -0700

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

maximebeauchemin pushed a commit to branch template_less
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/template_less by this push:
     new a7b7e6319c refactor(Table): Use Table instead of html <table> in 
CollectionTable (#33159)
a7b7e6319c is described below

commit a7b7e6319cd5e31b3dbe4043fc3e5707dd48f83f
Author: Mehmet Salih Yavuz <[email protected]>
AuthorDate: Fri Apr 25 19:52:04 2025 +0300

    refactor(Table): Use Table instead of html <table> in CollectionTable 
(#33159)
---
 .../cypress/e2e/explore/control.test.ts            |   8 +-
 .../cypress-base/cypress/support/directories.ts    |   2 +-
 .../components/Datasource/CollectionTable.test.tsx |   4 +-
 .../src/components/Datasource/CollectionTable.tsx  | 544 ++++++++++-----------
 .../src/components/Datasource/DatasourceEditor.jsx |  10 +-
 .../Datasource/DatasourceEditor.test.jsx           |  10 +-
 .../src/components/Datasource/types.ts             |   1 +
 superset-frontend/src/components/Table/index.tsx   |   2 +
 8 files changed, 277 insertions(+), 304 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/e2e/explore/control.test.ts 
b/superset-frontend/cypress-base/cypress/e2e/explore/control.test.ts
index 66e2c51308..2e1e7f6c82 100644
--- a/superset-frontend/cypress-base/cypress/e2e/explore/control.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/explore/control.test.ts
@@ -40,15 +40,11 @@ describe('Datasource control', () => {
     // create new metric
     cy.get('[data-test="crud-add-table-item"]', { timeout: 10000 }).click();
     cy.wait(1000);
-    cy.get(
-      '[data-test="table-content-rows"] 
[data-test="textarea-editable-title-input"]',
-    )
+    cy.get('.antd5-table-body [data-test="editable-title-input"]')
       .first()
       .click();
 
-    cy.get(
-      '[data-test="table-content-rows"] 
[data-test="textarea-editable-title-input"]',
-    )
+    cy.get('.antd5-table-body [data-test="editable-title-input"]')
       .first()
       .focus();
     cy.focused().clear({ force: true });
diff --git a/superset-frontend/cypress-base/cypress/support/directories.ts 
b/superset-frontend/cypress-base/cypress/support/directories.ts
index 1f1d5a543f..05f6aecefb 100644
--- a/superset-frontend/cypress-base/cypress/support/directories.ts
+++ b/superset-frontend/cypress-base/cypress/support/directories.ts
@@ -583,7 +583,7 @@ export const exploreView = {
     saveButton: dataTestLocator('datasource-modal-save'),
     metricsTab: {
       addItem: dataTestLocator('crud-add-table-item'),
-      rowsContainer: dataTestLocator('table-content-rows'),
+      rowsContainer: '.antd5-table-body',
     },
     confirmModal: {
       okButton: '.antd5-modal-confirm-btns .antd5-btn-primary',
diff --git 
a/superset-frontend/src/components/Datasource/CollectionTable.test.tsx 
b/superset-frontend/src/components/Datasource/CollectionTable.test.tsx
index 1ebe7406d9..69c7fc09a0 100644
--- a/superset-frontend/src/components/Datasource/CollectionTable.test.tsx
+++ b/superset-frontend/src/components/Datasource/CollectionTable.test.tsx
@@ -34,6 +34,6 @@ test('renders a table', () => {
   expect(
     getByRole('table')
       .getElementsByTagName('tbody')[0]
-      .getElementsByClassName('row'),
-  ).toHaveLength(length);
+      .getElementsByTagName('tr'),
+  ).toHaveLength(length + 1); // Ant design measure row;
 });
diff --git a/superset-frontend/src/components/Datasource/CollectionTable.tsx 
b/superset-frontend/src/components/Datasource/CollectionTable.tsx
index 075be69a90..63bfb2ebf9 100644
--- a/superset-frontend/src/components/Datasource/CollectionTable.tsx
+++ b/superset-frontend/src/components/Datasource/CollectionTable.tsx
@@ -16,87 +16,30 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { PureComponent } from 'react';
+import { PureComponent, ReactNode } from 'react';
 
 import { nanoid } from 'nanoid';
 
 import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
-import { t, styled } from '@superset-ui/core';
+import { t, styled, css } from '@superset-ui/core';
 
 import { Icons } from 'src/components/Icons';
+import { FilterValue } from 'react-table';
 import { Button } from '../Button';
 import Fieldset from './Fieldset';
 import { recurseReactClone } from './utils';
 import {
-  SortOrder,
   type CRUDCollectionProps,
   type CRUDCollectionState,
   type Sort,
 } from './types';
-
-function createCollectionArray(collection: Record<PropertyKey, any>) {
-  return Object.keys(collection).map(k => collection[k]);
-}
-
-function createKeyedCollection(arr: Array<object>) {
-  const collectionArray = arr.map((o: any) => ({
-    ...o,
-    id: o.id || nanoid(),
-  }));
-
-  const collection: Record<PropertyKey, any> = {};
-  collectionArray.forEach((o: any) => {
-    collection[o.id] = o;
-  });
-
-  return {
-    collection,
-    collectionArray,
-  };
-}
-
-const CrudTableWrapper = styled.div<{ stickyHeader?: boolean }>`
-  ${({ stickyHeader }) =>
-    stickyHeader &&
-    `
-      height: 350px;
-      overflow-y: auto;
-      overflow-x: auto;
-
-      .table {
-        min-width: 800px;
-      }
-      thead th {
-        background: #fff;
-        position: sticky;
-        top: 0;
-        z-index: 9;
-        min
-      }
-    `}
-  ${({ theme }) => `
-    th span {
-      vertical-align: ${theme.sizeUnit * -2}px;
-    }
-    .text-right {
-      text-align: right;
-    }
-    .empty-collection {
-      padding: ${theme.sizeUnit * 2 + 2}px;
-    }
-    .tiny-cell {
-      width: ${theme.sizeUnit + 1}px;
-    }
-    i.fa-caret-down,
-    i.fa-caret-up {
-      width: ${theme.sizeUnit + 1}px;
-    }
-    td.expanded {
-      border-top: 0;
-      padding: 0;
-    }
-  `}
-`;
+import Table, {
+  type ColumnsType,
+  type SortOrder,
+  type SorterResult,
+  type TablePaginationConfig,
+  TableSize,
+} from '../Table';
 
 const CrudButtonWrapper = styled.div`
   text-align: right;
@@ -113,6 +56,32 @@ 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(
+    (o: any) =>
+      ({
+        ...o,
+        id: o.id || nanoid(),
+      }) as CollectionItem,
+  );
+
+  const collection: Record<PropertyKey, any> = {};
+  collectionArray.forEach((o: CollectionItem) => {
+    collection[o.id] = o;
+  });
+
+  return {
+    collection,
+    collectionArray,
+  };
+}
+
 export default class CRUDCollection extends PureComponent<
   CRUDCollectionProps,
   CRUDCollectionState
@@ -130,15 +99,14 @@ export default class CRUDCollection extends PureComponent<
       sortColumn: '',
       sort: 0,
     };
-    this.renderItem = this.renderItem.bind(this);
     this.onAddItem = this.onAddItem.bind(this);
     this.renderExpandableSection = this.renderExpandableSection.bind(this);
     this.getLabel = this.getLabel.bind(this);
     this.onFieldsetChange = this.onFieldsetChange.bind(this);
-    this.renderTableBody = this.renderTableBody.bind(this);
     this.changeCollection = this.changeCollection.bind(this);
-    this.sortColumn = this.sortColumn.bind(this);
-    this.renderSortIcon = this.renderSortIcon.bind(this);
+    this.handleTableChange = this.handleTableChange.bind(this);
+    this.buildTableColumns = this.buildTableColumns.bind(this);
+    this.toggleExpand = this.toggleExpand.bind(this);
   }
 
   UNSAFE_componentWillReceiveProps(nextProps: CRUDCollectionProps) {
@@ -146,30 +114,69 @@ export default class CRUDCollection extends PureComponent<
       const { collection, collectionArray } = createKeyedCollection(
         nextProps.collection,
       );
-      this.setState({
+      this.setState(prevState => ({
         collection,
         collectionArray,
-      });
+        expandedColumns: prevState.expandedColumns,
+      }));
     }
   }
 
   onCellChange(id: number, col: string, val: boolean) {
-    this.changeCollection({
-      ...this.state.collection,
-      [id]: {
-        ...this.state.collection[id],
-        [col]: val,
-      },
+    this.setState(prevState => {
+      const updatedCollection = {
+        ...prevState.collection,
+        [id]: {
+          ...prevState.collection[id],
+          [col]: val,
+        },
+      };
+      const updatedCollectionArray = prevState.collectionArray.map(item =>
+        item.id === id ? updatedCollection[id] : item,
+      );
+
+      if (this.props.onChange) {
+        this.props.onChange(updatedCollectionArray);
+      }
+      return {
+        collection: updatedCollection,
+        collectionArray: updatedCollectionArray,
+      };
     });
   }
 
   onAddItem() {
     if (this.props.itemGenerator) {
       let newItem = this.props.itemGenerator();
+      const shouldStartExpanded = newItem.expanded === true;
       if (!newItem.id) {
         newItem = { ...newItem, id: nanoid() };
       }
-      this.changeCollection(this.state.collection, newItem);
+      delete newItem.expanded;
+
+      this.setState(
+        prevState => {
+          const newCollection = {
+            ...prevState.collection,
+            [newItem.id]: newItem,
+          };
+          const newExpandedColumns = shouldStartExpanded
+            ? { ...prevState.expandedColumns, [newItem.id]: true }
+            : prevState.expandedColumns;
+          const newCollectionArray = [newItem, ...prevState.collectionArray];
+
+          return {
+            collection: newCollection,
+            collectionArray: newCollectionArray,
+            expandedColumns: newExpandedColumns,
+          };
+        },
+        () => {
+          if (this.props.onChange) {
+            this.props.onChange(this.state.collectionArray);
+          }
+        },
+      );
     }
   }
 
@@ -180,52 +187,38 @@ export default class CRUDCollection extends PureComponent<
     });
   }
 
-  getLabel(col: any) {
+  getLabel(col: any): string {
     const { columnLabels } = this.props;
     let label = columnLabels?.[col] ? columnLabels[col] : col;
     if (label.startsWith('__')) {
-      // special label-free columns (ie: caret for expand, delete cross)
       label = '';
     }
     return label;
   }
 
-  getTooltip(col: string) {
+  getTooltip(col: string): string | undefined {
     const { columnLabelTooltips } = this.props;
     return columnLabelTooltips?.[col];
   }
 
-  changeCollection(collection: any, newItem?: object) {
-    this.setState({ collection });
-    if (this.props.onChange) {
-      const collectionArray = this.state.collectionArray
-        .map((c: { id: number }) => collection[c.id])
-        // filter out removed items
-        .filter(c => c !== undefined);
+  changeCollection(collection: any) {
+    console.log({ collection });
+    const newCollectionArray = createCollectionArray(collection);
+    console.log({ newCollectionArray });
+    this.setState({ collection, collectionArray: newCollectionArray });
 
-      if (newItem) {
-        collectionArray.unshift(newItem);
-      }
-      this.props.onChange(collectionArray);
+    if (this.props.onChange) {
+      this.props.onChange(newCollectionArray);
     }
   }
 
-  deleteItem(id: number) {
+  deleteItem(id: string | number) {
     const newColl = { ...this.state.collection };
     delete newColl[id];
     this.changeCollection(newColl);
   }
 
-  effectiveTableColumns() {
-    const { tableColumns, allowDeletes, expandFieldset } = this.props;
-    const cols = allowDeletes
-      ? tableColumns.concat(['__actions'])
-      : tableColumns;
-    return expandFieldset ? ['__expand'].concat(cols) : cols;
-  }
-
   toggleExpand(id: any) {
-    this.onCellChange(id, '__expanded', false);
     this.setState(prevState => ({
       expandedColumns: {
         ...prevState.expandedColumns,
@@ -234,95 +227,62 @@ export default class CRUDCollection extends PureComponent<
     }));
   }
 
-  sortColumn(col: string, sort = SortOrder.Unsorted) {
+  handleTableChange(
+    _pagination: TablePaginationConfig,
+    _filters: Record<string, FilterValue | null>,
+    sorter: SorterResult<CollectionItem> | SorterResult<CollectionItem>[],
+  ) {
+    const columnSorter = Array.isArray(sorter) ? sorter[0] : sorter;
+    let newSortColumn = '';
+    let newSortOrder = 0;
+
+    if (columnSorter?.columnKey && columnSorter?.order) {
+      newSortColumn = columnSorter.columnKey as string;
+      newSortOrder = columnSorter.order === 'ascend' ? 1 : 2;
+    }
+
     const { sortColumns } = this.props;
-    // default sort logic sorting string, boolean and number
-    const compareSort = (m: Sort, n: Sort) => {
-      if (typeof m === 'string') {
-        return (m || ' ').localeCompare(n);
-      }
-      return m - n;
-    };
-    return () => {
-      if (sortColumns?.includes(col)) {
-        // display in unsorted order if no sort specified
-        if (sort === SortOrder.Unsorted) {
-          const { collection } = createKeyedCollection(this.props.collection);
-          const collectionArray = createCollectionArray(collection);
-          this.setState({
-            collectionArray,
-            sortColumn: '',
-            sort,
-          });
-          return;
+    const col = newSortColumn;
+
+    if (sortColumns?.includes(col) || newSortOrder === 0) {
+      let sortedArray = [...this.props.collection];
+
+      if (newSortOrder !== 0) {
+        const compareSort = (m: Sort, n: Sort) => {
+          if (typeof m === 'string' && typeof n === 'string') {
+            return (m || '').localeCompare(n || '');
+          }
+          if (typeof m === 'number' && typeof n === 'number') {
+            return m - n;
+          }
+          if (typeof m === 'boolean' && typeof n === 'boolean') {
+            return m === n ? 0 : m ? 1 : -1;
+          }
+          const mStr = String(m ?? '');
+          const nStr = String(n ?? '');
+          return mStr.localeCompare(nStr);
+        };
+
+        sortedArray.sort((a: any, b: any) => compareSort(a[col], b[col]));
+        if (newSortOrder === 2) {
+          sortedArray.reverse();
         }
-
-        // newly ordered collection
-        const sorted = [...this.state.collectionArray].sort(
-          (a: Record<PropertyKey, any>, b: Record<PropertyKey, any>) =>
-            compareSort(a[col], b[col]),
+      } else {
+        const { collectionArray } = createKeyedCollection(
+          this.props.collection,
         );
-        const newCollection =
-          sort === SortOrder.Asc ? sorted : sorted.reverse();
-
-        this.setState(prevState => ({
-          ...prevState,
-          collectionArray: newCollection,
-          sortColumn: col,
-          sort,
-        }));
+        sortedArray = collectionArray;
       }
-    };
-  }
 
-  renderSortIcon(col: string) {
-    if (this.state.sortColumn === col && this.state.sort === SortOrder.Asc) {
-      return <Icons.SortAsc onClick={this.sortColumn(col, 2)} />;
-    }
-    if (this.state.sortColumn === col && this.state.sort === SortOrder.Desc) {
-      return <Icons.SortDesc onClick={this.sortColumn(col, 0)} />;
+      this.setState({
+        collectionArray: sortedArray,
+        sortColumn: newSortColumn,
+        sort: newSortOrder,
+      });
     }
-    return <Icons.Sort onClick={this.sortColumn(col, 1)} />;
-  }
-
-  renderTH(col: string, sortColumns: Array<string>) {
-    const tooltip = this.getTooltip(col);
-    return (
-      <th key={col} className="no-wrap">
-        {this.getLabel(col)}
-        {tooltip && (
-          <>
-            {' '}
-            <InfoTooltipWithTrigger
-              label={t('description')}
-              tooltip={tooltip}
-            />
-          </>
-        )}
-        {sortColumns?.includes(col) && this.renderSortIcon(col)}
-      </th>
-    );
-  }
-
-  renderHeaderRow() {
-    const cols = this.effectiveTableColumns();
-    const { allowDeletes, expandFieldset, extraButtons, sortColumns } =
-      this.props;
-    return (
-      <thead>
-        <tr>
-          {expandFieldset && <th aria-label="Expand" className="tiny-cell" />}
-          {cols.map(col => this.renderTH(col, sortColumns))}
-          {extraButtons}
-          {allowDeletes && (
-            <th key="delete-item" aria-label="Delete" className="tiny-cell" />
-          )}
-        </tr>
-      </thead>
-    );
   }
 
-  renderExpandableSection(item: any) {
+  renderExpandableSection(item: any): ReactNode {
     const propsGenerator = () => ({ item, onChange: this.onFieldsetChange });
     return recurseReactClone(
       this.props.expandFieldset,
@@ -331,109 +291,116 @@ export default class CRUDCollection extends 
PureComponent<
     );
   }
 
-  getCellProps(record: any, col: any) {
-    const cellPropsFn = this.props.itemCellProps?.[col];
-    const val = record[col];
-    return cellPropsFn ? cellPropsFn(val, this.getLabel(col), record) : {};
-  }
-
-  renderCell(record: any, col: any) {
+  renderCell(record: any, col: any): ReactNode {
     const renderer = this.props.itemRenderers?.[col];
     const val = record[col];
     const onChange = this.onCellChange.bind(this, record.id, col);
     return renderer ? renderer(val, onChange, this.getLabel(col), record) : 
val;
   }
 
-  renderItem(record: any) {
-    const { allowAddItem, allowDeletes, expandFieldset, tableColumns } =
-      this.props;
-    /* eslint-disable no-underscore-dangle */
-    const isExpanded =
-      !!this.state.expandedColumns[record.id] || record.__expanded;
-    let tds = [];
-    if (expandFieldset) {
-      tds.push(
-        <td key="__expand" className="expand">
-          <i
-            role="button"
-            aria-label="Toggle expand"
-            tabIndex={0}
-            // TODO: Remove fa-icon
-            // eslint-disable-next-line icons/no-fa-icons-usage
-            className={`fa fa-caret-${
-              isExpanded ? 'down' : 'right'
-            } text-primary pointer`}
-            onClick={this.toggleExpand.bind(this, record.id)}
-          />
-        </td>,
-      );
-    }
-    tds = tds.concat(
-      tableColumns.map(col => (
-        <td {...this.getCellProps(record, col)} key={col}>
-          {this.renderCell(record, col)}
-        </td>
-      )),
-    );
-    if (allowAddItem) {
-      tds.push(<td key="add" aria-label="Add" />);
-    }
+  buildTableColumns() {
+    const { tableColumns, allowDeletes, sortColumns = [] } = this.props;
+
+    const antdColumns: ColumnsType = tableColumns.map(col => {
+      const label = this.getLabel(col);
+      const tooltip = this.getTooltip(col);
+      const isSortable = sortColumns.includes(col);
+      const currentSortOrder: SortOrder | null | undefined =
+        this.state.sortColumn === col
+          ? this.state.sort === 1
+            ? 'ascend'
+            : this.state.sort === 2
+              ? 'descend'
+              : null
+          : null;
+
+      return {
+        key: col,
+        dataIndex: col,
+        minWidth: 100,
+        title: (
+          <>
+            {label}
+            {tooltip && (
+              <>
+                {' '}
+                <InfoTooltipWithTrigger
+                  label={t('description')}
+                  tooltip={tooltip}
+                  placement="top"
+                />
+              </>
+            )}
+          </>
+        ),
+        render: (text: any, record: CollectionItem) =>
+          this.renderCell(record, col),
+        onCell: (record: CollectionItem) => {
+          const cellPropsFn = this.props.itemCellProps?.[col];
+          const val = record[col];
+          return cellPropsFn ? cellPropsFn(val, label, record) : {};
+        },
+        sorter: isSortable,
+        sortOrder: currentSortOrder,
+      };
+    });
+
     if (allowDeletes) {
-      tds.push(
-        <td
-          key="__actions"
-          data-test="crud-delete-option"
-          className="text-primary"
-        >
-          <Icons.DeleteOutlined
-            aria-label="Delete item"
-            className="pointer"
-            data-test="crud-delete-icon"
-            role="button"
-            tabIndex={0}
-            onClick={this.deleteItem.bind(this, record.id)}
-            iconSize="l"
-          />
-        </td>,
-      );
-    }
-    const trs = [
-      <tr {...{ 'data-test': 'table-row' }} className="row" key={record.id}>
-        {tds}
-      </tr>,
-    ];
-    if (isExpanded) {
-      trs.push(
-        <tr className="exp" key={`exp__${record.id}`}>
-          <td
-            colSpan={this.effectiveTableColumns().length}
-            className="expanded"
+      antdColumns.push({
+        key: '__actions',
+        dataIndex: '__actions',
+        sorter: false,
+        title: <></>,
+        onCell: () => ({}),
+        sortOrder: null,
+        minWidth: 50,
+        render: (_, record: CollectionItem) => (
+          <span
+            data-test="crud-delete-option"
+            className="text-primary"
+            css={{ display: 'flex', justifyContent: 'center' }}
           >
-            <div>{this.renderExpandableSection(record)}</div>
-          </td>
-        </tr>,
-      );
+            <Icons.DeleteOutlined
+              aria-label="Delete item"
+              className="pointer"
+              data-test="crud-delete-icon"
+              role="button"
+              tabIndex={0}
+              onClick={() => this.deleteItem(record.id)}
+              iconSize="l"
+            />
+          </span>
+        ),
+      });
     }
-    return trs;
+
+    return antdColumns as ColumnsType<CollectionItem>;
   }
 
-  renderEmptyCell() {
-    return (
-      <tr>
-        <td className="empty-collection">{this.props.emptyMessage}</td>
-      </tr>
+  render() {
+    const {
+      stickyHeader,
+      emptyMessage = t('No items'),
+      expandFieldset,
+    } = this.props;
+
+    const tableColumns = this.buildTableColumns();
+    const expandedRowKeys = Object.keys(this.state.expandedColumns).filter(
+      id => this.state.expandedColumns[id],
     );
-  }
 
-  renderTableBody() {
-    const data = this.state.collectionArray;
-    const content = data.length
-      ? data.map(d => this.renderItem(d))
-      : this.renderEmptyCell();
-    return <tbody data-test="table-content-rows">{content}</tbody>;
-  }
+    const expandableConfig = expandFieldset
+      ? {
+          expandedRowRender: (record: CollectionItem) =>
+            this.renderExpandableSection(record),
+          rowExpandable: () => true,
+          expandedRowKeys,
+          onExpand: (expanded: boolean, record: CollectionItem) => {
+            this.toggleExpand(record.id);
+          },
+        }
+      : undefined;
 
-  render() {
     return (
       <>
         <CrudButtonWrapper>
@@ -454,15 +421,26 @@ export default class CRUDCollection extends PureComponent<
             </StyledButtonWrapper>
           )}
         </CrudButtonWrapper>
-        <CrudTableWrapper
-          className="CRUD"
-          stickyHeader={this.props.stickyHeader}
-        >
-          <table data-test="crud-table" className="table">
-            {this.renderHeaderRow()}
-            {this.renderTableBody()}
-          </table>
-        </CrudTableWrapper>
+        <Table<CollectionItem>
+          data-test="crud-table"
+          columns={tableColumns}
+          data={this.state.collectionArray as CollectionItem[]}
+          rowKey={(record: CollectionItem) => String(record.id)}
+          sticky={stickyHeader}
+          pagination={false}
+          onChange={this.handleTableChange}
+          locale={{ emptyText: emptyMessage }}
+          css={
+            stickyHeader &&
+            css`
+              height: 350px;
+              overflow: auto;
+            `
+          }
+          expandable={expandableConfig}
+          size={TableSize.Middle}
+          tableLayout="auto"
+        />
       </>
     );
   }
diff --git a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx 
b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
index 6b5e8d21b4..9d9a352e62 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.jsx
@@ -433,9 +433,7 @@ function ColumnCollectionTable({
                 ),
               main_dttm_col: (value, _onItemChange, _label, record) => {
                 const checked = datasource.main_dttm_col === 
record.column_name;
-                const disabled = !columns.find(
-                  column => column.column_name === record.column_name,
-                ).is_dttm;
+                const disabled = !record?.is_dttm;
                 return (
                   <Radio
                     data-test={`radio-default-dttm-${record.column_name}`}
@@ -483,9 +481,7 @@ function ColumnCollectionTable({
                 ),
               main_dttm_col: (value, _onItemChange, _label, record) => {
                 const checked = datasource.main_dttm_col === 
record.column_name;
-                const disabled = !columns.find(
-                  column => column.column_name === record.column_name,
-                ).is_dttm;
+                const disabled = !record?.is_dttm;
                 return (
                   <Radio
                     data-test={`radio-default-dttm-${record.column_name}`}
@@ -1465,7 +1461,7 @@ class DatasourceEditor extends PureComponent {
                       filterable: true,
                       groupby: true,
                       expression: t('<enter SQL expression here>'),
-                      __expanded: true,
+                      expanded: true,
                     })}
                   />
                 </StyledColumnsTabWrapper>
diff --git 
a/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx 
b/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx
index 6b88e55f54..44dd3a4857 100644
--- a/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceEditor.test.jsx
@@ -94,7 +94,7 @@ describe('DatasourceEditor', () => {
     userEvent.click(columnsTab);
 
     const getToggles = screen.getAllByRole('button', {
-      name: /toggle expand/i,
+      name: /expand row/i,
     });
     userEvent.click(getToggles[0]);
     const getTextboxes = screen.getAllByRole('textbox');
@@ -120,7 +120,7 @@ describe('DatasourceEditor', () => {
     userEvent.click(columnsTab);
 
     const getToggles = screen.getAllByRole('button', {
-      name: /toggle expand/i,
+      name: /expand row/i,
     });
 
     userEvent.click(getToggles[0]);
@@ -201,7 +201,7 @@ describe('DatasourceEditor RTL', () => {
     await asyncRender(props);
     const metricButton = screen.getByTestId('collection-tab-Metrics');
     userEvent.click(metricButton);
-    const expandToggle = await screen.findAllByLabelText(/toggle expand/i);
+    const expandToggle = await screen.findAllByLabelText(/expand row/i);
     userEvent.click(expandToggle[0]);
     const certificationDetails = await screen.findByPlaceholderText(
       /certification details/i,
@@ -227,7 +227,7 @@ describe('DatasourceEditor RTL', () => {
     await asyncRender(propsWithCurrency);
     const metricButton = screen.getByTestId('collection-tab-Metrics');
     userEvent.click(metricButton);
-    const expandToggle = await screen.findAllByLabelText(/toggle expand/i);
+    const expandToggle = await screen.findAllByLabelText(/expand row/i);
     userEvent.click(expandToggle[0]);
 
     expect(await screen.findByText('Metric currency')).toBeVisible();
@@ -304,7 +304,7 @@ describe('DatasourceEditor RTL', () => {
     await asyncRender(props);
     const metricButton = screen.getByTestId('collection-tab-Metrics');
     userEvent.click(metricButton);
-    const expandToggle = await screen.findAllByLabelText(/toggle expand/i);
+    const expandToggle = await screen.findAllByLabelText(/expand row/i);
     userEvent.click(expandToggle[1]);
     const certifiedBy = await screen.findByPlaceholderText(/certified by/i);
     userEvent.type(certifiedBy, 'I am typing a new name');
diff --git a/superset-frontend/src/components/Datasource/types.ts 
b/superset-frontend/src/components/Datasource/types.ts
index b5acefe880..62cd19921d 100644
--- a/superset-frontend/src/components/Datasource/types.ts
+++ b/superset-frontend/src/components/Datasource/types.ts
@@ -70,6 +70,7 @@ export interface CRUDCollectionProps {
   ) => ReactNode)[];
   onChange?: (arg0: any) => void;
   tableColumns: any[];
+  tableLayout?: 'fixed' | 'auto';
   sortColumns: string[];
   stickyHeader?: boolean;
 }
diff --git a/superset-frontend/src/components/Table/index.tsx 
b/superset-frontend/src/components/Table/index.tsx
index ea6738f10d..3da7374cee 100644
--- a/superset-frontend/src/components/Table/index.tsx
+++ b/superset-frontend/src/components/Table/index.tsx
@@ -44,6 +44,8 @@ export enum ETableAction {
 }
 
 export type { ColumnsType };
+export type { TablePaginationConfig } from 'antd-v5/es/table';
+export type { SorterResult } from 'antd-v5/es/table/interface';
 export type OnChangeFunction<RecordType> =
   AntTableProps<RecordType>['onChange'];
 

Reply via email to