You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by GitBox <gi...@apache.org> on 2022/11/08 00:14:45 UTC

[GitHub] [superset] michael-s-molina commented on a diff in pull request #21520: feat: create table component based on ant design Table

michael-s-molina commented on code in PR #21520:
URL: https://github.com/apache/superset/pull/21520#discussion_r1015980277


##########
superset-frontend/src/components/MetadataBar/MetadataBar.stories.mdx:
##########
@@ -1,17 +1,25 @@
-import { Meta, Source } from '@storybook/addon-docs';
+import { Meta, Source, Story } from '@storybook/addon-docs';
 
-<Meta title="MetadataBar/Overview" />
+<Meta title="Design System/Components/MetadataBar/Overview" />
 
-# Usage
+# Metadata bar
 
-The metadata bar component is used to display additional information about an entity. Some of the common applications in Superset are:
+The metadata bar component is used to display additional information about an entity.
+
+## Usage
+
+Some of the common applications in Superset are:
 
 - Display the chart's metadata in Explore to help the user understand what dashboards this chart is added to and get
   to know the details of the chart
 - Display the database's metadata in a drill to detail modal to help the user understand what data they are looking
   at while accessing the feature in the dashboard
 
-# Variations
+## Basic example
+
+<Story id="design-system-components-metadatabar-examples--basic" />

Review Comment:
   You need to update the link to the component page on line 36 to `[MetadataBar](/story/design-system-components-metadatabar-examples--basic)`



##########
superset-frontend/src/components/Table/Table.overview.mdx:
##########
@@ -0,0 +1,260 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Overview" />
+
+# Table
+
+A table is UI that allows the user to explore data in a tabular format.
+
+## Usage
+
+Common table applications in Superset:
+
+- Display lists of user-generated entities (e.g. dashboard, charts, queries) for further exploration and use
+- Display data that can help the user make a decision (e.g. query results)
+
+This component provides a general use Table.
+
+---
+
+### [Basic example](./?path=/docs/design-system-components-table-examples--basic)
+
+<Story id="design-system-components-table-examples--basic" />
+
+### Data and Columns
+
+To set the visible columns and data for the table you use the `columns` and `data` props.
+
+<details>
+
+The basic table example for the `columns` prop is:
+
+```
+const basicColumns: = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    width: 150,
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('name', a, b),
+  },
+  {
+    title: 'Category',
+    dataIndex: 'category',
+    key: 'category',
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('category', a, b),
+  },
+  {
+    title: 'Price',
+    dataIndex: 'price',
+    key: 'price',
+    sorter: (a: BasicData, b: BasicData) =>
+      numericalSort('price', a, b),
+  },
+  {
+    title: 'Description',
+    dataIndex: 'description',
+    key: 'description',
+  },
+];
+```
+
+The data prop is:
+
+```
+const basicData: = [
+  {
+    key: 1,
+    name: 'Floppy Disk 10 pack',
+    category: 'Disk Storage',
+    price: '9.99'
+    description: 'A real blast from the past',
+  },
+  {
+    key: 2,
+    name: 'DVD 100 pack',
+    category: 'Optical Storage',
+    price: '27.99'
+    description: 'Still pretty ancient',
+  },
+  {
+    key: 3,
+    name: '128 GB SSD',
+    category: 'Hardrive',
+    price: '49.99'
+    description: 'Reliable and fast data storage',
+  },
+];
+```
+
+</details>
+
+### Column Sort Functions
+
+To ensure consistency for column sorting and to avoid redundant definitions for common column sorters, reusable sort functions are provided.
+When defining the object for the `columns` prop you can provide an optional attribute `sorter`.
+The function provided in the `sorter` prop is given the entire record representing a row as props `a` and `b`.
+When using a provided sorter function the pattern is to wrap the call to the sorter with an inline function, then specify the specific attribute value from `dataIndex`, representing a column
+of the data object for that row, as the first argument of the sorter function.
+
+#### alphabeticalSort
+
+The alphabeticalSort is for columns that display a string of text.
+
+<details>
+
+```
+import { alphabeticalSort } from 'src/components/Table/sorters';
+
+const basicColumns = [
+  {
+    title: 'Column Name',
+    dataIndex: 'columnName',
+    key: 'columnName',
+    sorter: (a, b) =>
+      alphabeticalSort('columnName', a, b),
+  }
+]
+```
+
+</details>
+
+#### numericSort
+
+The numericalSort is for columns that display a numeric value.
+
+<details>
+
+```
+import { numericalSort } from './sorters';
+
+const basicColumns = [
+  {
+    title: 'Height',
+    dataIndex: 'height',
+    key: 'height',
+    sorter: (a, b) =>
+      numericalSort('height', a, b),
+  }
+]
+```
+
+</details>
+
+If a different sort option is needed, consider adding it as a re-usable sort function following the pattern provided above.

Review Comment:
   ```suggestion
   If a different sort option is needed, consider adding it as a reusable sort function following the pattern provided above.
   ```



##########
superset-frontend/src/components/Table/Table.overview.mdx:
##########
@@ -0,0 +1,260 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Overview" />
+
+# Table
+
+A table is UI that allows the user to explore data in a tabular format.
+
+## Usage
+
+Common table applications in Superset:
+
+- Display lists of user-generated entities (e.g. dashboard, charts, queries) for further exploration and use
+- Display data that can help the user make a decision (e.g. query results)
+
+This component provides a general use Table.
+
+---
+
+### [Basic example](./?path=/docs/design-system-components-table-examples--basic)
+
+<Story id="design-system-components-table-examples--basic" />
+
+### Data and Columns
+
+To set the visible columns and data for the table you use the `columns` and `data` props.
+
+<details>
+
+The basic table example for the `columns` prop is:
+
+```
+const basicColumns: = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    width: 150,
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('name', a, b),
+  },
+  {
+    title: 'Category',
+    dataIndex: 'category',
+    key: 'category',
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('category', a, b),
+  },
+  {
+    title: 'Price',
+    dataIndex: 'price',
+    key: 'price',
+    sorter: (a: BasicData, b: BasicData) =>
+      numericalSort('price', a, b),
+  },
+  {
+    title: 'Description',
+    dataIndex: 'description',
+    key: 'description',
+  },
+];
+```
+
+The data prop is:
+
+```
+const basicData: = [
+  {
+    key: 1,
+    name: 'Floppy Disk 10 pack',
+    category: 'Disk Storage',
+    price: '9.99'
+    description: 'A real blast from the past',
+  },
+  {
+    key: 2,
+    name: 'DVD 100 pack',
+    category: 'Optical Storage',
+    price: '27.99'
+    description: 'Still pretty ancient',
+  },
+  {
+    key: 3,
+    name: '128 GB SSD',
+    category: 'Hardrive',
+    price: '49.99'
+    description: 'Reliable and fast data storage',
+  },
+];
+```
+
+</details>
+
+### Column Sort Functions
+
+To ensure consistency for column sorting and to avoid redundant definitions for common column sorters, reusable sort functions are provided.
+When defining the object for the `columns` prop you can provide an optional attribute `sorter`.
+The function provided in the `sorter` prop is given the entire record representing a row as props `a` and `b`.
+When using a provided sorter function the pattern is to wrap the call to the sorter with an inline function, then specify the specific attribute value from `dataIndex`, representing a column
+of the data object for that row, as the first argument of the sorter function.
+
+#### alphabeticalSort
+
+The alphabeticalSort is for columns that display a string of text.
+
+<details>
+
+```
+import { alphabeticalSort } from 'src/components/Table/sorters';
+
+const basicColumns = [
+  {
+    title: 'Column Name',
+    dataIndex: 'columnName',
+    key: 'columnName',
+    sorter: (a, b) =>
+      alphabeticalSort('columnName', a, b),
+  }
+]
+```
+
+</details>
+
+#### numericSort
+
+The numericalSort is for columns that display a numeric value.
+
+<details>
+
+```
+import { numericalSort } from './sorters';
+
+const basicColumns = [
+  {
+    title: 'Height',
+    dataIndex: 'height',
+    key: 'height',
+    sorter: (a, b) =>
+      numericalSort('height', a, b),
+  }
+]
+```
+
+</details>
+
+If a different sort option is needed, consider adding it as a re-usable sort function following the pattern provided above.
+
+---
+
+### Cell Content Renderers
+
+By default each column will render the value as simple text. Often you will want to show formatted values, such as a numeric column showing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided to the optional `render` attribute on a column definition that enables injecting a specific React component to enable this.
+
+<Story id="design-system-components-table-examples--cell-renderers" />
+
+For convenience and consistency, the Table component provides pre-built Cell Renderers for:
+The following data types can be displayed in table cells.
+
+- Text (default)
+- [Button Cell](./?path=/docs/design-system-components-table-cell-renderers-buttoncell--basic)
+- [Numeric Cell](./docs/design-system-components-table-cell-renderers-numericcell--basic)
+  - Support Locale and currency formatting
+  - w/ icons - Coming Soon
+- [Action Menu Cell](./?path=/docs/design-system-components-table-cell-renderers-actioncell-overview--page)
+- Provide a list of menu options with callback functions that retain a reference to the row the menu is defined for
+- Custom
+  - You can provide your own React component as a cell renderer in cases not supported
+
+---
+
+### Loading
+
+The table can be set to a loading state simply by setting the loading prop to true | false
+
+<Story id="design-system-components-table-examples--loading" />
+
+---
+
+### Pagination
+
+Table displays set number of rows at a time, user navigates table via pagination. Use in scenarios where the user is searching for a specific piece of content.
+The default page size and page size options for menu are configurable via the `pageSizeOptions` and `defaultPageSize` props.

