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/03 21:41:57 UTC

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

lyndsiWilliams commented on code in PR #21520:
URL: https://github.com/apache/superset/pull/21520#discussion_r1013333401


##########
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,

Review Comment:
   ```suggestion
   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
   ```



##########
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 re-usable 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 columne will redner the value as simple text. Often you will want to show formatted values, such as a numeric column shwoing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided tot he optional `render` attribute on a columne definition that enable injecting a sepcific React compoent toenable this.

Review Comment:
   ```suggestion
   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.
   ```



##########
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 columne will redner the value as simple text. Often you will want to show formatted values, such as a numeric column shwoing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided tot he optional `render` attribute on a columne definition that enable injecting a sepcific React compoent toenable this.
+
+<Story id="design-system-components-table-examples--cell-renderers" />
+
+For convenience and consistency the Table component provides pre-build Cell Renderers for:

Review Comment:
   ```suggestion
   For convenience and consistency, the Table component provides pre-built Cell Renderers for:
   ```



##########
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 columne will redner the value as simple text. Often you will want to show formatted values, such as a numeric column shwoing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided tot he optional `render` attribute on a columne definition that enable injecting a sepcific React compoent toenable this.
+
+<Story id="design-system-components-table-examples--cell-renderers" />
+
+For convenience and consistency the Table component provides pre-build 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.
+NOTE: Pagination controls will only display when the data for the table has more records than the default page size.
+
+<Story id="design-system-components-table-examples--many-columns" />
+
+```
+<Table pageSizeOptions={[5, 10, 15, 20, 25] defaultPageSize={10} />
+```
+
+---
+
+## Integration Checklist
+
+The following specifications are required every time a table is used. These choices should be intentional based on the specific user needs for the table instance.
+
+<details>
+
+- [ ] Size
+  - Large
+  - Small
+- Columns
+  - [ ] Number of
+  - [ ] Contents
+  - [ ] Order
+  - [ ] Widths
+- Column headers
+  - [ ] Labels
+  - [ ] Has tooltip
+    - [ ] Tooltip text
+- [ ] Default sort
+- Functionality
+  - [ ] Can sort columns
+  - [ ] Can filter columns
+- [ ] Loading
+  - Pagination
+    - [ ] Number of rows per page
+  - Infinite scroll
+- [ ] Has toolbar
+  - [ ] Has table title
+    - [ ] Label
+  - [ ] Has buttons
+    - [ ] Labels
+    - [ ] Actions
+  - [ ] Has search
+
+</details>
+
+---
+
+## Experimental features
+
+The Table component has features that are still experimental and can be used at your own risk.
+These features are inteded to be made fully stable in future releases

Review Comment:
   ```suggestion
   These features are intended to be made fully stable in future releases.
   ```



##########
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 columne will redner the value as simple text. Often you will want to show formatted values, such as a numeric column shwoing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided tot he optional `render` attribute on a columne definition that enable injecting a sepcific React compoent toenable this.
+
+<Story id="design-system-components-table-examples--cell-renderers" />
+
+For convenience and consistency the Table component provides pre-build 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.
+NOTE: Pagination controls will only display when the data for the table has more records than the default page size.
+
+<Story id="design-system-components-table-examples--many-columns" />
+
+```
+<Table pageSizeOptions={[5, 10, 15, 20, 25] defaultPageSize={10} />
+```
+
+---
+
+## Integration Checklist
+
+The following specifications are required every time a table is used. These choices should be intentional based on the specific user needs for the table instance.
+
+<details>
+
+- [ ] Size
+  - Large
+  - Small
+- Columns
+  - [ ] Number of
+  - [ ] Contents
+  - [ ] Order
+  - [ ] Widths
+- Column headers
+  - [ ] Labels
+  - [ ] Has tooltip
+    - [ ] Tooltip text
+- [ ] Default sort
+- Functionality
+  - [ ] Can sort columns
+  - [ ] Can filter columns
+- [ ] Loading
+  - Pagination
+    - [ ] Number of rows per page
+  - Infinite scroll
+- [ ] Has toolbar
+  - [ ] Has table title
+    - [ ] Label
+  - [ ] Has buttons
+    - [ ] Labels
+    - [ ] Actions
+  - [ ] Has search
+
+</details>
+
+---
+
+## Experimental features
+
+The Table component has features that are still experimental and can be used at your own risk.
+These features are inteded to be made fully stable in future releases
+
+### Resizable Columns
+
+The prop `resizeable` enables table columns to be resized by the user dragging from the right edge of each

Review Comment:
   ```suggestion
   The prop `resizable` enables table columns to be resized by the user dragging from the right edge of each
   ```



