From 2c020ff2a04d5a807300b572e313a48be69464a3 Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Tue, 25 May 2021 14:55:02 -0600 Subject: [PATCH 1/9] Change TableElement from a class component to a functional component --- .../src/SqlLab/components/TableElement.jsx | 118 ++++++++---------- 1 file changed, 53 insertions(+), 65 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index e44cccf0d695e..8663778734902 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import Collapse from 'src/components/Collapse'; import Card from 'src/components/Card'; @@ -56,44 +56,37 @@ const Fade = styled.div` opacity: ${props => (props.hovered ? 1 : 0)}; `; -class TableElement extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - sortColumns: false, - hovered: false, - }; - this.toggleSortColumns = this.toggleSortColumns.bind(this); - this.removeTable = this.removeTable.bind(this); - this.setHover = debounce(this.setHover.bind(this), 100); - } +const TableElement = props => { + const [sortColumns, setSortColumns] = useState(false); + const [hovered, setHovered] = useState(false); - setHover(hovered) { - this.setState({ hovered }); - } + const { table, actions } = props; + + const setHover = hovered => { + debounce(() => setHovered(hovered), 100)(); + }; - popSelectStar() { + const popSelectStar = () => { const qe = { id: shortid.generate(), - title: this.props.table.name, - dbId: this.props.table.dbId, + title: table.name, + dbId: table.dbId, autorun: true, - sql: this.props.table.selectStar, + sql: table.selectStar, }; - this.props.actions.addQueryEditor(qe); - } + actions.addQueryEditor(qe); + }; - removeTable() { - this.props.actions.removeDataPreview(this.props.table); - this.props.actions.removeTable(this.props.table); - } + const removeTable = () => { + actions.removeDataPreview(table); + actions.removeTable(table); + }; - toggleSortColumns() { - this.setState(prevState => ({ sortColumns: !prevState.sortColumns })); - } + const toggleSortColumns = () => { + setSortColumns(prevState => !prevState); + }; - renderWell() { - const { table } = this.props; + const renderWell = () => { let header; if (table.partitions) { let partitionQuery; @@ -126,11 +119,10 @@ class TableElement extends React.PureComponent { ); } return header; - } + }; - renderControls() { + const renderControls = () => { let keyLink; - const { table } = this.props; if (table.indexes && table.indexes.length > 0) { keyLink = ( ); - } + }; - renderHeader() { - const { table } = this.props; + const renderHeader = () => { return (
this.setHover(true)} - onMouseLeave={() => this.setHover(false)} + onMouseEnter={() => setHover(true)} + onMouseLeave={() => setHover(false)} > e.stopPropagation()} > - {this.renderControls()} + {renderControls()} )}
); - } + }; - renderBody() { - const { table } = this.props; + const renderBody = () => { let cols; if (table.columns) { cols = table.columns.slice(); - if (this.state.sortColumns) { + if (sortColumns) { cols.sort((a, b) => { const colA = a.name.toUpperCase(); const colB = b.name.toUpperCase(); @@ -253,11 +243,11 @@ class TableElement extends React.PureComponent { const metadata = (
this.setHover(true)} - onMouseLeave={() => this.setHover(false)} + onMouseEnter={() => setHover(true)} + onMouseLeave={() => setHover(false)} css={{ paddingTop: 6 }} > - {this.renderWell()} + {renderWell()}
{cols && cols.map(col => )} @@ -265,21 +255,19 @@ class TableElement extends React.PureComponent {
); return metadata; - } + }; - render() { - return ( - - {this.renderBody()} - - ); - } -} + return ( + + {renderBody()} + + ); +}; TableElement.propTypes = propTypes; TableElement.defaultProps = defaultProps; From 6b529ad3071693d69fa50b9771cf26543292f19f Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Tue, 25 May 2021 17:35:53 -0600 Subject: [PATCH 2/9] Replace class state checks in TableElement_spec.jsx with checks testing elements they change --- .../javascripts/sqllab/TableElement_spec.jsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx index d55720637b637..fa1bbecb4005f 100644 --- a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx +++ b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx @@ -86,13 +86,11 @@ describe('TableElement', () => { }, }, ); - expect(wrapper.find(TableElement).state().hovered).toBe(false); expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe( false, ); wrapper.find('.header-container').hostNodes().simulate('mouseEnter'); await waitForComponentToPaint(wrapper, 300); - expect(wrapper.find(TableElement).state().hovered).toBe(true); expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe( true, ); @@ -111,12 +109,22 @@ describe('TableElement', () => { }, }, ); - expect(wrapper.find(TableElement).state().sortColumns).toBe(false); + expect( + wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'), + ).toEqual(true); + expect( + wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'), + ).toEqual(false); wrapper.find('.header-container').hostNodes().simulate('click'); expect(wrapper.find(ColumnElement).first().props().column.name).toBe('id'); wrapper.find('.header-container').simulate('mouseEnter'); wrapper.find('.sort-cols').hostNodes().simulate('click'); - expect(wrapper.find(TableElement).state().sortColumns).toBe(true); + expect( + wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'), + ).toEqual(true); + expect( + wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'), + ).toEqual(false); expect(wrapper.find(ColumnElement).first().props().column.name).toBe( 'active', ); From a521903bdc648031481f6f9d11fbbbe4c446c69c Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Tue, 25 May 2021 19:22:13 -0600 Subject: [PATCH 3/9] Refactor small bit of logic to use optional chaining --- superset-frontend/src/SqlLab/components/TableElement.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 8663778734902..6530aa5c5a374 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -249,8 +249,9 @@ const TableElement = props => { > {renderWell()}
- {cols && - cols.map(col => )} + {cols?.map(col => ( + + ))}
); From 5a258ef660be9a7de0946d5ad08435b0f7bbb869 Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Wed, 26 May 2021 05:52:27 -0600 Subject: [PATCH 4/9] Add optional chaining to some logic --- superset-frontend/src/SqlLab/components/TableElement.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 6530aa5c5a374..08d46d112c0b2 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -123,7 +123,7 @@ const TableElement = props => { const renderControls = () => { let keyLink; - if (table.indexes && table.indexes.length > 0) { + if (table?.indexes.length > 0) { keyLink = ( Date: Wed, 26 May 2021 16:33:50 -0600 Subject: [PATCH 5/9] Fix IconTooltip and add IconTooltip to the collapse button --- .../javascripts/sqllab/TableElement_spec.jsx | 12 ++++---- .../src/SqlLab/components/TableElement.jsx | 30 ++++++++++++++++--- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx index fa1bbecb4005f..bed743b0bc565 100644 --- a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx +++ b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx @@ -43,7 +43,7 @@ describe('TableElement', () => { it('renders with props', () => { expect(React.isValidElement()).toBe(true); }); - it('has 4 IconTooltip elements', () => { + it('has 5 IconTooltip elements', () => { const wrapper = mount( @@ -55,7 +55,7 @@ describe('TableElement', () => { }, }, ); - expect(wrapper.find(IconTooltip)).toHaveLength(4); + expect(wrapper.find(IconTooltip)).toHaveLength(5); }); it('has 14 columns', () => { const wrapper = shallow(); @@ -110,20 +110,20 @@ describe('TableElement', () => { }, ); expect( - wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'), + wrapper.find(IconTooltip).at(2).hasClass('fa-sort-alpha-asc'), ).toEqual(true); expect( - wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'), + wrapper.find(IconTooltip).at(2).hasClass('fa-sort-numeric-asc'), ).toEqual(false); wrapper.find('.header-container').hostNodes().simulate('click'); expect(wrapper.find(ColumnElement).first().props().column.name).toBe('id'); wrapper.find('.header-container').simulate('mouseEnter'); wrapper.find('.sort-cols').hostNodes().simulate('click'); expect( - wrapper.find(IconTooltip).at(1).hasClass('fa-sort-numeric-asc'), + wrapper.find(IconTooltip).at(2).hasClass('fa-sort-numeric-asc'), ).toEqual(true); expect( - wrapper.find(IconTooltip).at(1).hasClass('fa-sort-alpha-asc'), + wrapper.find(IconTooltip).at(2).hasClass('fa-sort-alpha-asc'), ).toEqual(false); expect(wrapper.find(ColumnElement).first().props().column.name).toBe( 'active', diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 08d46d112c0b2..5113e440ccec5 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -32,6 +32,7 @@ import ColumnElement from './ColumnElement'; import ShowSQL from './ShowSQL'; import ModalTrigger from '../../components/ModalTrigger'; import Loading from '../../components/Loading'; +import { UpOutlined } from '@ant-design/icons'; const propTypes = { table: PropTypes.object, @@ -60,7 +61,7 @@ const TableElement = props => { const [sortColumns, setSortColumns] = useState(false); const [hovered, setHovered] = useState(false); - const { table, actions } = props; + const { table, actions, isActive } = props; const setHover = hovered => { debounce(() => setHovered(hovered), 100)(); @@ -161,13 +162,15 @@ const TableElement = props => { {table.selectStar && ( + } text={table.selectStar} shouldShowText={false} - tooltipText={t('Copy SELECT statement to the clipboard')} /> )} {table.view && ( @@ -258,12 +261,31 @@ const TableElement = props => { return metadata; }; + const collapseExpandIcon = () => { + return ( + + + + ); + }; + return ( {renderBody()} From 3b54694569671f4a89d79ac3a94335b4482376f2 Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Thu, 27 May 2021 11:06:59 -0600 Subject: [PATCH 6/9] Fix custom icon using IconToolTip so it better matches the original --- .../src/SqlLab/components/TableElement.jsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 5113e440ccec5..8329737651096 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -32,7 +32,7 @@ import ColumnElement from './ColumnElement'; import ShowSQL from './ShowSQL'; import ModalTrigger from '../../components/ModalTrigger'; import Loading from '../../components/Loading'; -import { UpOutlined } from '@ant-design/icons'; +import { RightOutlined } from '@ant-design/icons'; const propTypes = { table: PropTypes.object, @@ -265,15 +265,22 @@ const TableElement = props => { return ( - ); From 143bde592a6e13761ef42b9fb3c286aaad87e863 Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Fri, 28 May 2021 14:29:59 -0600 Subject: [PATCH 7/9] Update collapse/expand icon to use Icons component instead of importing from antdesign directly --- .../src/SqlLab/components/TableElement.jsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 8329737651096..fbf03d3c4d0fb 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -32,7 +32,7 @@ import ColumnElement from './ColumnElement'; import ShowSQL from './ShowSQL'; import ModalTrigger from '../../components/ModalTrigger'; import Loading from '../../components/Loading'; -import { RightOutlined } from '@ant-design/icons'; +import Icons from 'src/components/Icons'; const propTypes = { table: PropTypes.object, @@ -268,8 +268,6 @@ const TableElement = props => { position: 'fixed', right: '16px', left: 'auto', - height: '1em', - width: '1em', fontSize: '12px', transform: 'rotate(90deg)', display: 'flex', @@ -278,9 +276,9 @@ const TableElement = props => { aria-label="Collapse" tooltip={t(`${!isActive ? 'Expand' : 'Collapse'} table preview`)} > - ); From c8efeadfdb2e370091d88b6741aafcb050e34746 Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Tue, 1 Jun 2021 14:04:31 -0600 Subject: [PATCH 8/9] Fix eslint errors --- .../javascripts/sqllab/TableElement_spec.jsx | 4 +- .../src/SqlLab/components/TableElement.jsx | 120 ++++++++---------- 2 files changed, 55 insertions(+), 69 deletions(-) diff --git a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx index bed743b0bc565..8c07008815aeb 100644 --- a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx +++ b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx @@ -62,7 +62,7 @@ describe('TableElement', () => { expect(wrapper.find(ColumnElement)).toHaveLength(14); }); it('mounts', () => { - mount( + const wrapper = mount( , @@ -73,6 +73,8 @@ describe('TableElement', () => { }, }, ); + + expect(wrapper.find(TableElement)).toHaveLength(1); }); it('fades table', async () => { const wrapper = mount( diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index fbf03d3c4d0fb..28fc3bde8bdfb 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -21,18 +21,17 @@ import PropTypes from 'prop-types'; import Collapse from 'src/components/Collapse'; import Card from 'src/components/Card'; import ButtonGroup from 'src/components/ButtonGroup'; -import shortid from 'shortid'; import { t, styled } from '@superset-ui/core'; import { debounce } from 'lodash'; import { Tooltip } from 'src/components/Tooltip'; +import Icons from 'src/components/Icons'; import CopyToClipboard from '../../components/CopyToClipboard'; import { IconTooltip } from '../../components/IconTooltip'; import ColumnElement from './ColumnElement'; import ShowSQL from './ShowSQL'; import ModalTrigger from '../../components/ModalTrigger'; import Loading from '../../components/Loading'; -import Icons from 'src/components/Icons'; const propTypes = { table: PropTypes.object, @@ -67,17 +66,6 @@ const TableElement = props => { debounce(() => setHovered(hovered), 100)(); }; - const popSelectStar = () => { - const qe = { - id: shortid.generate(), - title: table.name, - dbId: table.dbId, - autorun: true, - sql: table.selectStar, - }; - actions.addQueryEditor(qe); - }; - const removeTable = () => { actions.removeDataPreview(table); actions.removeTable(table); @@ -189,41 +177,39 @@ const TableElement = props => { ); }; - const renderHeader = () => { - return ( -
setHover(true)} - onMouseLeave={() => setHover(false)} + const renderHeader = () => ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + > + - - - {table.name} - - + + {table.name} + + -
- {table.isMetadataLoading || table.isExtraMetadataLoading ? ( - - ) : ( - e.stopPropagation()} - > - {renderControls()} - - )} -
+
+ {table.isMetadataLoading || table.isExtraMetadataLoading ? ( + + ) : ( + e.stopPropagation()} + > + {renderControls()} + + )}
- ); - }; +
+ ); const renderBody = () => { let cols; @@ -261,35 +247,33 @@ const TableElement = props => { return metadata; }; - const collapseExpandIcon = () => { - return ( - - - - ); - }; + const collapseExpandIcon = () => ( + + + + ); return ( {renderBody()} From 967fe576dd4d813b32e5d307208e1b46f1a17d73 Mon Sep 17 00:00:00 2001 From: Corbin Robb Date: Thu, 3 Jun 2021 15:51:30 -0600 Subject: [PATCH 9/9] Clean up some code for readability --- superset-frontend/src/SqlLab/components/TableElement.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 28fc3bde8bdfb..384771a96e83e 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -112,7 +112,7 @@ const TableElement = props => { const renderControls = () => { let keyLink; - if (table?.indexes.length > 0) { + if (table?.indexes?.length) { keyLink = ( { {keyLink} { alignItems: 'center', }} aria-label="Collapse" - tooltip={t(`${!isActive ? 'Expand' : 'Collapse'} table preview`)} + tooltip={t(`${isActive ? 'Collapse' : 'Expand'} table preview`)} >