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

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


The following commit(s) were added to refs/heads/master by this push:
     new 004a6d9  refactor: Convert TableElement.jsx component from class to 
functional with hooks (#14830)
004a6d9 is described below

commit 004a6d9e54fe55cd497c1b5c74a097c467e4caba
Author: Corbin Robb <[email protected]>
AuthorDate: Thu Jun 3 17:04:56 2021 -0600

    refactor: Convert TableElement.jsx component from class to functional with 
hooks (#14830)
    
    * Change TableElement from a class component to a functional component
    
    * Replace class state checks in TableElement_spec.jsx with checks testing 
elements they change
    
    * Refactor small bit of logic to use optional chaining
    
    * Add optional chaining to some logic
    
    * Fix IconTooltip and add IconTooltip to the collapse button
    
    * Fix custom icon using IconToolTip so it better matches the original
    
    * Update collapse/expand icon to use Icons component instead of importing 
from antdesign directly
    
    * Fix eslint errors
    
    * Clean up some code for readability
    
    Co-authored-by: Corbin Robb <[email protected]>
---
 .../spec/javascripts/sqllab/TableElement_spec.jsx  |  24 ++-
 .../src/SqlLab/components/TableElement.jsx         | 202 ++++++++++-----------
 2 files changed, 118 insertions(+), 108 deletions(-)

diff --git a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx 
b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx
index d557206..8c07008 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(<TableElement {...mockedProps} />)).toBe(true);
   });