##########
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 re-sizeable 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 exceed 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: 600;
+    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?.initializeResizeableColumns(
+          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 "resizeable" prop of Table is experimental and NOT recommended for use in production deployments.',

Review Comment:
   ```suggestion
           'EXPERIMENTAL FEATURE ENABLED: The "resizable" prop of Table is experimental and NOT recommended for use in production deployments.',
   ```



##########
superset-frontend/src/components/Table/utils/InteractiveTableUtils.ts:
##########
@@ -0,0 +1,237 @@
+/**
+ * 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 { SUPERSET_TABLE_COLUMN } from 'src/components/Table';
+import { withinRange } from './utils';
+
+export default class InteractiveTableUtils {
+  tableRef: HTMLTableElement | null;
+
+  columnRef: HTMLElement | null;
+
+  setDerivedColumns: Function;
+
+  isDragging: boolean;
+
+  resizable: boolean;
+
+  reorderable: boolean;
+
+  derivedColumns: Array<any>;
+
+  RESIZE_INDICATOR_THRESHOLD: number;
+
+  constructor(
+    tableRef: HTMLTableElement,
+    derivedColumns: Array<any>,
+    setDerivedColumns: Function,
+  ) {
+    this.setDerivedColumns = setDerivedColumns;
+    this.tableRef = tableRef;
+    this.isDragging = false;
+    this.RESIZE_INDICATOR_THRESHOLD = 8;
+    this.resizable = false;
+    this.reorderable = false;
+    this.derivedColumns = [...derivedColumns];
+    document.addEventListener('mouseup', this.handleMouseup);
+  }
+
+  clearListeners = () => {
+    document.removeEventListener('mouseup', this.handleMouseup);
+    this.initializeResizeableColumns(false, this.tableRef);

Review Comment:
   ```suggestion
       this.initializeResizableColumns(false, this.tableRef);
   ```



##########
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}`);
+
+export interface BasicData {
+  name: string;
+  category: string;
+  price: number;
+  description?: string;
+  key: number;
+}
+
+export interface RendererData {
+  key: number;
+  buttonCell: string;
+  textCell: string;
+  euroCell: number;
+  dollarCell: number;
+}
+
+export interface ExampleData {
+  title: string;
+  name: string;
+  age: number;
+  address: string;
+  tags?: string[];
+  key: number;
+}
+
+function generateValues(amount: number): object {
+  const cells = {};
+  for (let i = 0; i < amount; i += 1) {
+    cells[`col-${i}`] = `Text ${i}`;
+  }
+  return cells;
+}
+
+function generateColumns(amount: number): ColumnsType<ExampleData>[] {
+  const newCols: any[] = [];
+  for (let i = 0; i < amount; i += 1) {
+    newCols.push({
+      title: `Column Header ${i}`,
+      dataIndex: `col-${i}`,
+      key: `col-${i}`,
+    });
+  }
+  return newCols as ColumnsType<ExampleData>[];
+}
+const recordCount = 200;
+const columnCount = 12;
+const randomCols: ColumnsType<ExampleData>[] = generateColumns(columnCount);
+
+const basicData: 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',
+  },
+];
+
+const basicColumns: ColumnsType<BasicData> = [
+  {
+    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',
+  },
+];
+
+const bigColumns: ColumnsType<ExampleData> = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    render: (text: string, row: object, index: number) => (
+      <ButtonCell label={text} onClick={handleClick} row={row} index={index} />
+    ),
+    width: 150,
+  },
+  {
+    title: 'Age',
+    dataIndex: 'age',
+    key: 'age',
+  },
+  {
+    title: 'Address',
+    dataIndex: 'address',
+    key: 'address',
+  },
+  ...(randomCols as ColumnsType<ExampleData>),
+];
+
+const rendererColumns: ColumnsType<RendererData> = [
+  {
+    title: 'Button Cell',
+    dataIndex: 'buttonCell',
+    key: 'buttonCell',
+    width: 150,
+    render: (text: string, data: object, index: number) => (
+      <ButtonCell
+        label={text}
+        row={data}
+        index={index}
+        onClick={(row: object, index: number) =>
+          // eslint-disable-next-line no-alert
+          alert(`Cell was clicked: row ${index}, row: ${JSON.stringify(row)}`)
+        }
+      />
+    ),
+  },
+  {
+    title: 'Text Cell',
+    dataIndex: 'textCell',
+    key: 'textCell',
+  },
+  {
+    title: 'Euro Cell',
+    dataIndex: 'euroCell',
+    key: 'euroCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.EUR }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    title: 'Dollar Cell',
+    dataIndex: 'dollarCell',
+    key: 'dollarCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.USD }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    dataIndex: 'actions',
+    key: 'actions',
+    render: (text: string, row: object) => (
+      <ActionCell row={row} menuOptions={exampleMenuOptions} />
+    ),
+    width: 32,
+    fixed: 'right',
+  },
+];
+
+const baseData: any[] = [

Review Comment:
   Can this `any` be described?



##########
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 columne will redner the value as simple text. Often you will want to show formatted values, such as a numeric column shwoing as currency, or a more complex component such as a button or action menu as a cell value.
+Cell Renderers are React components provided tot he optional `render` attribute on a columne definition that enable injecting a sepcific React compoent toenable this.
+
+<Story id="design-system-components-table-examples--cell-renderers" />
+
+For convenience and consistency the Table component provides pre-build 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.
+NOTE: Pagination controls will only display when the data for the table has more records than the default page size.
+
+<Story id="design-system-components-table-examples--many-columns" />
+
+```
+<Table pageSizeOptions={[5, 10, 15, 20, 25] defaultPageSize={10} />
+```
+
+---
+
+## Integration Checklist
+
+The following specifications are required every time a table is used. These choices should be intentional based on the specific user needs for the table instance.
+
+<details>
+
+- [ ] Size
+  - Large
+  - Small
+- Columns
+  - [ ] Number of
+  - [ ] Contents
+  - [ ] Order
+  - [ ] Widths
+- Column headers
+  - [ ] Labels
+  - [ ] Has tooltip
+    - [ ] Tooltip text
+- [ ] Default sort
+- Functionality
+  - [ ] Can sort columns
+  - [ ] Can filter columns
+- [ ] Loading
+  - Pagination
+    - [ ] Number of rows per page
+  - Infinite scroll
+- [ ] Has toolbar
+  - [ ] Has table title
+    - [ ] Label
+  - [ ] Has buttons
+    - [ ] Labels
+    - [ ] Actions
+  - [ ] Has search
+
+</details>
+
+---
+
+## Experimental features
+
+The Table component has features that are still experimental and can be used at your own risk.
+These features are inteded to be made fully stable in future releases
+
+### Resizable Columns
+
+The prop `resizeable` enables table columns to be resized by the user dragging from the right edge of each
+column to increase or decrease the columns' width
+
+<Story id="design-system-components-table-examples--resizable-columns" />
+
+### Drag & Drop Columns
+
+The prop `reorderable` can enable column drag drop reordering as well as dragging a column to another component. If you want to accept the drop event of a Table Column

Review Comment:
   ```suggestion
   The prop `reorderable` can enable column drag and drop reordering as well as dragging a column to another component. If you want to accept the drop event of a Table Column
   ```



##########
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:
   Is there a different way that the user can be notified of this click, that way there's no need to dodge eslint? Maybe some text nearby instead?



##########
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}`);
+
+export interface BasicData {
+  name: string;
+  category: string;
+  price: number;
+  description?: string;
+  key: number;
+}
+
+export interface RendererData {
+  key: number;
+  buttonCell: string;
+  textCell: string;
+  euroCell: number;
+  dollarCell: number;
+}
+
+export interface ExampleData {
+  title: string;
+  name: string;
+  age: number;
+  address: string;
+  tags?: string[];
+  key: number;
+}
+
+function generateValues(amount: number): object {
+  const cells = {};
+  for (let i = 0; i < amount; i += 1) {
+    cells[`col-${i}`] = `Text ${i}`;
+  }
+  return cells;
+}
+
+function generateColumns(amount: number): ColumnsType<ExampleData>[] {
+  const newCols: any[] = [];
+  for (let i = 0; i < amount; i += 1) {
+    newCols.push({
+      title: `Column Header ${i}`,
+      dataIndex: `col-${i}`,
+      key: `col-${i}`,
+    });
+  }
+  return newCols as ColumnsType<ExampleData>[];
+}
+const recordCount = 200;
+const columnCount = 12;
+const randomCols: ColumnsType<ExampleData>[] = generateColumns(columnCount);
+
+const basicData: 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',
+  },
+];
+
+const basicColumns: ColumnsType<BasicData> = [
+  {
+    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',
+  },
+];
+
+const bigColumns: ColumnsType<ExampleData> = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    render: (text: string, row: object, index: number) => (
+      <ButtonCell label={text} onClick={handleClick} row={row} index={index} />
+    ),
+    width: 150,
+  },
+  {
+    title: 'Age',
+    dataIndex: 'age',
+    key: 'age',
+  },
+  {
+    title: 'Address',
+    dataIndex: 'address',
+    key: 'address',
+  },
+  ...(randomCols as ColumnsType<ExampleData>),
+];
+
+const rendererColumns: ColumnsType<RendererData> = [
+  {
+    title: 'Button Cell',
+    dataIndex: 'buttonCell',
+    key: 'buttonCell',
+    width: 150,
+    render: (text: string, data: object, index: number) => (
+      <ButtonCell
+        label={text}
+        row={data}
+        index={index}
+        onClick={(row: object, index: number) =>
+          // eslint-disable-next-line no-alert
+          alert(`Cell was clicked: row ${index}, row: ${JSON.stringify(row)}`)

Review Comment:
   Same as before, is there a different way that the user can be notified of this click so there's no need to dodge eslint?



##########
superset-frontend/src/components/Table/utils/InteractiveTableUtils.ts:
##########
@@ -0,0 +1,237 @@
+/**
+ * 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 { SUPERSET_TABLE_COLUMN } from 'src/components/Table';
+import { withinRange } from './utils';
+
+export default class InteractiveTableUtils {
+  tableRef: HTMLTableElement | null;
+
+  columnRef: HTMLElement | null;
+
+  setDerivedColumns: Function;
+
+  isDragging: boolean;
+
+  resizable: boolean;
+
+  reorderable: boolean;
+
+  derivedColumns: Array<any>;
+
+  RESIZE_INDICATOR_THRESHOLD: number;
+
+  constructor(
+    tableRef: HTMLTableElement,
+    derivedColumns: Array<any>,
+    setDerivedColumns: Function,
+  ) {
+    this.setDerivedColumns = setDerivedColumns;
+    this.tableRef = tableRef;
+    this.isDragging = false;
+    this.RESIZE_INDICATOR_THRESHOLD = 8;
+    this.resizable = false;
+    this.reorderable = false;
+    this.derivedColumns = [...derivedColumns];
+    document.addEventListener('mouseup', this.handleMouseup);
+  }
+
+  clearListeners = () => {
+    document.removeEventListener('mouseup', this.handleMouseup);
+    this.initializeResizeableColumns(false, this.tableRef);
+    this.initializeDragDropColumns(false, this.tableRef);
+  };
+
+  setTableRef = (table: HTMLTableElement) => {
+    this.tableRef = table;
+  };
+
+  getColumnIndex = (): number => {
+    let index = -1;
+    const parent: HTMLElement | null | undefined = this.columnRef
+      ?.parentNode as HTMLElement;
+    if (parent) {
+      index = Array.prototype.indexOf.call(parent.children, this.columnRef);
+    }
+    return index;
+  };
+
+  handleColumnDragStart = (ev: DragEvent): void => {
+    const target: HTMLElement = ev?.currentTarget as HTMLElement;
+    if (target) {
+      this.columnRef = target;
+    }
+    this.isDragging = true;
+    const index = this.getColumnIndex();
+    const columnData = this.derivedColumns[index];
+    const dragData = { index, columnData };
+    ev?.dataTransfer?.setData(SUPERSET_TABLE_COLUMN, JSON.stringify(dragData));
+  };
+
+  handleDragDrop = (ev: DragEvent): void => {
+    const data = ev.dataTransfer?.getData?.(SUPERSET_TABLE_COLUMN);
+    if (data) {
+      ev.preventDefault();
+      const parent: Element = (ev.currentTarget as HTMLElement)
+        ?.parentNode as HTMLElement;
+      const dropIndex = Array.prototype.indexOf.call(
+        parent.children,
+        ev.currentTarget,
+      );
+      const dragIndex = this.getColumnIndex();
+      const columnsCopy = [...this.derivedColumns];
+      const removedItem = columnsCopy.slice(dragIndex, dragIndex + 1);
+      columnsCopy.splice(dragIndex, 1);
+      columnsCopy.splice(dropIndex, 0, removedItem[0]);
+      this.derivedColumns = [...columnsCopy];
+      this.setDerivedColumns(columnsCopy);
+    }
+  };
+
+  allowDrop = (ev: DragEvent): void => {
+    ev.preventDefault();
+  };
+
+  handleMouseDown = (event: MouseEvent) => {
+    const target: HTMLElement = event?.currentTarget as HTMLElement;
+    if (target) {
+      this.columnRef = target;
+      if (
+        event &&
+        withinRange(
+          event.offsetX,
+          target.offsetWidth,
+          this.RESIZE_INDICATOR_THRESHOLD,
+        )
+      ) {
+        // @ts-ignore
+        target.mouseDown = true;
+        // @ts-ignore
+        target.oldX = event.x;
+        // @ts-ignore
+        target.oldWidth = target.offsetWidth;
+        target.draggable = false;
+      } else if (this.reorderable) {
+        target.draggable = true;
+      }
+    }
+  };
+
+  handleMouseMove = (event: MouseEvent) => {
+    if (this.resizable === true && !this.isDragging) {
+      const target: HTMLElement = event.currentTarget as HTMLElement;
+      if (
+        event &&
+        withinRange(
+          event.offsetX,
+          target.offsetWidth,
+          this.RESIZE_INDICATOR_THRESHOLD,
+        )
+      ) {
+        target.style.cursor = 'col-resize';
+      } else {
+        target.style.cursor = 'default';
+      }
+
+      const column = this.columnRef;
+      // @ts-ignore
+      if (column?.mouseDown) {
+        // @ts-ignore
+        let width = column.oldWidth;
+        // @ts-ignore
+        const diff = event.x - column.oldX;
+        // @ts-ignore
+        if (column.oldWidth + (event.x - column.oldX) > 0) {
+          // @ts-ignore
+          width = column.oldWidth + diff;
+        }
+        const colIndex = this.getColumnIndex();
+        if (!Number.isNaN(colIndex)) {
+          const columnDef = { ...this.derivedColumns[colIndex] };
+          columnDef.width = width;
+          this.derivedColumns[colIndex] = columnDef;
+          this.setDerivedColumns([...this.derivedColumns]);
+        }
+      }
+    }
+  };
+
+  handleMouseup = () => {
+    if (this.columnRef) {
+      // @ts-ignore
+      this.columnRef.mouseDown = false;
+      this.columnRef.style.cursor = 'default';
+      this.columnRef.draggable = false;
+    }
+    this.isDragging = false;
+  };
+
+  initializeResizeableColumns = (

Review Comment:
   ```suggestion
     initializeResizableColumns = (
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,70 @@
+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 thae table rowthat the cell is a member of.

Review Comment:
   ```suggestion
   specific to the data in the table row that the cell is a member of.
   ```



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,70 @@
+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 thae table rowthat 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 arry 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 ading an action cell to cell definitions provided to the table using the ActionCell component

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



##########
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 columne will redner the value as simple text. Often you will want to show formatted values, such as a numeric column shwoing 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/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}`);
+
+export interface BasicData {
+  name: string;
+  category: string;
+  price: number;
+  description?: string;
+  key: number;
+}
+
+export interface RendererData {
+  key: number;
+  buttonCell: string;
+  textCell: string;
+  euroCell: number;
+  dollarCell: number;
+}
+
+export interface ExampleData {
+  title: string;
+  name: string;
+  age: number;
+  address: string;
+  tags?: string[];
+  key: number;
+}
+
+function generateValues(amount: number): object {
+  const cells = {};
+  for (let i = 0; i < amount; i += 1) {
+    cells[`col-${i}`] = `Text ${i}`;
+  }
+  return cells;
+}
+
+function generateColumns(amount: number): ColumnsType<ExampleData>[] {
+  const newCols: any[] = [];
+  for (let i = 0; i < amount; i += 1) {
+    newCols.push({
+      title: `Column Header ${i}`,
+      dataIndex: `col-${i}`,
+      key: `col-${i}`,
+    });
+  }
+  return newCols as ColumnsType<ExampleData>[];
+}
+const recordCount = 200;
+const columnCount = 12;
+const randomCols: ColumnsType<ExampleData>[] = generateColumns(columnCount);
+
+const basicData: 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',
+  },
+];
+
+const basicColumns: ColumnsType<BasicData> = [
+  {
+    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',
+  },
+];
+
+const bigColumns: ColumnsType<ExampleData> = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    render: (text: string, row: object, index: number) => (
+      <ButtonCell label={text} onClick={handleClick} row={row} index={index} />
+    ),
+    width: 150,
+  },
+  {
+    title: 'Age',
+    dataIndex: 'age',
+    key: 'age',
+  },
+  {
+    title: 'Address',
+    dataIndex: 'address',
+    key: 'address',
+  },
+  ...(randomCols as ColumnsType<ExampleData>),
+];
+
+const rendererColumns: ColumnsType<RendererData> = [
+  {
+    title: 'Button Cell',
+    dataIndex: 'buttonCell',
+    key: 'buttonCell',
+    width: 150,
+    render: (text: string, data: object, index: number) => (
+      <ButtonCell
+        label={text}
+        row={data}
+        index={index}
+        onClick={(row: object, index: number) =>
+          // eslint-disable-next-line no-alert
+          alert(`Cell was clicked: row ${index}, row: ${JSON.stringify(row)}`)
+        }
+      />
+    ),
+  },
+  {
+    title: 'Text Cell',
+    dataIndex: 'textCell',
+    key: 'textCell',
+  },
+  {
+    title: 'Euro Cell',
+    dataIndex: 'euroCell',
+    key: 'euroCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.EUR }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    title: 'Dollar Cell',
+    dataIndex: 'dollarCell',
+    key: 'dollarCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.USD }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    dataIndex: 'actions',
+    key: 'actions',
+    render: (text: string, row: object) => (
+      <ActionCell row={row} menuOptions={exampleMenuOptions} />
+    ),
+    width: 32,
+    fixed: 'right',
+  },
+];
+
+const baseData: any[] = [
+  {
+    key: 1,
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer'],
+    ...generateValues(columnCount),
+  },
+  {
+    key: 2,
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser'],
+    ...generateValues(columnCount),
+  },
+  {
+    key: 3,
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher'],
+    ...generateValues(columnCount),
+  },
+];
+
+const bigdata: any[] = [];
+for (let i = 0; i < recordCount; i += 1) {
+  bigdata.push({
+    key: i + baseData.length,
+    name: `Dynamic record ${i}`,
+    age: 32 + i,
+    address: `DynamoCity, Dynamic Lane no. ${i}`,
+    ...generateValues(columnCount),
+  });
+}
+
+export const Basic: ComponentStory<typeof Table> = args => (
+  <ThemeProvider theme={supersetTheme}>
+    <div>
+      <Table {...args} />
+    </div>
+  </ThemeProvider>
+);
+
+function handlers(record: object, rowIndex: number) {
+  return {
+    onClick: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-alert
+      alert(`Click, row:  ${rowIndex}, ${event.currentTarget.tagName}`);
+    }, // click row
+    onDoubleClick: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-alert
+      alert(`Double Click, row:  ${rowIndex}, ${event.currentTarget.tagName}`);
+    }, // double click row
+    onContextMenu: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      event.preventDefault();
+      // eslint-disable-next-line no-alert
+      alert(`Context Menu, row:  ${rowIndex}, ${event.currentTarget.tagName}`);
+    }, // right button click row
+    onMouseEnter: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-console
+      console.log(
+        `Mouse Enter, row:  ${rowIndex}, record: ${JSON.stringify(record)} , ${
+          event.currentTarget.tagName
+        }`,
+      );
+    }, // mouse enter row
+    onMouseLeave: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-console
+      console.log(
+        `Mouse Leave, row:  ${rowIndex}, ${event.currentTarget.tagName}`,
+      );
+    }, // mouse leave row
+  };
+}
+
+Basic.args = {
+  data: basicData,
+  columns: basicColumns,
+  selectedRows: [1],
+  handleRowSelection: (selection: React.Key[]) => {
+    // eslint-disable-next-line no-alert

Review Comment:
   Same as before, is there a different way that the user can be notified of these things? I was thinking maybe a text-based "notification box" nearby or something, that way there aren't so many "no eslint" comments which would make this code easier to read.



##########
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' });
+  const clickHandler = jest.fn();
+  exampleMenuOptions[0].onClick = clickHandler;
+  render(<ActionCell menuOptions={exampleMenuOptions} row={exampleRow} />);
+  // Open the menu
+  await userEvent.click(screen.getByTestId('dropdown-trigger'));
+  // verify all of the menu items are being displayed
+  exampleMenuOptions.forEach((item, index) => {
+    expect(screen.getByText(item.label)).toBeInTheDocument();
+    if (index === 0) {
+      // verify the menu items' onClick gets invoked
+      userEvent.click(screen.getByText(item.label));
+    }
+  });
+  expect(clickHandler).toHaveBeenCalled();
+});
+
+test('appendDataToMenu utility', () => {
+  exampleMenuOptions.forEach(item => expect(item?.row).toBeUndefined());
+  const modifiedMenuOptions = appendDataToMenu(exampleMenuOptions, exampleRow);
+  modifiedMenuOptions.forEach(item => expect(item?.row).toBeDefined());
+});

Review Comment:
   This test is a little unclear to me, what is this test doing?



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,70 @@
+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 thae table rowthat 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 arry 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 ading an action cell to cell definitions provided to the table using the ActionCell component
+for the return valueform the render function on the cell definition. See the [Basic example](./?path=/docs/design-system-components-table-examples--basic)
+
+```
+import ActionCell from './index';
+
+// NOTE: include the other columns you

Review Comment:
   What is this comment saying?



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,70 @@
+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 thae table rowthat 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 arry 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 ading an action cell to cell definitions provided to the table using the ActionCell component
+for the return valueform the render function on the cell definition. See the [Basic example](./?path=/docs/design-system-components-table-examples--basic)

Review Comment:
   ```suggestion
   for the return value from the render function on the cell definition. See the [Basic example](./?path=/docs/design-system-components-table-examples--basic)
   ```



##########
superset-frontend/src/components/Table/cell-renderers/NumericCell/NumericCell.test.tsx:
##########
@@ -0,0 +1,50 @@
+/**
+ * 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 NumericCell, { CurrencyCode, LocaleCode, Style } from './index';
+
+test('renders with french locale and Euro currency format', async () => {

Review Comment:
   ```suggestion
   test('renders with French locale and Euro currency format', () => {
   ```
   There are no async actions in this test so `async` should be good to be removed here.



##########
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' });
+  const clickHandler = jest.fn();
+  exampleMenuOptions[0].onClick = clickHandler;
+  render(<ActionCell menuOptions={exampleMenuOptions} row={exampleRow} />);
+  // Open the menu
+  await userEvent.click(screen.getByTestId('dropdown-trigger'));

Review Comment:
   ```suggestion
     userEvent.click(await screen.findByTestId('dropdown-trigger'));
   ```
   This test has an `act` error, if you change this line it will remove the error.



##########
superset-frontend/src/components/Table/Table.test.tsx:
##########
@@ -0,0 +1,79 @@
+/**
+ * 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(screen.getByText(testColumns[0].title as string)).toBeInTheDocument();

Review Comment:
   ```suggestion
     expect(
       await screen.findByText(testColumns[0].title as string),
     ).toBeInTheDocument();
   ```
   This test has an `act` error, if you change this line it will remove the error.



##########
superset-frontend/src/components/Table/cell-renderers/NumericCell/NumericCell.test.tsx:
##########
@@ -0,0 +1,50 @@
+/**
+ * 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 NumericCell, { CurrencyCode, LocaleCode, Style } from './index';
+
+test('renders with french locale and Euro currency format', async () => {
+  render(
+    <NumericCell
+      value={5678943}
+      locale={LocaleCode.fr}
+      options={{
+        style: Style.CURRENCY,
+        currency: CurrencyCode.EUR,
+      }}
+    />,
+  );
+  expect(screen.getByText('5 678 943,00 €')).toBeInTheDocument();
+});
+
+test('renders with english US locale and USD currency format', async () => {

Review Comment:
   ```suggestion
   test('renders with English US locale and USD currency format', () => {
   ```
   Same as above, no async in this test.



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

Review Comment:
   Can this `any` be defined?



##########
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 re-sizeable 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 exceed 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>`

Review Comment:
   Can these `any`s be defined?



##########
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 re-sizeable by user.

Review Comment:
   ```suggestion
      * Controls if columns are resizable by user.
   ```



##########
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 re-sizeable 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 exceed 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;

Review Comment:
   `HEIGHT_OFFSET` should use the Superset theme's gridvalue since it's a set height.



##########
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 re-sizeable 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 exceed the visible space.

Review Comment:
   ```suggestion
      * when the number of rows exceeds the visible space.
   ```



##########
superset-frontend/src/components/Table/utils/InteractiveTableUtils.ts:
##########
@@ -0,0 +1,237 @@
+/**
+ * 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 { SUPERSET_TABLE_COLUMN } from 'src/components/Table';
+import { withinRange } from './utils';
+
+export default class InteractiveTableUtils {
+  tableRef: HTMLTableElement | null;
+
+  columnRef: HTMLElement | null;
+
+  setDerivedColumns: Function;
+
+  isDragging: boolean;
+
+  resizable: boolean;
+
+  reorderable: boolean;
+
+  derivedColumns: Array<any>;

Review Comment:
   Can this `any` be defined?



##########
superset-frontend/src/components/Table/utils/InteractiveTableUtils.ts:
##########
@@ -0,0 +1,237 @@
+/**
+ * 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 { SUPERSET_TABLE_COLUMN } from 'src/components/Table';
+import { withinRange } from './utils';
+
+export default class InteractiveTableUtils {
+  tableRef: HTMLTableElement | null;
+
+  columnRef: HTMLElement | null;
+
+  setDerivedColumns: Function;
+
+  isDragging: boolean;
+
+  resizable: boolean;
+
+  reorderable: boolean;
+
+  derivedColumns: Array<any>;
+
+  RESIZE_INDICATOR_THRESHOLD: number;
+
+  constructor(
+    tableRef: HTMLTableElement,
+    derivedColumns: Array<any>,

Review Comment:
   Can this `any` be defined?



##########
superset-frontend/src/components/Table/sorters.test.ts:
##########
@@ -0,0 +1,103 @@
+/**
+ * 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 { alphabeticalSort, numericalSort } from './sorters';
+
+const rows = [
+  {
+    name: 'Deathstar Lamp',
+    category: 'Lamp',
+    cost: 75.99,
+  },
+  {
+    name: 'Desk Lamp',
+    category: 'Lamp',
+    cost: 15.99,
+  },
+  {
+    name: 'Bedside Lamp',
+    category: 'Lamp',
+    cost: 15.99,
+  },
+  { name: 'Drafting Desk', category: 'Desk', cost: 125 },
+  { name: 'Sit / Stand Desk', category: 'Desk', cost: 275.99 },
+];
+
+/**
+ * NOTE:  Sorters for antd table use < 0, 0, > 0 for sorting
+ * -1 or less means the first item comes after the second item
+ * 0 means the items sort values is equivalent
+ * 1 or greater means the first item comes before the second item
+ */
+test('alphabeticalSort sorts correctly', () => {
+  expect(alphabeticalSort('name', rows[0], rows[1])).toBe(-1);
+  expect(alphabeticalSort('name', rows[1], rows[0])).toBe(1);
+  expect(alphabeticalSort('category', rows[1], rows[0])).toBe(0);
+});
+
+test('numericalSort sorts correctly', () => {
+  expect(numericalSort('cost', rows[1], rows[2])).toBe(0);
+  expect(numericalSort('cost', rows[1], rows[0])).toBeLessThan(0);
+  expect(numericalSort('cost', rows[4], rows[1])).toBeGreaterThan(0);
+});
+
+/**
+ * We want to make sure our sorters do not throw runtime errors given bad inputs.
+ * Runtime Errors in a sorter will cause a catastrophic React lifecycle error and produce white screen of death
+ * In the case the sorter cannot perform the comparison it should return undefined and the next sort step will proceed without error
+ */
+test('alphabeticalSort bad inputs no errors', () => {
+  // @ts-ignore
+  expect(alphabeticalSort('name', null, null)).toBe(undefined);
+  // incorrect non-object values
+  // @ts-ignore
+  expect(alphabeticalSort('name', 3, [])).toBe(undefined);
+  // incorrect object values without specificed key
+  // @ts-ignore
+  expect(alphabeticalSort('name', {}, {})).toBe(undefined);
+  // Object as value for name when it should be a string
+  // @ts-ignore
+  expect(
+    alphabeticalSort(
+      'name',
+      { name: { title: 'the name attribute should not be an object' } },
+      { name: 'Doug' },
+    ),
+  ).toBe(undefined);
+});
+
+test('numericalSort bad inputs no errors', () => {
+  // @ts-ignore
+  expect(numericalSort('name', undefined, undefined)).toBe(NaN);
+  // @ts-ignore
+  expect(numericalSort('name', null, null)).toBe(NaN);
+  // incorect non-object values
+  // @ts-ignore
+  expect(numericalSort('name', 3, [])).toBe(NaN);
+  // incorect object values without specificed key
+  expect(numericalSort('name', {}, {})).toBe(NaN);
+  // Object as value for naemw when it should be a string

Review Comment:
   ```suggestion
     // incorrect non-object values
     // @ts-ignore
     expect(numericalSort('name', 3, [])).toBe(NaN);
     // incorrect object values without specified key
     expect(numericalSort('name', {}, {})).toBe(NaN);
     // Object as value for name when it should be a string
   ```



##########
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}`);
+
+export interface BasicData {
+  name: string;
+  category: string;
+  price: number;
+  description?: string;
+  key: number;
+}
+
+export interface RendererData {
+  key: number;
+  buttonCell: string;
+  textCell: string;
+  euroCell: number;
+  dollarCell: number;
+}
+
+export interface ExampleData {
+  title: string;
+  name: string;
+  age: number;
+  address: string;
+  tags?: string[];
+  key: number;
+}
+
+function generateValues(amount: number): object {
+  const cells = {};
+  for (let i = 0; i < amount; i += 1) {
+    cells[`col-${i}`] = `Text ${i}`;
+  }
+  return cells;
+}
+
+function generateColumns(amount: number): ColumnsType<ExampleData>[] {
+  const newCols: any[] = [];
+  for (let i = 0; i < amount; i += 1) {
+    newCols.push({
+      title: `Column Header ${i}`,
+      dataIndex: `col-${i}`,
+      key: `col-${i}`,
+    });
+  }
+  return newCols as ColumnsType<ExampleData>[];
+}
+const recordCount = 200;
+const columnCount = 12;
+const randomCols: ColumnsType<ExampleData>[] = generateColumns(columnCount);
+
+const basicData: 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',
+  },
+];
+
+const basicColumns: ColumnsType<BasicData> = [
+  {
+    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',
+  },
+];
+
+const bigColumns: ColumnsType<ExampleData> = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    render: (text: string, row: object, index: number) => (
+      <ButtonCell label={text} onClick={handleClick} row={row} index={index} />
+    ),
+    width: 150,
+  },
+  {
+    title: 'Age',
+    dataIndex: 'age',
+    key: 'age',
+  },
+  {
+    title: 'Address',
+    dataIndex: 'address',
+    key: 'address',
+  },
+  ...(randomCols as ColumnsType<ExampleData>),
+];
+
+const rendererColumns: ColumnsType<RendererData> = [
+  {
+    title: 'Button Cell',
+    dataIndex: 'buttonCell',
+    key: 'buttonCell',
+    width: 150,
+    render: (text: string, data: object, index: number) => (
+      <ButtonCell
+        label={text}
+        row={data}
+        index={index}
+        onClick={(row: object, index: number) =>
+          // eslint-disable-next-line no-alert
+          alert(`Cell was clicked: row ${index}, row: ${JSON.stringify(row)}`)
+        }
+      />
+    ),
+  },
+  {
+    title: 'Text Cell',
+    dataIndex: 'textCell',
+    key: 'textCell',
+  },
+  {
+    title: 'Euro Cell',
+    dataIndex: 'euroCell',
+    key: 'euroCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.EUR }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    title: 'Dollar Cell',
+    dataIndex: 'dollarCell',
+    key: 'dollarCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.USD }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    dataIndex: 'actions',
+    key: 'actions',
+    render: (text: string, row: object) => (
+      <ActionCell row={row} menuOptions={exampleMenuOptions} />
+    ),
+    width: 32,
+    fixed: 'right',
+  },
+];
+
+const baseData: any[] = [
+  {
+    key: 1,
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer'],
+    ...generateValues(columnCount),
+  },
+  {
+    key: 2,
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser'],
+    ...generateValues(columnCount),
+  },
+  {
+    key: 3,
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher'],
+    ...generateValues(columnCount),
+  },
+];
+
+const bigdata: any[] = [];
+for (let i = 0; i < recordCount; i += 1) {
+  bigdata.push({
+    key: i + baseData.length,
+    name: `Dynamic record ${i}`,
+    age: 32 + i,
+    address: `DynamoCity, Dynamic Lane no. ${i}`,
+    ...generateValues(columnCount),
+  });
+}
+
+export const Basic: ComponentStory<typeof Table> = args => (
+  <ThemeProvider theme={supersetTheme}>
+    <div>
+      <Table {...args} />
+    </div>
+  </ThemeProvider>
+);
+
+function handlers(record: object, rowIndex: number) {
+  return {
+    onClick: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-alert
+      alert(`Click, row:  ${rowIndex}, ${event.currentTarget.tagName}`);
+    }, // click row
+    onDoubleClick: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-alert
+      alert(`Double Click, row:  ${rowIndex}, ${event.currentTarget.tagName}`);
+    }, // double click row
+    onContextMenu: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      event.preventDefault();
+      // eslint-disable-next-line no-alert
+      alert(`Context Menu, row:  ${rowIndex}, ${event.currentTarget.tagName}`);
+    }, // right button click row
+    onMouseEnter: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-console
+      console.log(
+        `Mouse Enter, row:  ${rowIndex}, record: ${JSON.stringify(record)} , ${
+          event.currentTarget.tagName
+        }`,
+      );
+    }, // mouse enter row
+    onMouseLeave: (event: React.MouseEvent<HTMLTableRowElement>) => {
+      // eslint-disable-next-line no-console
+      console.log(
+        `Mouse Leave, row:  ${rowIndex}, ${event.currentTarget.tagName}`,
+      );
+    }, // mouse leave row
+  };
+}
+
+Basic.args = {
+  data: basicData,
+  columns: basicColumns,
+  selectedRows: [1],
+  handleRowSelection: (selection: React.Key[]) => {
+    // eslint-disable-next-line no-alert

Review Comment:
   I see more instances of alert that could be addressed as well if there's a text-based route that could be taken instead to appease eslint, I'll leave those comments out for now until we can talk solutions.



##########
superset-frontend/src/components/Table/utils/utils.ts:
##########
@@ -0,0 +1,40 @@
+/**
+ * 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.
+ */
+
+/**
+ * Method to check if a number is within inclusive range between a maximum values minus a threshold

Review Comment:
   ```suggestion
    * Method to check if a number is within inclusive range between a maximum value minus a threshold
   ```



##########
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}`);
+
+export interface BasicData {
+  name: string;
+  category: string;
+  price: number;
+  description?: string;
+  key: number;
+}
+
+export interface RendererData {
+  key: number;
+  buttonCell: string;
+  textCell: string;
+  euroCell: number;
+  dollarCell: number;
+}
+
+export interface ExampleData {
+  title: string;
+  name: string;
+  age: number;
+  address: string;
+  tags?: string[];
+  key: number;
+}
+
+function generateValues(amount: number): object {
+  const cells = {};
+  for (let i = 0; i < amount; i += 1) {
+    cells[`col-${i}`] = `Text ${i}`;
+  }
+  return cells;
+}
+
+function generateColumns(amount: number): ColumnsType<ExampleData>[] {
+  const newCols: any[] = [];
+  for (let i = 0; i < amount; i += 1) {
+    newCols.push({
+      title: `Column Header ${i}`,
+      dataIndex: `col-${i}`,
+      key: `col-${i}`,
+    });
+  }
+  return newCols as ColumnsType<ExampleData>[];
+}
+const recordCount = 200;
+const columnCount = 12;
+const randomCols: ColumnsType<ExampleData>[] = generateColumns(columnCount);
+
+const basicData: 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',
+  },
+];
+
+const basicColumns: ColumnsType<BasicData> = [
+  {
+    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',
+  },
+];
+
+const bigColumns: ColumnsType<ExampleData> = [
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    key: 'name',
+    render: (text: string, row: object, index: number) => (
+      <ButtonCell label={text} onClick={handleClick} row={row} index={index} />
+    ),
+    width: 150,
+  },
+  {
+    title: 'Age',
+    dataIndex: 'age',
+    key: 'age',
+  },
+  {
+    title: 'Address',
+    dataIndex: 'address',
+    key: 'address',
+  },
+  ...(randomCols as ColumnsType<ExampleData>),
+];
+
+const rendererColumns: ColumnsType<RendererData> = [
+  {
+    title: 'Button Cell',
+    dataIndex: 'buttonCell',
+    key: 'buttonCell',
+    width: 150,
+    render: (text: string, data: object, index: number) => (
+      <ButtonCell
+        label={text}
+        row={data}
+        index={index}
+        onClick={(row: object, index: number) =>
+          // eslint-disable-next-line no-alert
+          alert(`Cell was clicked: row ${index}, row: ${JSON.stringify(row)}`)
+        }
+      />
+    ),
+  },
+  {
+    title: 'Text Cell',
+    dataIndex: 'textCell',
+    key: 'textCell',
+  },
+  {
+    title: 'Euro Cell',
+    dataIndex: 'euroCell',
+    key: 'euroCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.EUR }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    title: 'Dollar Cell',
+    dataIndex: 'dollarCell',
+    key: 'dollarCell',
+    render: (value: number) => (
+      <NumericCell
+        options={{ style: Style.CURRENCY, currency: CurrencyCode.USD }}
+        value={value}
+        locale={LocaleCode.en_US}
+      />
+    ),
+  },
+  {
+    dataIndex: 'actions',
+    key: 'actions',
+    render: (text: string, row: object) => (
+      <ActionCell row={row} menuOptions={exampleMenuOptions} />
+    ),
+    width: 32,
+    fixed: 'right',
+  },
+];
+
+const baseData: any[] = [
+  {
+    key: 1,
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer'],
+    ...generateValues(columnCount),
+  },
+  {
+    key: 2,
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser'],
+    ...generateValues(columnCount),
+  },
+  {
+    key: 3,
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher'],
+    ...generateValues(columnCount),
+  },
+];
+
+const bigdata: any[] = [];

Review Comment:
   Can this `any` be described?



##########
superset-frontend/src/components/Table/cell-renderers/ActionCell/ActionCell.overview.mdx:
##########
@@ -0,0 +1,70 @@
+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 thae table rowthat 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 arry of objects that define the label, tooltip, an onClick callback functions

Review Comment:
   ```suggestion
   The action cell accepts an array of objects that define the label, tooltip, an onClick callback functions
   ```



##########
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 re-sizeable 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 exceed 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: 600;
+    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?.initializeResizeableColumns(

Review Comment:
   ```suggestion
           interactiveTableUtils?.current?.initializeResizableColumns(
   ```



##########
superset-frontend/src/components/Table/cell-renderers/NumericCell/index.tsx:
##########
@@ -0,0 +1,418 @@
+/**
+ * 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();
+  try {
+    displayValue = value?.toLocaleString?.(locale, options);
+  } catch (e: any) {

Review Comment:
   Can this `any` be defined?



##########
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 re-sizeable 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 exceed 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: 600;

Review Comment:
   ```suggestion
       font-weight: ${theme.typography.weights.bold};
   ```



-- 
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