Review Comment:
   ```suggestion
   The default page size and page size options for the menu are configurable via the `pageSizeOptions` and `defaultPageSize` props.
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.test.tsx:
##########
@@ -0,0 +1,49 @@
+/**
+ * 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 React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import { configure } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import ActionCell, { appendDataToMenu } from './index';
+import { exampleMenuOptions, exampleRow } from './fixtures';
+
+test('renders with default props', async () => {
+  configure({ testIdAttribute: 'data-test' });

Review Comment:
   This is already set in `spec/helpers/setup.ts`
   
   ```suggestion
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ButtonCell/ButtonCell.test.tsx:
##########
@@ -0,0 +1,43 @@
+/**
+ * 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 React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import { configure } from '@testing-library/react';

Review Comment:
   ```suggestion
   ```



##########
superset-frontend/src/components/Table/index.tsx:
##########
@@ -0,0 +1,320 @@
+/**
+ * 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.
+ */
+

Review Comment:
   We generally don't leave a blank line after the license. It's not a blocker for me, but if you choose to remove it to keep the codebase consistent then you can update the other files.



##########
superset-frontend/src/components/Table/Table.overview.mdx:
##########
@@ -0,0 +1,260 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Overview" />
+
+# Table
+
+A table is UI that allows the user to explore data in a tabular format.
+
+## Usage
+
+Common table applications in Superset:
+
+- Display lists of user-generated entities (e.g. dashboard, charts, queries) for further exploration and use
+- Display data that can help the user make a decision (e.g. query results)
+
+This component provides a general use Table.
+
+---
+
+### [Basic example](./?path=/docs/design-system-components-table-examples--basic)
+
+<Story id="design-system-components-table-examples--basic" />
+
+### Data and Columns
+
+To set the visible columns and data for the table you use the `columns` and `data` props.
+
+<details>
+
+The basic table example for the `columns` prop is:
+
+```
+const basicColumns: = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    width: 150,
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('name', a, b),
+  },
+  {
+    title: 'Category',
+    dataIndex: 'category',
+    key: 'category',
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('category', a, b),
+  },
+  {
+    title: 'Price',
+    dataIndex: 'price',
+    key: 'price',
+    sorter: (a: BasicData, b: BasicData) =>
+      numericalSort('price', a, b),
+  },
+  {
+    title: 'Description',
+    dataIndex: 'description',
+    key: 'description',
+  },
+];
+```
+
+The data prop is:
+
+```
+const basicData: = [
+  {
+    key: 1,
+    name: 'Floppy Disk 10 pack',
+    category: 'Disk Storage',
+    price: '9.99'
+    description: 'A real blast from the past',
+  },
+  {
+    key: 2,
+    name: 'DVD 100 pack',
+    category: 'Optical Storage',
+    price: '27.99'
+    description: 'Still pretty ancient',
+  },
+  {
+    key: 3,
+    name: '128 GB SSD',
+    category: 'Hardrive',
+    price: '49.99'
+    description: 'Reliable and fast data storage',
+  },
+];
+```
+
+</details>
+
+### Column Sort Functions
+
+To ensure consistency for column sorting and to avoid redundant definitions for common column sorters, reusable sort functions are provided.
+When defining the object for the `columns` prop you can provide an optional attribute `sorter`.
+The function provided in the `sorter` prop is given the entire record representing a row as props `a` and `b`.
+When using a provided sorter function the pattern is to wrap the call to the sorter with an inline function, then specify the specific attribute value from `dataIndex`, representing a column
+of the data object for that row, as the first argument of the sorter function.
+
+#### alphabeticalSort
+
+The alphabeticalSort is for columns that display a string of text.
+
+<details>
+
+```
+import { alphabeticalSort } from 'src/components/Table/sorters';
+
+const basicColumns = [
+  {
+    title: 'Column Name',
+    dataIndex: 'columnName',
+    key: 'columnName',
+    sorter: (a, b) =>
+      alphabeticalSort('columnName', a, b),
+  }
+]
+```
+
+</details>
+
+#### numericSort
+
+The numericalSort is for columns that display a numeric value.
+
+<details>
+
+```
+import { numericalSort } from './sorters';
+
+const basicColumns = [
+  {
+    title: 'Height',
+    dataIndex: 'height',
+    key: 'height',
+    sorter: (a, b) =>
+      numericalSort('height', a, b),
+  }
+]
+```
+
+</details>
+
+If a different sort option is needed, consider adding it as a re-usable sort function following the pattern provided above.
+
+---
+
+### Cell Content Renderers
+
+By default each column will render the value as simple text. Often you will want to show formatted values, such as a numeric column showing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided to the optional `render` attribute on a column definition that enables injecting a specific React component to enable this.
+
+<Story id="design-system-components-table-examples--cell-renderers" />
+
+For convenience and consistency, the Table component provides pre-built Cell Renderers for:
+The following data types can be displayed in table cells.
+
+- Text (default)
+- [Button Cell](./?path=/docs/design-system-components-table-cell-renderers-buttoncell--basic)
+- [Numeric Cell](./docs/design-system-components-table-cell-renderers-numericcell--basic)
+  - Support Locale and currency formatting
+  - w/ icons - Coming Soon
+- [Action Menu Cell](./?path=/docs/design-system-components-table-cell-renderers-actioncell-overview--page)
+- Provide a list of menu options with callback functions that retain a reference to the row the menu is defined for
+- Custom
+  - You can provide your own React component as a cell renderer in cases not supported
+
+---
+
+### Loading
+
+The table can be set to a loading state simply by setting the loading prop to true | false
+
+<Story id="design-system-components-table-examples--loading" />
+
+---
+
+### Pagination
+
+Table displays set number of rows at a time, user navigates table via pagination. Use in scenarios where the user is searching for a specific piece of content.

Review Comment:
   ```suggestion
   The table displays a set number of rows at a time, the user navigates the table via pagination. Use in scenarios where the user is searching for a specific piece of content.
   ```



##########
superset-frontend/src/components/Table/Table.overview.mdx:
##########
@@ -0,0 +1,260 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Overview" />
+
+# Table
+
+A table is UI that allows the user to explore data in a tabular format.
+
+## Usage
+
+Common table applications in Superset:
+
+- Display lists of user-generated entities (e.g. dashboard, charts, queries) for further exploration and use
+- Display data that can help the user make a decision (e.g. query results)
+
+This component provides a general use Table.
+
+---
+
+### [Basic example](./?path=/docs/design-system-components-table-examples--basic)
+
+<Story id="design-system-components-table-examples--basic" />
+
+### Data and Columns
+
+To set the visible columns and data for the table you use the `columns` and `data` props.
+
+<details>
+
+The basic table example for the `columns` prop is:
+
+```
+const basicColumns: = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    width: 150,
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('name', a, b),
+  },
+  {
+    title: 'Category',
+    dataIndex: 'category',
+    key: 'category',
+    sorter: (a: BasicData, b: BasicData) =>
+      alphabeticalSort('category', a, b),
+  },
+  {
+    title: 'Price',
+    dataIndex: 'price',
+    key: 'price',
+    sorter: (a: BasicData, b: BasicData) =>
+      numericalSort('price', a, b),
+  },
+  {
+    title: 'Description',
+    dataIndex: 'description',
+    key: 'description',
+  },
+];
+```
+
+The data prop is:
+
+```
+const basicData: = [
+  {
+    key: 1,
+    name: 'Floppy Disk 10 pack',
+    category: 'Disk Storage',
+    price: '9.99'
+    description: 'A real blast from the past',
+  },
+  {
+    key: 2,
+    name: 'DVD 100 pack',
+    category: 'Optical Storage',
+    price: '27.99'
+    description: 'Still pretty ancient',
+  },
+  {
+    key: 3,
+    name: '128 GB SSD',
+    category: 'Hardrive',
+    price: '49.99'
+    description: 'Reliable and fast data storage',
+  },
+];
+```
+
+</details>
+
+### Column Sort Functions
+
+To ensure consistency for column sorting and to avoid redundant definitions for common column sorters, reusable sort functions are provided.
+When defining the object for the `columns` prop you can provide an optional attribute `sorter`.
+The function provided in the `sorter` prop is given the entire record representing a row as props `a` and `b`.
+When using a provided sorter function the pattern is to wrap the call to the sorter with an inline function, then specify the specific attribute value from `dataIndex`, representing a column
+of the data object for that row, as the first argument of the sorter function.
+
+#### alphabeticalSort
+
+The alphabeticalSort is for columns that display a string of text.
+
+<details>
+
+```
+import { alphabeticalSort } from 'src/components/Table/sorters';
+
+const basicColumns = [
+  {
+    title: 'Column Name',
+    dataIndex: 'columnName',
+    key: 'columnName',
+    sorter: (a, b) =>
+      alphabeticalSort('columnName', a, b),
+  }
+]
+```
+
+</details>
+
+#### numericSort
+
+The numericalSort is for columns that display a numeric value.
+
+<details>
+
+```
+import { numericalSort } from './sorters';
+
+const basicColumns = [
+  {
+    title: 'Height',
+    dataIndex: 'height',
+    key: 'height',
+    sorter: (a, b) =>
+      numericalSort('height', a, b),
+  }
+]
+```
+
+</details>
+
+If a different sort option is needed, consider adding it as a re-usable sort function following the pattern provided above.
+
+---
+
+### Cell Content Renderers
+
+By default each column will render the value as simple text. Often you will want to show formatted values, such as a numeric column showing as currency, or a more complex component such as a button or action menu as a cell value.