-  it('has 4 IconTooltip elements', () => {
+  it('has 5 IconTooltip elements', () => {
     const wrapper = mount(
       <Provider store={store}>
         <TableElement {...mockedProps} />
@@ -55,14 +55,14 @@ describe('TableElement', () => {
         },
       },
     );
-    expect(wrapper.find(IconTooltip)).toHaveLength(4);
+    expect(wrapper.find(IconTooltip)).toHaveLength(5);
   });
   it('has 14 columns', () => {
     const wrapper = shallow(<TableElement {...mockedProps} />);
     expect(wrapper.find(ColumnElement)).toHaveLength(14);
   });
   it('mounts', () => {
-    mount(
+    const wrapper = mount(
       <Provider store={store}>
         <TableElement {...mockedProps} />
       </Provider>,
@@ -73,6 +73,8 @@ describe('TableElement', () => {
         },
       },
     );
+
+    expect(wrapper.find(TableElement)).toHaveLength(1);
   });
   it('fades table', async () => {
     const wrapper = mount(
@@ -86,13 +88,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 +111,22 @@ describe('TableElement', () => {
         },
       },
     );
-    expect(wrapper.find(TableElement).state().sortColumns).toBe(false);
+    expect(
+      wrapper.find(IconTooltip).at(2).hasClass('fa-sort-alpha-asc'),
+    ).toEqual(true);
+    expect(
+      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(TableElement).state().sortColumns).toBe(true);
+    expect(
+      wrapper.find(IconTooltip).at(2).hasClass('fa-sort-numeric-asc'),
+    ).toEqual(true);
+    expect(
+      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 e44cccf..384771a 100644
--- a/superset-frontend/src/SqlLab/components/TableElement.jsx
+++ b/superset-frontend/src/SqlLab/components/TableElement.jsx
@@ -16,16 +16,16 @@
  * 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';
 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';
@@ -56,44 +56,26 @@ 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, isActive } = props;
 
-  popSelectStar() {
-    const qe = {
-      id: shortid.generate(),
-      title: this.props.table.name,
-      dbId: this.props.table.dbId,
-      autorun: true,
-      sql: this.props.table.selectStar,
-    };
-    this.props.actions.addQueryEditor(qe);
-  }
+  const setHover = hovered => {
+    debounce(() => setHovered(hovered), 100)();
+  };
 
-  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,12 +108,11 @@ class TableElement extends React.PureComponent {
       );
     }
     return header;
-  }
+  };
 
-  renderControls() {
+  const renderControls = () => {
     let keyLink;
-    const { table } = this.props;
-    if (table.indexes && table.indexes.length > 0) {
+    if (table?.indexes?.length) {
       keyLink = (
         <ModalTrigger
           modalTitle={
@@ -156,12 +137,12 @@ class TableElement extends React.PureComponent {
         {keyLink}
         <IconTooltip
           className={
-            `fa fa-sort-${!this.state.sortColumns ? 'alpha' : 'numeric'}-asc ` 
+
+            `fa fa-sort-${sortColumns ? 'numeric' : 'alpha'}-asc ` +
             'pull-left sort-cols m-l-2 pointer'
           }
-          onClick={this.toggleSortColumns}
+          onClick={toggleSortColumns}
           tooltip={
-            !this.state.sortColumns
+            !sortColumns
               ? t('Sort columns alphabetically')
               : t('Original table column order')
           }
@@ -169,13 +150,15 @@ class TableElement extends React.PureComponent {
         {table.selectStar && (
           <CopyToClipboard
             copyNode={
-              <IconTooltip aria-label="Copy">
+              <IconTooltip
+                aria-label="Copy"
+                tooltip={t('Copy SELECT statement to the clipboard')}
+              >
                 <i aria-hidden className="fa fa-clipboard pull-left m-l-2" />
               </IconTooltip>
             }
             text={table.selectStar}
             shouldShowText={false}
-            tooltipText={t('Copy SELECT statement to the clipboard')}
           />
         )}
         {table.view && (
@@ -187,56 +170,52 @@ class TableElement extends React.PureComponent {
         )}
         <IconTooltip
           className="fa fa-times table-remove pull-left m-l-2 pointer"
-          onClick={this.removeTable}
+          onClick={removeTable}
           tooltip={t('Remove table preview')}
         />
       </ButtonGroup>
     );
-  }
+  };
 
-  renderHeader() {
-    const { table } = this.props;
-    return (
-      <div
-        className="clearfix header-container"
-        onMouseEnter={() => this.setHover(true)}
-        onMouseLeave={() => this.setHover(false)}
+  const renderHeader = () => (
+    <div
+      className="clearfix header-container"
+      onMouseEnter={() => setHover(true)}
+      onMouseLeave={() => setHover(false)}
+    >
+      <Tooltip
+        id="copy-to-clipboard-tooltip"
+        placement="topLeft"
+        style={{ cursor: 'pointer' }}
+        title={table.name}
+        trigger={['hover']}
       >
-        <Tooltip
-          id="copy-to-clipboard-tooltip"
-          placement="topLeft"
-          style={{ cursor: 'pointer' }}
-          title={table.name}
-          trigger={['hover']}
-        >
-          <StyledSpan data-test="collapse" className="table-name">
-            <strong>{table.name}</strong>
-          </StyledSpan>
-        </Tooltip>
+        <StyledSpan data-test="collapse" className="table-name">
+          <strong>{table.name}</strong>
+        </StyledSpan>
+      </Tooltip>
 
-        <div className="pull-right header-right-side">
-          {table.isMetadataLoading || table.isExtraMetadataLoading ? (
-            <Loading position="inline" />
-          ) : (
-            <Fade
-              data-test="fade"
-              hovered={this.state.hovered}
-              onClick={e => e.stopPropagation()}
-            >
-              {this.renderControls()}
-            </Fade>
-          )}
-        </div>
+      <div className="pull-right header-right-side">
+        {table.isMetadataLoading || table.isExtraMetadataLoading ? (
+          <Loading position="inline" />
+        ) : (
+          <Fade
+            data-test="fade"
+            hovered={hovered}
+            onClick={e => e.stopPropagation()}
+          >
+            {renderControls()}
+          </Fade>
+        )}
       </div>
-    );
-  }
+    </div>
+  );
 
-  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,33 +232,54 @@ class TableElement extends React.PureComponent {
 
     const metadata = (
       <div
-        onMouseEnter={() => this.setHover(true)}
-        onMouseLeave={() => this.setHover(false)}
+        onMouseEnter={() => setHover(true)}
+        onMouseLeave={() => setHover(false)}
         css={{ paddingTop: 6 }}
       >
-        {this.renderWell()}
+        {renderWell()}
         <div>
-          {cols &&
-            cols.map(col => <ColumnElement column={col} key={col.name} />)}
+          {cols?.map(col => (
+            <ColumnElement column={col} key={col.name} />
+          ))}
         </div>
       </div>
     );
     return metadata;
-  }
+  };
 
-  render() {
-    return (
-      <Collapse.Panel
-        {...this.props}
-        header={this.renderHeader()}
-        className="TableElement"
-        forceRender="true"
-      >
-        {this.renderBody()}
-      </Collapse.Panel>
-    );
-  }
-}
+  const collapseExpandIcon = () => (
+    <IconTooltip
+      style={{
+        position: 'fixed',
+        right: '16px',
+        left: 'auto',
+        fontSize: '12px',
+        transform: 'rotate(90deg)',
+        display: 'flex',
+        alignItems: 'center',
+      }}
+      aria-label="Collapse"
+      tooltip={t(`${isActive ? 'Collapse' : 'Expand'} table preview`)}
+    >
+      <Icons.RightOutlined
+        iconSize="s"
+        style={isActive ? { transform: 'rotateY(180deg)' } : null}
+      />
+    </IconTooltip>
+  );
+
+  return (
+    <Collapse.Panel
+      {...props}
+      header={renderHeader()}
+      className="TableElement"
+      forceRender
+      expandIcon={collapseExpandIcon}
+    >
+      {renderBody()}
+    </Collapse.Panel>
+  );
+};
 
 TableElement.propTypes = propTypes;
 TableElement.defaultProps = defaultProps;

Reply via email to