Review Comment:
   ```suggestion
   By default, each column will render the value as simple text. Often you will want to show formatted values, such as a numeric column showing as currency, or a more complex component such as a button or action menu as a cell value.
   ```



##########
superset-frontend/src/components/DesignSystem.stories.mdx:
##########
@@ -0,0 +1,25 @@
+import { Meta, Source } from '@storybook/addon-docs';
+import AtomicDesign from './atomic-design.png';
+
+<Meta title="Design System/Introduction" />
+
+# Superset Design System
+
+A design system is a complete set of standards intended to manage design at scale using reusable components and patterns.
+
+You can get an overview of Atmomic Design concepts and link to full book on the topic here:

Review Comment:
   ```suggestion
   You can get an overview of Atomic Design concepts and a link to the full book on the topic here:
   ```



##########
superset-frontend/src/components/Dropdown/index.tsx:
##########
@@ -66,14 +67,35 @@ const MenuDotsWrapper = styled.div`
   padding-left: ${({ theme }) => theme.gridUnit}px;
 `;
 
+export enum IconOrientation {
+  VERTICAL = 'vertical',
+  HORIZONTAL = 'horizontal',
+}
 export interface DropdownProps extends DropDownProps {
   overlay: React.ReactElement;
+  orientation?: IconOrientation;

Review Comment:
   The first time I read the property I thought it referred to the orientation of the dropdown. Maybe rename it to `iconOrientation`? It would be helpful when reading the property from a parent component and the type is not displayed.



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,69 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Cell Renderers/ActionCell/Overview" />
+
+# ActionCell
+
+An ActionCell is used to display overflow icon that opens a menu allowing the user to take actions
+specific to the data in the table row that the cell is a member of.

Review Comment:
   ```suggestion
   An ActionCell is used to display an overflow icon that opens a menu allowing the user to take actions
   specific to the data in the table row that the cell is a member of.
   ```



##########
superset-frontend/src/components/Table/index.tsx:
##########
@@ -0,0 +1,320 @@
+/**
+ * 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 React, { useState, useEffect, useRef, ReactElement } from 'react';
+import { Table as AntTable, ConfigProvider } from 'antd';
+import type { ColumnsType, TableProps as AntTableProps } from 'antd/es/table';
+import { t, useTheme } from '@superset-ui/core';
+import Loading from 'src/components/Loading';
+import styled, { StyledComponent } from '@emotion/styled';
+import InteractiveTableUtils from './utils/InteractiveTableUtils';
+
+export const SUPERSET_TABLE_COLUMN = 'superset/table-column';
+export interface TableDataType {
+  key: React.Key;
+}
+
+export enum SelectionType {
+  'DISABLED' = 'disabled',
+  'SINGLE' = 'single',
+  'MULTI' = 'multi',
+}
+
+export interface Locale {
+  /**
+   * Text contained within the Table UI.
+   */
+  filterTitle: string;
+  filterConfirm: string;
+  filterReset: string;
+  filterEmptyText: string;
+  filterCheckall: string;
+  filterSearchPlaceholder: string;
+  emptyText: string;
+  selectAll: string;
+  selectInvert: string;
+  selectNone: string;
+  selectionAll: string;
+  sortTitle: string;
+  expand: string;
+  collapse: string;
+  triggerDesc: string;
+  triggerAsc: string;
+  cancelSort: string;
+}
+
+export interface TableProps extends AntTableProps<TableProps> {
+  /**
+   * Data that will populate the each row and map to the column key.
+   */
+  data: object[];
+  /**
+   * Table column definitions.
+   */
+  columns: ColumnsType<any>;
+  /**
+   * Array of row keys to represent list of selected rows.
+   */
+  selectedRows?: React.Key[];
+  /**
+   * Callback function invoked when a row is selected by user.
+   */
+  handleRowSelection?: Function;
+  /**
+   * Controls the size of the table.
+   */
+  size: TableSize;
+  /**
+   * Adjusts the padding around elements for different amounts of spacing between elements.
+   */
+  selectionType?: SelectionType;
+  /*
+   * Places table in visual loading state.  Use while waiting to retrieve data or perform an async operation that will update the table.
+   */
+  loading?: boolean;
+  /**
+   * Uses a sticky header which always displays when vertically scrolling the table.  Default: true
+   */
+  sticky?: boolean;
+  /**
+   * Controls if columns are resizable by user.
+   */
+  resizable?: boolean;
+  /**
+   * EXPERIMENTAL: Controls if columns are re-orderable by user drag drop.
+   */
+  reorderable?: boolean;
+  /**
+   * Default number of rows table will display per page of data.
+   */
+  defaultPageSize?: number;
+  /**
+   * Array of numeric options for the number of rows table will display per page of data.
+   * The user can select from these options in the page size drop down menu.
+   */
+  pageSizeOptions?: string[];
+  /**
+   * Set table to display no data even if data has been provided
+   */
+  hideData?: boolean;
+  /**
+   * emptyComponent
+   */
+  emptyComponent?: ReactElement;
+  /**
+   * Enables setting the text displayed in various components and tooltips within the Table UI.
+   */
+  locale?: Locale;
+  /**
+   * Restricts the visible height of the table and allows for internal scrolling within the table
+   * when the number of rows exceeds the visible space.
+   */
+  height?: number;
+}
+
+export enum TableSize {
+  SMALL = 'small',
+  MIDDLE = 'middle',
+}
+
+const defaultRowSelection: React.Key[] = [];
+// This accounts for the tables header and pagination if user gives table instance a height. this is a temp solution
+const HEIGHT_OFFSET = 108;
+
+const StyledTable: StyledComponent<any> = styled(AntTable)<any>`
+  ${({ theme, height }) => `
+  .ant-table-body {
+    overflow: scroll;
+    height: ${height ? `${height - HEIGHT_OFFSET}px` : undefined};
+  }
+
+  th.ant-table-cell {
+    font-weight: ${theme.typography.weights.bold};
+    color: ${theme.colors.grayscale.dark1};
+    user-select: none;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .ant-pagination-item-active {
+    border-color: ${theme.colors.primary.base};
+  }
+  `}
+`;
+
+const defaultLocale = {
+  filterTitle: t('Filter menu'),
+  filterConfirm: t('OK'),
+  filterReset: t('Reset'),
+  filterEmptyText: t('No filters'),
+  filterCheckall: t('Select all items'),
+  filterSearchPlaceholder: t('Search in filters'),
+  emptyText: t('No data'),
+  selectAll: t('Select current page'),
+  selectInvert: t('Invert current page'),
+  selectNone: t('Clear all data'),
+  selectionAll: t('Select all data'),
+  sortTitle: t('Sort'),
+  expand: t('Expand row'),
+  collapse: t('Collapse row'),
+  triggerDesc: t('Click to sort descending'),
+  triggerAsc: t('Click to sort ascending'),
+  cancelSort: t('Click to cancel sorting'),
+};
+
+const selectionMap = {};
+selectionMap[SelectionType.MULTI] = 'checkbox';
+selectionMap[SelectionType.SINGLE] = 'radio';
+selectionMap[SelectionType.DISABLED] = null;
+
+export function Table(props: TableProps) {
+  const {
+    data,
+    columns,
+    selectedRows = defaultRowSelection,
+    handleRowSelection,
+    size,
+    selectionType = SelectionType.DISABLED,
+    sticky = true,
+    loading = false,
+    resizable = false,
+    reorderable = false,
+    defaultPageSize = 15,
+    pageSizeOptions = ['5', '15', '25', '50', '100'],
+    hideData = false,
+    emptyComponent,
+    locale,
+    ...rest
+  } = props;
+
+  const wrapperRef = useRef<HTMLDivElement | null>(null);
+  const [derivedColumns, setDerivedColumns] = useState(columns);
+  const [pageSize, setPageSize] = useState(defaultPageSize);
+  const [mergedLocale, setMergedLocale] = useState({ ...defaultLocale });
+  const [selectedRowKeys, setSelectedRowKeys] =
+    useState<React.Key[]>(selectedRows);
+  const interactiveTableUtils = useRef<InteractiveTableUtils | null>(null);
+
+  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+    setSelectedRowKeys(newSelectedRowKeys);
+    handleRowSelection?.(newSelectedRowKeys);
+  };
+
+  const selectionTypeValue = selectionMap[selectionType];
+  const rowSelection = {
+    type: selectionTypeValue,
+    selectedRowKeys,
+    onChange: onSelectChange,
+  };
+
+  const renderEmpty = () =>
+    emptyComponent ?? <div>{mergedLocale.emptyText}</div>;
+
+  const initializeInteractiveTable = () => {
+    if (interactiveTableUtils.current) {
+      interactiveTableUtils.current?.clearListeners();
+    }
+    const table = wrapperRef.current?.getElementsByTagName('table')[0];
+    if (table) {
+      interactiveTableUtils.current = new InteractiveTableUtils(
+        table,
+        derivedColumns,
+        setDerivedColumns,
+      );
+      if (reorderable) {
+        interactiveTableUtils?.current?.initializeDragDropColumns(
+          reorderable,
+          table,
+        );
+      }
+      if (resizable) {
+        interactiveTableUtils?.current?.initializeResizableColumns(
+          resizable,
+          table,
+        );
+      }
+    }
+  };
+
+  // Log use of experimental features
+  useEffect(() => {
+    if (reorderable === true) {
+      // eslint-disable-next-line no-console
+      console.warn(
+        'EXPERIMENTAL FEATURE ENABLED: The "reorderable" prop of Table is experimental and NOT recommended for use in production deployments.',
+      );
+    }
+    if (resizable === true) {
+      // eslint-disable-next-line no-console
+      console.warn(
+        'EXPERIMENTAL FEATURE ENABLED: The "resizable" prop of Table is experimental and NOT recommended for use in production deployments.',
+      );
+    }
+  }, [reorderable, resizable]);
+
+  useEffect(() => {
+    let updatedLocale;
+    if (locale) {
+      // This spread allows for locale to only contain a subset of locale overrides on props
+      updatedLocale = { ...defaultLocale, ...locale };
+    } else {
+      updatedLocale = { ...defaultLocale };
+    }
+    setMergedLocale(updatedLocale);
+  }, [locale]);
+
+  useEffect(() => {
+    initializeInteractiveTable();
+    return () => {
+      interactiveTableUtils?.current?.clearListeners?.();
+    };
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [wrapperRef, reorderable, resizable]);

Review Comment:
   Can you add the `initializeInteractiveTable` dependency and wrap the function with `useCallback`?



##########
superset-frontend/src/components/DesignSystem.stories.mdx:
##########
@@ -0,0 +1,25 @@
+import { Meta, Source } from '@storybook/addon-docs';
+import AtomicDesign from './atomic-design.png';
+
+<Meta title="Design System/Introduction" />
+
+# Superset Design System
+
+A design system is a complete set of standards intended to manage design at scale using reusable components and patterns.
+
+You can get an overview of Atmomic Design concepts and link to full book on the topic here:
+
+<a href="https://bradfrost.com/blog/post/atomic-web-design/" target="_blank">
+  Intro to Atomic Design
+</a>
+
+While the Superset Design System will use Atomic Design priciples, we are choosing different language to describe the elements.

Review Comment:
   ```suggestion
   While the Superset Design System will use Atomic Design principles, we choose a different language to describe the elements.
   ```



##########
superset-frontend/src/components/Table/Table.test.tsx:
##########
@@ -0,0 +1,85 @@
+/**
+ * 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 React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import type { ColumnsType } from 'antd/es/table';
+import { Table, TableSize } from './index';
+
+interface BasicData {
+  columnName: string;
+  columnType: string;
+  dataType: string;
+}
+
+const testData: BasicData[] = [
+  {
+    columnName: 'Number',
+    columnType: 'Numerical',
+    dataType: 'number',
+  },
+  {
+    columnName: 'String',
+    columnType: 'Physical',
+    dataType: 'string',
+  },
+  {
+    columnName: 'Date',
+    columnType: 'Virtual',
+    dataType: 'date',
+  },
+];
+
+const testColumns: ColumnsType<BasicData> = [
+  {
+    title: 'Column Name',
+    dataIndex: 'columnName',
+    key: 'columnName',
+  },
+  {
+    title: 'Column Type',
+    dataIndex: 'columnType',
+    key: 'columnType',
+  },
+  {
+    title: 'Data Type',
+    dataIndex: 'dataType',
+    key: 'dataType',
+  },
+];
+
+test('renders with default props', async () => {
+  render(
+    <Table size={TableSize.MIDDLE} columns={testColumns} data={testData} />,
+  );
+  expect(
+    await screen.findByText(testColumns[0].title as string),
+  ).toBeInTheDocument();
+  expect(
+    await screen.findByText(testColumns[1].title as string),
+  ).toBeInTheDocument();
+  expect(
+    await screen.findByText(testColumns[2].title as string),
+  ).toBeInTheDocument();

Review Comment:
   ```suggestion
     await waitFor(() =>
       testColumns.forEach(column =>
         expect(screen.getByText(column.title as string)).toBeInTheDocument(),
       ),
     );
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,69 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Cell Renderers/ActionCell/Overview" />
+
+# ActionCell
+
+An ActionCell is used to display overflow icon that opens a menu allowing the user to take actions
+specific to the data in the table row that the cell is a member of.
+
+### [Basic example](./?path=/docs/design-system-components-table-cell-renderers-actioncell--basic)
+
+<Story id="design-system-components-table-cell-renderers-actioncell--basic" />
+
+---
+
+## Usage
+
+The action cell accepts an array of objects that define the label, tooltip, an onClick callback functions
+and an optional data payload to be provided back to the onClick handler function.

Review Comment:
   ```suggestion
   The action cell accepts an array of objects that define the label, tooltip, onClick callback functions,
   and an optional data payload to be provided back to the onClick handler function.
   ```



##########
superset-frontend/src/components/Table/Table.stories.tsx:
##########
@@ -0,0 +1,471 @@
+/**
+ * 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 React from 'react';
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ColumnsType } from 'antd/es/table';
+import { Table, TableSize, SUPERSET_TABLE_COLUMN } from './index';
+import { numericalSort, alphabeticalSort } from './sorters';
+import ButtonCell from './cell-renderers/ButtonCell';
+import ActionCell from './cell-renderers/ActionCell';
+import { exampleMenuOptions } from './cell-renderers/ActionCell/fixtures';
+import NumericCell, {
+  CurrencyCode,
+  LocaleCode,
+  Style,
+} from './cell-renderers/NumericCell';
+
+export default {
+  title: 'Design System/Components/Table/Examples',
+  component: Table,
+} as ComponentMeta<typeof Table>;
+
+// eslint-disable-next-line no-alert
+const handleClick = (data: object, index: number) =>
+  alert(`I was Clicked: ${JSON.stringify(data)}, index: ${index}`);

Review Comment:
   Can we use Storybook actions for this purpose instead of alerts? You can check the MetadataBar component for an example.



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/index.tsx:
##########
@@ -0,0 +1,146 @@
+/**
+ * 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 React, { useState, useEffect } from 'react';
+import { styled } from '@superset-ui/core';
+import { Dropdown, IconOrientation } from 'src/components/Dropdown';
+import { Menu } from 'src/components/Menu';
+import { MenuProps } from 'antd/lib/menu';
+
+/**
+ * Props interface for Action Cell Renderer
+ */
+export interface ActionCellProps {
+  /**
+   * The Menu option presented to user when menu displays
+   */
+  menuOptions: ActionMenuItem[];
+  /**
+   * Object representing the data rendering the Table row with attribute for each column
+   */
+  row: object;
+}
+
+export interface ActionMenuItem {
+  /**
+   * Click handler specific to the menu item
+   * @param menuItem The definition of the menu item that was clicked
+   * @returns ActionMenuItem
+   */
+  onClick: (menuItem: ActionMenuItem) => void;
+  /**
+   * Label user will see displayed in the list of menu options
+   */
+  label: string;
+  /**
+   * Optional tooltip user will see if they hover over the menu option to get more context
+   */
+  tooltip?: string;
+  /**
+   * Optional variable that can contain data relevant to the menu item that you
+   * want easy access to in the callback function for the menu
+   */
+  payload?: any;
+  /**
+   * Object representing the data rendering the Table row with attribute for each column
+   */
+  row?: object;
+}
+
+/**
+ * Props interface for ActionMenu
+ */
+export interface ActionMenuProps {
+  menuOptions: ActionMenuItem[];
+  setVisible: (visible: boolean) => void;
+}
+
+const SHADOW =
+  'box-shadow: 0px 3px 6px -4px rgba(0, 0, 0, 0.12), 0px 9px 28px 8px rgba(0, 0, 0, 0.05)';
+const FILTER = 'drop-shadow(0px 6px 16px rgba(0, 0, 0, 0.08))';
+
+const StyledMenu = styled(Menu)`
+  box-shadow: ${SHADOW} !important;

Review Comment:
   Can we use theme colors for the shadow? You can pick the closest one.



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,69 @@
+import { Meta, Source, Story, ArgsTable } from '@storybook/addon-docs';
+
+<Meta title="Design System/Components/Table/Cell Renderers/ActionCell/Overview" />
+
+# ActionCell
+
+An ActionCell is used to display overflow icon that opens a menu allowing the user to take actions
+specific to the data in the table row that the cell is a member of.
+
+### [Basic example](./?path=/docs/design-system-components-table-cell-renderers-actioncell--basic)
+
+<Story id="design-system-components-table-cell-renderers-actioncell--basic" />
+
+---
+
+## Usage
+
+The action cell accepts an array of objects that define the label, tooltip, an onClick callback functions
+and an optional data payload to be provided back to the onClick handler function.
+
+### [Basic example](./?path=/docs/design-system-components-table-cell-renderers-actioncell--basic)
+
+<Story id="design-system-components-table-cell-renderers-actioncell--basic" />
+
+```
+import { ActionMenuItem } from 'src/components/Table/cell-renderers/index';
+
+export const exampleMenuOptions: ActionMenuItem[] = [
+  {
+    label: 'Action 1',
+    tooltip: "This is a tip, don't spend it all in one place",
+    onClick: (item: ActionMenuItem) => {
+      // eslint-disable-next-line no-alert
+      alert(JSON.stringify(item));
+    },
+    payload: {
+      taco: 'spicy chicken',
+    },
+  },
+  {
+    label: 'Action 2',
+    tooltip: 'This is another tip',
+    onClick: (item: ActionMenuItem) => {
+      // eslint-disable-next-line no-alert
+      alert(JSON.stringify(item));
+    },
+    payload: {
+      taco: 'saucy tofu',
+    },
+  },
+];
+
+```
+
+Within context of adding an action cell to cell definitions provided to the table using the ActionCell component

Review Comment:
   ```suggestion
   Within the context of adding an action cell to cell definitions provided to the table using the ActionCell component
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.test.tsx:
##########
@@ -0,0 +1,49 @@
+/**
+ * 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 React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import { configure } from '@testing-library/react';

Review Comment:
   ```suggestion
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ButtonCell/index.tsx:
##########
@@ -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 React from 'react';
+import Button, { ButtonStyle, ButtonSize } from 'src/components/Button';
+
+type onClickFunction = (row: object, index: number) => void;

Review Comment:
   ```suggestion
   type onClickFunction = (row: object, index: number) => void;
   
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/index.tsx:
##########
@@ -0,0 +1,146 @@
+/**
+ * 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 React, { useState, useEffect } from 'react';
+import { styled } from '@superset-ui/core';
+import { Dropdown, IconOrientation } from 'src/components/Dropdown';
+import { Menu } from 'src/components/Menu';
+import { MenuProps } from 'antd/lib/menu';
+
+/**
+ * Props interface for Action Cell Renderer
+ */
+export interface ActionCellProps {
+  /**
+   * The Menu option presented to user when menu displays
+   */
+  menuOptions: ActionMenuItem[];
+  /**
+   * Object representing the data rendering the Table row with attribute for each column
+   */
+  row: object;
+}
+
+export interface ActionMenuItem {
+  /**
+   * Click handler specific to the menu item
+   * @param menuItem The definition of the menu item that was clicked
+   * @returns ActionMenuItem
+   */
+  onClick: (menuItem: ActionMenuItem) => void;
+  /**
+   * Label user will see displayed in the list of menu options
+   */
+  label: string;
+  /**
+   * Optional tooltip user will see if they hover over the menu option to get more context
+   */
+  tooltip?: string;
+  /**
+   * Optional variable that can contain data relevant to the menu item that you
+   * want easy access to in the callback function for the menu
+   */
+  payload?: any;
+  /**
+   * Object representing the data rendering the Table row with attribute for each column
+   */
+  row?: object;
+}
+
+/**
+ * Props interface for ActionMenu
+ */
+export interface ActionMenuProps {
+  menuOptions: ActionMenuItem[];
+  setVisible: (visible: boolean) => void;
+}
+
+const SHADOW =
+  'box-shadow: 0px 3px 6px -4px rgba(0, 0, 0, 0.12), 0px 9px 28px 8px rgba(0, 0, 0, 0.05)';
+const FILTER = 'drop-shadow(0px 6px 16px rgba(0, 0, 0, 0.08))';
+
+const StyledMenu = styled(Menu)`
+  box-shadow: ${SHADOW} !important;

Review Comment:
   We always try to avoid `!important`.  Is it possible to use a more specific selector?



##########
superset-frontend/src/components/Table/cell-renderers/ButtonCell/ButtonCell.stories.tsx:
##########
@@ -0,0 +1,63 @@
+/**
+ * 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 React from 'react';
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { ButtonCell } from './index';
+
+export default {
+  title: 'Design System/Components/Table/Cell Renderers/ButtonCell',
+  component: ButtonCell,
+} as ComponentMeta<typeof ButtonCell>;
+
+// eslint-disable-next-line no-alert
+const clickHandler = () => alert(`I was Clicked`);

Review Comment:
   Same as before. Use Storybook actions instead of alerts.



##########
superset-frontend/src/components/Table/cell-renderers/NumericCell/index.tsx:
##########
@@ -0,0 +1,419 @@
+/**
+ * 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 React from 'react';

Review Comment:
   ```suggestion
   import React from 'react';
   import { logging } from '@superset-ui/core';
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ButtonCell/ButtonCell.test.tsx:
##########
@@ -0,0 +1,43 @@
+/**
+ * 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 React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import { configure } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import ButtonCell from './index';
+import { exampleRow } from '../fixtures';
+
+test('renders with default props', async () => {
+  configure({ testIdAttribute: 'data-test' });

Review Comment:
   ```suggestion
   ```



##########
superset-frontend/src/components/Table/cell-renderers/NumericCell/index.tsx:
##########
@@ -0,0 +1,419 @@
+/**
+ * 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 React from 'react';
+
+export interface NumericCellProps {
+  /**
+   * The number to display (before optional formatting applied)
+   */
+  value: number;
+  /**
+   * ISO 639-1 language code with optional region or script modifier (e.g. en_US).
+   */
+  locale?: LocaleCode;
+  /**
+   * Options for number formatting
+   */
+  options?: NumberOptions;
+}
+
+interface NumberOptions {
+  /**
+   * Style of number to display
+   */
+  style?: Style;
+
+  /**
+   * ISO 4217 currency code
+   */
+  currency?: CurrencyCode;
+
+  /**
+   * Languages in the form of a ISO 639-1 language code with optional region or script modifier (e.g. de_AT).
+   */
+  maximumFractionDigits?: number;
+
+  /**
+   * A number from 1 to 21 (default is 21)
+   */
+  maximumSignificantDigits?: number;
+
+  /**
+   * A number from 0 to 20 (default is 3)
+   */
+  minimumFractionDigits?: number;
+
+  /**
+   * A number from 1 to 21 (default is 1)
+   */
+  minimumIntegerDigits?: number;
+
+  /**
+   * A number from 1 to 21 (default is 21)
+   */
+  minimumSignificantDigits?: number;
+}
+
+export enum Style {
+  CURRENCY = 'currency',
+  DECIMAL = 'decimal',
+  PERCENT = 'percent',
+}
+
+export enum CurrencyDisplay {
+  SYMBOL = 'symbol',
+  CODE = 'code',
+  NAME = 'name',
+}
+
+export enum LocaleCode {
+  af = 'af',
+  ak = 'ak',
+  sq = 'sq',
+  am = 'am',
+  ar = 'ar',
+  hy = 'hy',
+  as = 'as',
+  az = 'az',
+  bm = 'bm',
+  bn = 'bn',
+  eu = 'eu',
+  be = 'be',
+  bs = 'bs',
+  br = 'br',
+  bg = 'bg',
+  my = 'my',
+  ca = 'ca',
+  ce = 'ce',
+  zh = 'zh',
+  zh_Hans = 'zh-Hans',
+  zh_Hant = 'zh-Hant',
+  cu = 'cu',
+  kw = 'kw',
+  co = 'co',
+  hr = 'hr',
+  cs = 'cs',
+  da = 'da',
+  nl = 'nl',
+  nl_BE = 'nl-BE',
+  dz = 'dz',
+  en = 'en',
+  en_AU = 'en-AU',
+  en_CA = 'en-CA',
+  en_GB = 'en-GB',
+  en_US = 'en-US',
+  eo = 'eo',
+  et = 'et',
+  ee = 'ee',
+  fo = 'fo',
+  fi = 'fi',
+  fr = 'fr',
+  fr_CA = 'fr-CA',
+  fr_CH = 'fr-CH',
+  ff = 'ff',
+  gl = 'gl',
+  lg = 'lg',
+  ka = 'ka',
+  de = 'de',
+  de_AT = 'de-AT',
+  de_CH = 'de-CH',
+  el = 'el',
+  gu = 'gu',
+  ht = 'ht',
+  ha = 'ha',
+  he = 'he',
+  hi = 'hi',
+  hu = 'hu',
+  is = 'is',
+  ig = 'ig',
+  id = 'id',
+  ia = 'ia',
+  ga = 'ga',
+  it = 'it',
+  ja = 'ja',
+  jv = 'jv',
+  kl = 'kl',
+  kn = 'kn',
+  ks = 'ks',
+  kk = 'kk',
+  km = 'km',
+  ki = 'ki',
+  rw = 'rw',
+  ko = 'ko',
+  ku = 'ku',
+  ky = 'ky',
+  lo = 'lo',
+  la = 'la',
+  lv = 'lv',
+  ln = 'ln',
+  lt = 'lt',
+  lu = 'lu',
+  lb = 'lb',
+  mk = 'mk',
+  mg = 'mg',
+  ms = 'ms',
+  ml = 'ml',
+  mt = 'mt',
+  gv = 'gv',
+  mi = 'mi',
+  mr = 'mr',
+  mn = 'mn',
+  ne = 'ne',
+  nd = 'nd',
+  se = 'se',
+  nb = 'nb',
+  nn = 'nn',
+  ny = 'ny',
+  or = 'or',
+  om = 'om',
+  os = 'os',
+  ps = 'ps',
+  fa = 'fa',
+  fa_AF = 'fa-AF',
+  pl = 'pl',
+  pt = 'pt',
+  pt_BR = 'pt-BR',
+  pt_PT = 'pt-PT',
+  pa = 'pa',
+  qu = 'qu',
+  ro = 'ro',
+  ro_MD = 'ro-MD',
+  rm = 'rm',
+  rn = 'rn',
+  ru = 'ru',
+  sm = 'sm',
+  sg = 'sg',
+  sa = 'sa',
+  gd = 'gd',
+  sr = 'sr',
+  sn = 'sn',
+  ii = 'ii',
+  sd = 'sd',
+  si = 'si',
+  sk = 'sk',
+  sl = 'sl',
+  so = 'so',
+  st = 'st',
+  es = 'es',
+  es_ES = 'es-ES',
+  es_MX = 'es-MX',
+  su = 'su',
+  sw = 'sw',
+  sw_CD = 'sw-CD',
+  sv = 'sv',
+  tg = 'tg',
+  ta = 'ta',
+  tt = 'tt',
+  te = 'te',
+  th = 'th',
+  bo = 'bo',
+  ti = 'ti',
+  to = 'to',
+  tr = 'tr',
+  tk = 'tk',
+  uk = 'uk',
+  ur = 'ur',
+  ug = 'ug',
+  uz = 'uz',
+  vi = 'vi',
+  vo = 'vo',
+  cy = 'cy',
+  fy = 'fy',
+  wo = 'wo',
+  xh = 'xh',
+  yi = 'yi',
+  yo = 'yo',
+  zu = 'zu',
+}
+
+export enum CurrencyCode {
+  AED = 'AED',
+  AFN = 'AFN',
+  ALL = 'ALL',
+  AMD = 'AMD',
+  ANG = 'ANG',
+  AOA = 'AOA',
+  ARS = 'ARS',
+  AUD = 'AUD',
+  AWG = 'AWG',
+  AZN = 'AZN',
+  BAM = 'BAM',
+  BBD = 'BBD',
+  BDT = 'BDT',
+  BGN = 'BGN',
+  BHD = 'BHD',
+  BIF = 'BIF',
+  BMD = 'BMD',
+  BND = 'BND',
+  BOB = 'BOB',
+  BRL = 'BRL',
+  BSD = 'BSD',
+  BTN = 'BTN',
+  BWP = 'BWP',
+  BYN = 'BYN',
+  BZD = 'BZD',
+  CAD = 'CAD',
+  CDF = 'CDF',
+  CHF = 'CHF',
+  CLP = 'CLP',
+  CNY = 'CNY',
+  COP = 'COP',
+  CRC = 'CRC',
+  CUC = 'CUC',
+  CUP = 'CUP',
+  CVE = 'CVE',
+  CZK = 'CZK',
+  DJF = 'DJF',
+  DKK = 'DKK',
+  DOP = 'DOP',
+  DZD = 'DZD',
+  EGP = 'EGP',
+  ERN = 'ERN',
+  ETB = 'ETB',
+  EUR = 'EUR',
+  FJD = 'FJD',
+  FKP = 'FKP',
+  GBP = 'GBP',
+  GEL = 'GEL',
+  GHS = 'GHS',
+  GIP = 'GIP',
+  GMD = 'GMD',
+  GNF = 'GNF',
+  GTQ = 'GTQ',
+  GYD = 'GYD',
+  HKD = 'HKD',
+  HNL = 'HNL',
+  HRK = 'HRK',
+  HTG = 'HTG',
+  HUF = 'HUF',
+  IDR = 'IDR',
+  ILS = 'ILS',
+  INR = 'INR',
+  IQD = 'IQD',
+  IRR = 'IRR',
+  ISK = 'ISK',
+  JMD = 'JMD',
+  JOD = 'JOD',
+  JPY = 'JPY',
+  KES = 'KES',
+  KGS = 'KGS',
+  KHR = 'KHR',
+  KMF = 'KMF',
+  KPW = 'KPW',
+  KRW = 'KRW',
+  KWD = 'KWD',
+  KYD = 'KYD',
+  KZT = 'KZT',
+  LAK = 'LAK',
+  LBP = 'LBP',
+  LKR = 'LKR',
+  LRD = 'LRD',
+  LSL = 'LSL',
+  LYD = 'LYD',
+  MAD = 'MAD',
+  MDL = 'MDL',
+  MGA = 'MGA',
+  MKD = 'MKD',
+  MMK = 'MMK',
+  MNT = 'MNT',
+  MOP = 'MOP',
+  MRU = 'MRU',
+  MUR = 'MUR',
+  MVR = 'MVR',
+  MWK = 'MWK',
+  MXN = 'MXN',
+  MYR = 'MYR',
+  MZN = 'MZN',
+  NAD = 'NAD',
+  NGN = 'NGN',
+  NIO = 'NIO',
+  NOK = 'NOK',
+  NPR = 'NPR',
+  NZD = 'NZD',
+  OMR = 'OMR',
+  PAB = 'PAB',
+  PEN = 'PEN',
+  PGK = 'PGK',
+  PHP = 'PHP',
+  PKR = 'PKR',
+  PLN = 'PLN',
+  PYG = 'PYG',
+  QAR = 'QAR',
+  RON = 'RON',
+  RSD = 'RSD',
+  RUB = 'RUB',
+  RWF = 'RWF',
+  SAR = 'SAR',
+  SBD = 'SBD',
+  SCR = 'SCR',
+  SDG = 'SDG',
+  SEK = 'SEK',
+  SGD = 'SGD',
+  SHP = 'SHP',
+  SLL = 'SLL',
+  SOS = 'SOS',
+  SRD = 'SRD',
+  SSP = 'SSP',
+  STN = 'STN',
+  SVC = 'SVC',
+  SYP = 'SYP',
+  SZL = 'SZL',
+  THB = 'THB',
+  TJS = 'TJS',
+  TMT = 'TMT',
+  TND = 'TND',
+  TOP = 'TOP',
+  TRY = 'TRY',
+  TTD = 'TTD',
+  TWD = 'TWD',
+  TZS = 'TZS',
+  UAH = 'UAH',
+  UGX = 'UGX',
+  USD = 'USD',
+  UYU = 'UYU',
+  UZS = 'UZS',
+  VES = 'VES',
+  VND = 'VND',
+  VUV = 'VUV',
+  WST = 'WST',
+  XAF = 'XAF',
+  XCD = 'XCD',
+  XOF = 'XOF',
+  XPF = 'XPF',
+  YER = 'YER',
+  ZAR = 'ZAR',
+  ZMW = 'ZMW',
+  ZWL = 'ZWL',
+}
+
+export function NumericCell(props: NumericCellProps) {
+  const { value, locale = LocaleCode.en_US, options } = props;
+  let displayValue = value?.toString() ?? value;
+  try {
+    displayValue = value?.toLocaleString?.(locale, options);
+  } catch (e) {
+    // eslint-disable-next-line no-console
+    console.error(e);

Review Comment:
   ```suggestion
       logging.error(e);
   ```



##########
superset-frontend/src/components/Table/index.tsx:
##########
@@ -0,0 +1,320 @@
+/**
+ * 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 React, { useState, useEffect, useRef, ReactElement } from 'react';
+import { Table as AntTable, ConfigProvider } from 'antd';
+import type { ColumnsType, TableProps as AntTableProps } from 'antd/es/table';
+import { t, useTheme } from '@superset-ui/core';
+import Loading from 'src/components/Loading';
+import styled, { StyledComponent } from '@emotion/styled';
+import InteractiveTableUtils from './utils/InteractiveTableUtils';
+
+export const SUPERSET_TABLE_COLUMN = 'superset/table-column';
+export interface TableDataType {
+  key: React.Key;
+}
+
+export enum SelectionType {
+  'DISABLED' = 'disabled',
+  'SINGLE' = 'single',
+  'MULTI' = 'multi',
+}
+
+export interface Locale {
+  /**
+   * Text contained within the Table UI.
+   */
+  filterTitle: string;
+  filterConfirm: string;
+  filterReset: string;
+  filterEmptyText: string;
+  filterCheckall: string;
+  filterSearchPlaceholder: string;
+  emptyText: string;
+  selectAll: string;
+  selectInvert: string;
+  selectNone: string;
+  selectionAll: string;
+  sortTitle: string;
+  expand: string;
+  collapse: string;
+  triggerDesc: string;
+  triggerAsc: string;
+  cancelSort: string;
+}
+
+export interface TableProps extends AntTableProps<TableProps> {
+  /**
+   * Data that will populate the each row and map to the column key.
+   */
+  data: object[];
+  /**
+   * Table column definitions.
+   */
+  columns: ColumnsType<any>;
+  /**
+   * Array of row keys to represent list of selected rows.
+   */
+  selectedRows?: React.Key[];
+  /**
+   * Callback function invoked when a row is selected by user.
+   */
+  handleRowSelection?: Function;
+  /**
+   * Controls the size of the table.
+   */
+  size: TableSize;
+  /**
+   * Adjusts the padding around elements for different amounts of spacing between elements.
+   */
+  selectionType?: SelectionType;
+  /*
+   * Places table in visual loading state.  Use while waiting to retrieve data or perform an async operation that will update the table.
+   */
+  loading?: boolean;
+  /**
+   * Uses a sticky header which always displays when vertically scrolling the table.  Default: true
+   */
+  sticky?: boolean;
+  /**
+   * Controls if columns are resizable by user.
+   */
+  resizable?: boolean;
+  /**
+   * EXPERIMENTAL: Controls if columns are re-orderable by user drag drop.
+   */
+  reorderable?: boolean;
+  /**
+   * Default number of rows table will display per page of data.
+   */
+  defaultPageSize?: number;
+  /**
+   * Array of numeric options for the number of rows table will display per page of data.
+   * The user can select from these options in the page size drop down menu.
+   */
+  pageSizeOptions?: string[];
+  /**
+   * Set table to display no data even if data has been provided
+   */
+  hideData?: boolean;
+  /**
+   * emptyComponent
+   */
+  emptyComponent?: ReactElement;
+  /**
+   * Enables setting the text displayed in various components and tooltips within the Table UI.
+   */
+  locale?: Locale;
+  /**
+   * Restricts the visible height of the table and allows for internal scrolling within the table
+   * when the number of rows exceeds the visible space.
+   */
+  height?: number;
+}
+
+export enum TableSize {
+  SMALL = 'small',
+  MIDDLE = 'middle',
+}
+
+const defaultRowSelection: React.Key[] = [];
+// This accounts for the tables header and pagination if user gives table instance a height. this is a temp solution
+const HEIGHT_OFFSET = 108;
+
+const StyledTable: StyledComponent<any> = styled(AntTable)<any>`
+  ${({ theme, height }) => `
+  .ant-table-body {
+    overflow: scroll;
+    height: ${height ? `${height - HEIGHT_OFFSET}px` : undefined};
+  }
+
+  th.ant-table-cell {
+    font-weight: ${theme.typography.weights.bold};
+    color: ${theme.colors.grayscale.dark1};
+    user-select: none;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .ant-pagination-item-active {
+    border-color: ${theme.colors.primary.base};
+  }
+  `}
+`;
+
+const defaultLocale = {
+  filterTitle: t('Filter menu'),
+  filterConfirm: t('OK'),
+  filterReset: t('Reset'),
+  filterEmptyText: t('No filters'),
+  filterCheckall: t('Select all items'),
+  filterSearchPlaceholder: t('Search in filters'),
+  emptyText: t('No data'),
+  selectAll: t('Select current page'),
+  selectInvert: t('Invert current page'),
+  selectNone: t('Clear all data'),
+  selectionAll: t('Select all data'),
+  sortTitle: t('Sort'),
+  expand: t('Expand row'),
+  collapse: t('Collapse row'),
+  triggerDesc: t('Click to sort descending'),
+  triggerAsc: t('Click to sort ascending'),
+  cancelSort: t('Click to cancel sorting'),
+};
+
+const selectionMap = {};
+selectionMap[SelectionType.MULTI] = 'checkbox';
+selectionMap[SelectionType.SINGLE] = 'radio';
+selectionMap[SelectionType.DISABLED] = null;
+
+export function Table(props: TableProps) {
+  const {
+    data,
+    columns,
+    selectedRows = defaultRowSelection,
+    handleRowSelection,
+    size,
+    selectionType = SelectionType.DISABLED,
+    sticky = true,
+    loading = false,
+    resizable = false,
+    reorderable = false,
+    defaultPageSize = 15,
+    pageSizeOptions = ['5', '15', '25', '50', '100'],
+    hideData = false,
+    emptyComponent,
+    locale,
+    ...rest
+  } = props;
+
+  const wrapperRef = useRef<HTMLDivElement | null>(null);
+  const [derivedColumns, setDerivedColumns] = useState(columns);
+  const [pageSize, setPageSize] = useState(defaultPageSize);
+  const [mergedLocale, setMergedLocale] = useState({ ...defaultLocale });
+  const [selectedRowKeys, setSelectedRowKeys] =
+    useState<React.Key[]>(selectedRows);
+  const interactiveTableUtils = useRef<InteractiveTableUtils | null>(null);
+
+  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+    setSelectedRowKeys(newSelectedRowKeys);
+    handleRowSelection?.(newSelectedRowKeys);
+  };
+
+  const selectionTypeValue = selectionMap[selectionType];
+  const rowSelection = {
+    type: selectionTypeValue,
+    selectedRowKeys,
+    onChange: onSelectChange,
+  };
+
+  const renderEmpty = () =>
+    emptyComponent ?? <div>{mergedLocale.emptyText}</div>;
+
+  const initializeInteractiveTable = () => {
+    if (interactiveTableUtils.current) {
+      interactiveTableUtils.current?.clearListeners();
+    }
+    const table = wrapperRef.current?.getElementsByTagName('table')[0];
+    if (table) {
+      interactiveTableUtils.current = new InteractiveTableUtils(
+        table,
+        derivedColumns,
+        setDerivedColumns,
+      );
+      if (reorderable) {
+        interactiveTableUtils?.current?.initializeDragDropColumns(
+          reorderable,
+          table,
+        );
+      }
+      if (resizable) {
+        interactiveTableUtils?.current?.initializeResizableColumns(
+          resizable,
+          table,
+        );
+      }
+    }
+  };
+
+  // Log use of experimental features
+  useEffect(() => {
+    if (reorderable === true) {
+      // eslint-disable-next-line no-console
+      console.warn(
+        'EXPERIMENTAL FEATURE ENABLED: The "reorderable" prop of Table is experimental and NOT recommended for use in production deployments.',
+      );
+    }
+    if (resizable === true) {
+      // eslint-disable-next-line no-console
+      console.warn(

Review Comment:
   ```suggestion
         logging.warn(
   ```



##########
superset-frontend/src/components/Table/index.tsx:
##########
@@ -0,0 +1,320 @@
+/**
+ * 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 React, { useState, useEffect, useRef, ReactElement } from 'react';
+import { Table as AntTable, ConfigProvider } from 'antd';
+import type { ColumnsType, TableProps as AntTableProps } from 'antd/es/table';
+import { t, useTheme } from '@superset-ui/core';
+import Loading from 'src/components/Loading';
+import styled, { StyledComponent } from '@emotion/styled';
+import InteractiveTableUtils from './utils/InteractiveTableUtils';
+
+export const SUPERSET_TABLE_COLUMN = 'superset/table-column';
+export interface TableDataType {
+  key: React.Key;
+}
+
+export enum SelectionType {
+  'DISABLED' = 'disabled',
+  'SINGLE' = 'single',
+  'MULTI' = 'multi',
+}
+
+export interface Locale {
+  /**
+   * Text contained within the Table UI.
+   */
+  filterTitle: string;
+  filterConfirm: string;
+  filterReset: string;
+  filterEmptyText: string;
+  filterCheckall: string;
+  filterSearchPlaceholder: string;
+  emptyText: string;
+  selectAll: string;
+  selectInvert: string;
+  selectNone: string;
+  selectionAll: string;
+  sortTitle: string;
+  expand: string;
+  collapse: string;
+  triggerDesc: string;
+  triggerAsc: string;
+  cancelSort: string;
+}
+
+export interface TableProps extends AntTableProps<TableProps> {
+  /**
+   * Data that will populate the each row and map to the column key.
+   */
+  data: object[];
+  /**
+   * Table column definitions.
+   */
+  columns: ColumnsType<any>;
+  /**
+   * Array of row keys to represent list of selected rows.
+   */
+  selectedRows?: React.Key[];
+  /**
+   * Callback function invoked when a row is selected by user.
+   */
+  handleRowSelection?: Function;
+  /**
+   * Controls the size of the table.
+   */
+  size: TableSize;
+  /**
+   * Adjusts the padding around elements for different amounts of spacing between elements.
+   */
+  selectionType?: SelectionType;
+  /*
+   * Places table in visual loading state.  Use while waiting to retrieve data or perform an async operation that will update the table.
+   */
+  loading?: boolean;
+  /**
+   * Uses a sticky header which always displays when vertically scrolling the table.  Default: true
+   */
+  sticky?: boolean;
+  /**
+   * Controls if columns are resizable by user.
+   */
+  resizable?: boolean;
+  /**
+   * EXPERIMENTAL: Controls if columns are re-orderable by user drag drop.
+   */
+  reorderable?: boolean;
+  /**
+   * Default number of rows table will display per page of data.
+   */
+  defaultPageSize?: number;
+  /**
+   * Array of numeric options for the number of rows table will display per page of data.
+   * The user can select from these options in the page size drop down menu.
+   */
+  pageSizeOptions?: string[];
+  /**
+   * Set table to display no data even if data has been provided
+   */
+  hideData?: boolean;
+  /**
+   * emptyComponent
+   */
+  emptyComponent?: ReactElement;
+  /**
+   * Enables setting the text displayed in various components and tooltips within the Table UI.
+   */
+  locale?: Locale;
+  /**
+   * Restricts the visible height of the table and allows for internal scrolling within the table
+   * when the number of rows exceeds the visible space.
+   */
+  height?: number;
+}
+
+export enum TableSize {
+  SMALL = 'small',
+  MIDDLE = 'middle',
+}
+
+const defaultRowSelection: React.Key[] = [];
+// This accounts for the tables header and pagination if user gives table instance a height. this is a temp solution
+const HEIGHT_OFFSET = 108;
+
+const StyledTable: StyledComponent<any> = styled(AntTable)<any>`
+  ${({ theme, height }) => `
+  .ant-table-body {
+    overflow: scroll;
+    height: ${height ? `${height - HEIGHT_OFFSET}px` : undefined};
+  }
+
+  th.ant-table-cell {
+    font-weight: ${theme.typography.weights.bold};
+    color: ${theme.colors.grayscale.dark1};
+    user-select: none;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .ant-pagination-item-active {
+    border-color: ${theme.colors.primary.base};
+  }
+  `}
+`;
+
+const defaultLocale = {
+  filterTitle: t('Filter menu'),
+  filterConfirm: t('OK'),
+  filterReset: t('Reset'),
+  filterEmptyText: t('No filters'),
+  filterCheckall: t('Select all items'),
+  filterSearchPlaceholder: t('Search in filters'),
+  emptyText: t('No data'),
+  selectAll: t('Select current page'),
+  selectInvert: t('Invert current page'),
+  selectNone: t('Clear all data'),
+  selectionAll: t('Select all data'),
+  sortTitle: t('Sort'),
+  expand: t('Expand row'),
+  collapse: t('Collapse row'),
+  triggerDesc: t('Click to sort descending'),
+  triggerAsc: t('Click to sort ascending'),
+  cancelSort: t('Click to cancel sorting'),
+};
+
+const selectionMap = {};
+selectionMap[SelectionType.MULTI] = 'checkbox';
+selectionMap[SelectionType.SINGLE] = 'radio';
+selectionMap[SelectionType.DISABLED] = null;
+
+export function Table(props: TableProps) {
+  const {
+    data,
+    columns,
+    selectedRows = defaultRowSelection,
+    handleRowSelection,
+    size,
+    selectionType = SelectionType.DISABLED,
+    sticky = true,
+    loading = false,
+    resizable = false,
+    reorderable = false,
+    defaultPageSize = 15,
+    pageSizeOptions = ['5', '15', '25', '50', '100'],
+    hideData = false,
+    emptyComponent,
+    locale,
+    ...rest
+  } = props;
+
+  const wrapperRef = useRef<HTMLDivElement | null>(null);
+  const [derivedColumns, setDerivedColumns] = useState(columns);
+  const [pageSize, setPageSize] = useState(defaultPageSize);
+  const [mergedLocale, setMergedLocale] = useState({ ...defaultLocale });
+  const [selectedRowKeys, setSelectedRowKeys] =
+    useState<React.Key[]>(selectedRows);
+  const interactiveTableUtils = useRef<InteractiveTableUtils | null>(null);
+
+  const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+    setSelectedRowKeys(newSelectedRowKeys);
+    handleRowSelection?.(newSelectedRowKeys);
+  };
+
+  const selectionTypeValue = selectionMap[selectionType];
+  const rowSelection = {
+    type: selectionTypeValue,
+    selectedRowKeys,
+    onChange: onSelectChange,
+  };
+
+  const renderEmpty = () =>
+    emptyComponent ?? <div>{mergedLocale.emptyText}</div>;
+
+  const initializeInteractiveTable = () => {
+    if (interactiveTableUtils.current) {
+      interactiveTableUtils.current?.clearListeners();
+    }
+    const table = wrapperRef.current?.getElementsByTagName('table')[0];
+    if (table) {
+      interactiveTableUtils.current = new InteractiveTableUtils(
+        table,
+        derivedColumns,
+        setDerivedColumns,
+      );
+      if (reorderable) {
+        interactiveTableUtils?.current?.initializeDragDropColumns(
+          reorderable,
+          table,
+        );
+      }
+      if (resizable) {
+        interactiveTableUtils?.current?.initializeResizableColumns(
+          resizable,
+          table,
+        );
+      }
+    }
+  };
+
+  // Log use of experimental features
+  useEffect(() => {
+    if (reorderable === true) {
+      // eslint-disable-next-line no-console
+      console.warn(

Review Comment:
   ```suggestion
         logging.warn(
   ```



##########
superset-frontend/src/components/Table/index.tsx:
##########
@@ -0,0 +1,320 @@
+/**
+ * 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 React, { useState, useEffect, useRef, ReactElement } from 'react';
+import { Table as AntTable, ConfigProvider } from 'antd';
+import type { ColumnsType, TableProps as AntTableProps } from 'antd/es/table';
+import { t, useTheme } from '@superset-ui/core';

Review Comment:
   ```suggestion
   import { t, useTheme, logging } from '@superset-ui/core';
   ```



##########
superset-frontend/src/components/Loading/index.tsx:
##########
@@ -20,6 +20,8 @@
 import React from 'react';
 import { styled } from '@superset-ui/core';
 import cls from 'classnames';
+// @ts-ignore

Review Comment:
   You can follow the same pattern as https://github.com/apache/superset/blob/cd1b379bdf323f78c2e7d574525a55898c920942/superset-frontend/plugins/plugin-chart-table/types/external.d.ts#L20
   
   If you search for `declare module` in the codebase you'll see many examples.



##########
superset-frontend/src/components/Table/index.tsx:
##########
@@ -0,0 +1,320 @@
+/**
+ * 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 React, { useState, useEffect, useRef, ReactElement } from 'react';
+import { Table as AntTable, ConfigProvider } from 'antd';
+import type { ColumnsType, TableProps as AntTableProps } from 'antd/es/table';
+import { t, useTheme } from '@superset-ui/core';
+import Loading from 'src/components/Loading';
+import styled, { StyledComponent } from '@emotion/styled';
+import InteractiveTableUtils from './utils/InteractiveTableUtils';
+
+export const SUPERSET_TABLE_COLUMN = 'superset/table-column';
+export interface TableDataType {
+  key: React.Key;
+}
+
+export enum SelectionType {
+  'DISABLED' = 'disabled',
+  'SINGLE' = 'single',
+  'MULTI' = 'multi',
+}
+
+export interface Locale {
+  /**
+   * Text contained within the Table UI.
+   */
+  filterTitle: string;
+  filterConfirm: string;
+  filterReset: string;
+  filterEmptyText: string;
+  filterCheckall: string;
+  filterSearchPlaceholder: string;
+  emptyText: string;
+  selectAll: string;
+  selectInvert: string;
+  selectNone: string;
+  selectionAll: string;
+  sortTitle: string;
+  expand: string;
+  collapse: string;
+  triggerDesc: string;
+  triggerAsc: string;
+  cancelSort: string;
+}
+
+export interface TableProps extends AntTableProps<TableProps> {
+  /**
+   * Data that will populate the each row and map to the column key.
+   */
+  data: object[];
+  /**
+   * Table column definitions.
+   */
+  columns: ColumnsType<any>;
+  /**
+   * Array of row keys to represent list of selected rows.
+   */
+  selectedRows?: React.Key[];
+  /**
+   * Callback function invoked when a row is selected by user.
+   */
+  handleRowSelection?: Function;
+  /**
+   * Controls the size of the table.
+   */
+  size: TableSize;
+  /**
+   * Adjusts the padding around elements for different amounts of spacing between elements.
+   */
+  selectionType?: SelectionType;
+  /*
+   * Places table in visual loading state.  Use while waiting to retrieve data or perform an async operation that will update the table.
+   */
+  loading?: boolean;
+  /**
+   * Uses a sticky header which always displays when vertically scrolling the table.  Default: true
+   */
+  sticky?: boolean;
+  /**
+   * Controls if columns are resizable by user.
+   */
+  resizable?: boolean;
+  /**
+   * EXPERIMENTAL: Controls if columns are re-orderable by user drag drop.
+   */
+  reorderable?: boolean;
+  /**
+   * Default number of rows table will display per page of data.
+   */
+  defaultPageSize?: number;
+  /**
+   * Array of numeric options for the number of rows table will display per page of data.
+   * The user can select from these options in the page size drop down menu.
+   */
+  pageSizeOptions?: string[];
+  /**
+   * Set table to display no data even if data has been provided
+   */
+  hideData?: boolean;
+  /**
+   * emptyComponent
+   */
+  emptyComponent?: ReactElement;
+  /**
+   * Enables setting the text displayed in various components and tooltips within the Table UI.
+   */
+  locale?: Locale;
+  /**
+   * Restricts the visible height of the table and allows for internal scrolling within the table
+   * when the number of rows exceeds the visible space.
+   */
+  height?: number;
+}
+
+export enum TableSize {
+  SMALL = 'small',
+  MIDDLE = 'middle',
+}
+
+const defaultRowSelection: React.Key[] = [];
+// This accounts for the tables header and pagination if user gives table instance a height. this is a temp solution
+const HEIGHT_OFFSET = 108;
+
+const StyledTable: StyledComponent<any> = styled(AntTable)<any>`
+  ${({ theme, height }) => `
+  .ant-table-body {
+    overflow: scroll;
+    height: ${height ? `${height - HEIGHT_OFFSET}px` : undefined};
+  }
+
+  th.ant-table-cell {
+    font-weight: ${theme.typography.weights.bold};
+    color: ${theme.colors.grayscale.dark1};
+    user-select: none;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .ant-pagination-item-active {
+    border-color: ${theme.colors.primary.base};
+  }
+  `}

Review Comment:
   ```suggestion
     ${({ theme, height }) => `
       .ant-table-body {
         overflow: scroll;
         height: ${height ? `${height - HEIGHT_OFFSET}px` : undefined};
       }
   
       th.ant-table-cell {
         font-weight: ${theme.typography.weights.bold};
         color: ${theme.colors.grayscale.dark1};
         user-select: none;
         white-space: nowrap;
         overflow: hidden;
         text-overflow: ellipsis;
       }
   
       .ant-pagination-item-active {
         border-color: ${theme.colors.primary.base};
       }
     `}
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org