You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ly...@apache.org on 2022/02/11 12:56:34 UTC

[superset] branch master updated: chore: migrate QueryTable component from jsx to tsx (#17944)

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

lyndsi 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 9f678e5  chore: migrate QueryTable component from jsx to tsx (#17944)
9f678e5 is described below

commit 9f678e55b9ea253836ebb403e26909e0e82f9a79
Author: Meitong Qu <84...@users.noreply.github.com>
AuthorDate: Fri Feb 11 04:54:42 2022 -0800

    chore: migrate QueryTable component from jsx to tsx (#17944)
    
    * chore: fix misspelling of button
    
    * changed QuerySearch/index.jsx to index.tsx
    
    * updated Query type at src/SqlLab/types.ts/Query and extended it into QueryTable
    
    * updated defaultQueryLimit to be optional at ResultSetProps of superset-frontend/src/SqlLab/components/ResultSet/index.tsx
    
    * moved statusAttributes into useMemo hook so that it will not make the dependencies of useMemo Hook change on every render and statusAttributes is only used within useMomo hook
    
    * reset package-lock.json and package.json at frontend
    
    * Acquired redux rootestate type for SqlLab, to be used at useSelector(state)
    
    * updated query to be Query type and added a comment for q.status on line 178
    
    * updated queryTable for typescript conversion
    
    * updated actions type on QueryHistory and QuerySearch
    
    * updated type for actions in SouthPane
    
    * created type for SqlLab redux
    
    Co-authored-by: David Woolner <da...@gmail.com>
    Co-authored-by: AAfghahi <48...@users.noreply.github.com>
---
 .../src/SqlLab/components/QueryHistory/index.tsx   |   8 +-
 .../src/SqlLab/components/QuerySearch/index.tsx    |   5 +
 .../components/QueryTable/QueryTable.test.jsx      |   2 +
 .../components/QueryTable/{index.jsx => index.tsx} | 255 +++++++++++----------
 .../src/SqlLab/components/SouthPane/index.tsx      |   9 +-
 superset-frontend/src/SqlLab/types.ts              |  41 ++++
 superset-frontend/src/components/Icons/IconType.ts |   2 +-
 7 files changed, 202 insertions(+), 120 deletions(-)

diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
index 16816c9..e2d0453 100644
--- a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
@@ -24,7 +24,13 @@ import QueryTable from 'src/SqlLab/components/QueryTable';
 
 interface QueryHistoryProps {
   queries: Query[];
-  actions: Record<string, unknown>;
+  actions: {
+    queryEditorSetSql: Function;
+    cloneQueryToNewTab: Function;
+    fetchQueryResults: Function;
+    clearQueryResults: Function;
+    removeQuery: Function;
+  };
   displayLimit: number;
 }
 
diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx
index c5b1d47..ae25622 100644
--- a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx
@@ -37,6 +37,11 @@ interface QuerySearchProps {
   actions: {
     addDangerToast: (msg: string) => void;
     setDatabases: (data: Record<string, any>) => Record<string, any>;
+    queryEditorSetSql: Function;
+    cloneQueryToNewTab: Function;
+    fetchQueryResults: Function;
+    clearQueryResults: Function;
+    removeQuery: Function;
   };
   displayLimit: number;
 }
diff --git a/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx b/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx
index 3dc619a..5be5a38 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx
@@ -25,11 +25,13 @@ import TableView from 'src/components/TableView';
 import TableCollection from 'src/components/TableCollection';
 import { Provider } from 'react-redux';
 import { queries, user } from 'src/SqlLab/fixtures';
+import * as actions from 'src/SqlLab/actions/sqlLab';
 
 describe('QueryTable', () => {
   const mockedProps = {
     queries,
     displayLimit: 100,
+    actions,
   };
   it('is valid', () => {
     expect(React.isValidElement(<QueryTable displayLimit={100} />)).toBe(true);
diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.jsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
similarity index 57%
rename from superset-frontend/src/SqlLab/components/QueryTable/index.jsx
rename to superset-frontend/src/SqlLab/components/QueryTable/index.tsx
index 596cbe2..142d8a1 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/index.jsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
@@ -17,7 +17,6 @@
  * under the License.
  */
 import React, { useMemo } from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
 import Card from 'src/components/Card';
 import ProgressBar from 'src/components/ProgressBar';
@@ -29,133 +28,151 @@ import Button from 'src/components/Button';
 import { fDuration } from 'src/modules/dates';
 import Icons from 'src/components/Icons';
 import { Tooltip } from 'src/components/Tooltip';
+import { Query, RootState } from 'src/SqlLab/types';
+import ModalTrigger from 'src/components/ModalTrigger';
+import { UserWithPermissionsAndRoles as User } from 'src/types/bootstrapTypes';
 import ResultSet from '../ResultSet';
-import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
 
-const propTypes = {
-  columns: PropTypes.array,
-  actions: PropTypes.object,
-  queries: PropTypes.array,
-  onUserClicked: PropTypes.func,
-  onDbClicked: PropTypes.func,
-  displayLimit: PropTypes.number.isRequired,
-};
-const defaultProps = {
-  columns: ['started', 'duration', 'rows'],
-  queries: [],
-  onUserClicked: () => {},
-  onDbClicked: () => {},
-};
+interface QueryTableQuery extends Omit<Query, 'state' | 'sql' | 'progress'> {
+  state?: Record<string, any>;
+  sql?: Record<string, any>;
+  progress?: Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns?: string[];
+  actions: {
+    queryEditorSetSql: Function;
+    cloneQueryToNewTab: Function;
+    fetchQueryResults: Function;
+    clearQueryResults: Function;
+    removeQuery: Function;
+  };
+  queries?: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
 
-const openQuery = id => {
+const openQuery = (id: number) => {
   const url = `/superset/sqllab?queryId=${id}`;
   window.open(url);
 };
 
-const QueryTable = props => {
+const QueryTable = ({
+  columns = ['started', 'duration', 'rows'],
+  actions,
+  queries = [],
+  onUserClicked = () => undefined,
+  onDbClicked = () => undefined,
+  displayLimit,
+}: QueryTableProps) => {
   const theme = useTheme();
-  const statusAttributes = {
-    success: {
-      config: {
-        icon: <Icons.Check iconColor={theme.colors.success.base} />,
-        label: t('Success'),
-      },
-    },
-    failed: {
-      config: {
-        icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
-        label: t('Failed'),
-      },
-    },
-    stopped: {
-      config: {
-        icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
-        label: t('Failed'),
-      },
-    },
-    running: {
-      config: {
-        icon: <Icons.Running iconColor={theme.colors.primary.base} />,
-        label: t('Running'),
-      },
-    },
-    fetching: {
-      config: {
-        icon: <Icons.Queued iconColor={theme.colors.primary.base} />,
-        label: t('fetching'),
-      },
-    },
-    timed_out: {
-      config: {
-        icon: <Icons.Offline iconColor={theme.colors.grayscale.light1} />,
-        label: t('Offline'),
-      },
-    },
-    scheduled: {
-      config: {
-        icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
-        label: t('Scheduled'),
-      },
-    },
-    pending: {
-      config: {
-        icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
-        label: t('Scheduled'),
-      },
-    },
-    error: {
-      config: {
-        icon: <Icons.Error iconColor={theme.colors.error.base} />,
-        label: t('Unknown Status'),
-      },
-    },
-  };
 
-  const setHeaders = column => {
+  const setHeaders = (column: string) => {
     if (column === 'sql') {
       return column.toUpperCase();
     }
     return column.charAt(0).toUpperCase().concat(column.slice(1));
   };
-  const columns = useMemo(
+  const columnsOfTable = useMemo(
     () =>
-      props.columns.map(column => ({
+      columns.map(column => ({
         accessor: column,
         Header: () => setHeaders(column),
         disableSortBy: true,
       })),
-    [props.columns],
+    [columns],
   );
 
-  const user = useSelector(({ sqlLab: { user } }) => user);
+  const user = useSelector<RootState, User>(state => state.sqlLab.user);
 
-  const data = useMemo(() => {
-    const restoreSql = query => {
-      props.actions.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
-    };
+  const {
+    queryEditorSetSql,
+    cloneQueryToNewTab,
+    fetchQueryResults,
+    clearQueryResults,
+    removeQuery,
+  } = actions;
 
-    const openQueryInNewTab = query => {
-      props.actions.cloneQueryToNewTab(query, true);
+  const data = useMemo(() => {
+    const restoreSql = (query: Query) => {
+      queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
     };
 
-    const openAsyncResults = (query, displayLimit) => {
-      props.actions.fetchQueryResults(query, displayLimit);
+    const openQueryInNewTab = (query: Query) => {
+      cloneQueryToNewTab(query, true);
     };
 
-    const clearQueryResults = query => {
-      props.actions.clearQueryResults(query);
+    const openAsyncResults = (query: Query, displayLimit: number) => {
+      fetchQueryResults(query, displayLimit);
     };
 
-    const removeQuery = query => {
-      props.actions.removeQuery(query);
+    const statusAttributes = {
+      success: {
+        config: {
+          icon: <Icons.Check iconColor={theme.colors.success.base} />,
+          label: t('Success'),
+        },
+      },
+      failed: {
+        config: {
+          icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
+          label: t('Failed'),
+        },
+      },
+      stopped: {
+        config: {
+          icon: <Icons.XSmall iconColor={theme.colors.error.base} />,
+          label: t('Failed'),
+        },
+      },
+      running: {
+        config: {
+          icon: <Icons.Running iconColor={theme.colors.primary.base} />,
+          label: t('Running'),
+        },
+      },
+      fetching: {
+        config: {
+          icon: <Icons.Queued iconColor={theme.colors.primary.base} />,
+          label: t('Fetching'),
+        },
+      },
+      timed_out: {
+        config: {
+          icon: <Icons.Offline iconColor={theme.colors.grayscale.light1} />,
+          label: t('Offline'),
+        },
+      },
+      scheduled: {
+        config: {
+          icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
+          label: t('Scheduled'),
+        },
+      },
+      pending: {
+        config: {
+          icon: <Icons.Queued iconColor={theme.colors.grayscale.base} />,
+          label: t('Scheduled'),
+        },
+      },
+      error: {
+        config: {
+          icon: <Icons.Error iconColor={theme.colors.error.base} />,
+          label: t('Unknown Status'),
+        },
+      },
     };
 
-    return props.queries
+    return queries
       .map(query => {
-        const q = { ...query };
-        const status = statusAttributes[q.state] || statusAttributes.error;
+        const { state, sql, progress, ...rest } = query;
+        const q = rest as QueryTableQuery;
+
+        const status = statusAttributes[state] || statusAttributes.error;
 
         if (q.endDttm) {
           q.duration = fDuration(q.startDttm, q.endDttm);
@@ -172,7 +189,7 @@ const QueryTable = props => {
           <Button
             buttonSize="small"
             buttonStyle="link"
-            onClick={() => props.onUserClicked(q.userId)}
+            onClick={() => onUserClicked(q.userId)}
           >
             {q.user}
           </Button>
@@ -181,7 +198,7 @@ const QueryTable = props => {
           <Button
             buttonSize="small"
             buttonStyle="link"
-            onClick={() => props.onDbClicked(q.dbId)}
+            onClick={() => onDbClicked(q.dbId)}
           >
             {q.db}
           </Button>
@@ -200,7 +217,7 @@ const QueryTable = props => {
         q.sql = (
           <Card css={[StaticPosition]}>
             <HighlightedSql
-              sql={q.sql}
+              sql={sql}
               rawSql={q.executedSql}
               shrink
               maxWidth={60}
@@ -217,16 +234,17 @@ const QueryTable = props => {
                 </Label>
               }
               modalTitle={t('Data preview')}
-              beforeOpen={() => openAsyncResults(query, props.displayLimit)}
+              beforeOpen={() => openAsyncResults(query, displayLimit)}
               onExit={() => clearQueryResults(query)}
               modalBody={
                 <ResultSet
                   showSql
                   user={user}
                   query={query}
-                  actions={props.actions}
+                  actions={actions}
                   height={400}
-                  displayLimit={props.displayLimit}
+                  displayLimit={displayLimit}
+                  defaultQueryLimit={1000}
                 />
               }
               responsive
@@ -240,17 +258,14 @@ const QueryTable = props => {
           q.output = [schemaUsed, q.tempTable].filter(v => v).join('.');
         }
         q.progress =
-          q.state === 'success' ? (
+          state === 'success' ? (
             <ProgressBar
-              percent={parseInt(q.progress.toFixed(0), 10)}
+              percent={parseInt(progress.toFixed(0), 10)}
               striped
               showInfo={false}
             />
           ) : (
-            <ProgressBar
-              percent={parseInt(q.progress.toFixed(0), 10)}
-              striped
-            />
+            <ProgressBar percent={parseInt(progress.toFixed(0), 10)} striped />
           );
         q.state = (
           <Tooltip title={status.config.label} placement="bottom">
@@ -266,35 +281,44 @@ const QueryTable = props => {
               )}
               placement="top"
             >
-              <Icons.Edit iconSize="small" />
+              <Icons.Edit iconSize="s" />
             </StyledTooltip>
             <StyledTooltip
               onClick={() => openQueryInNewTab(query)}
               tooltip={t('Run query in a new tab')}
               placement="top"
             >
-              <Icons.PlusCircleOutlined
-                iconSize="x-small"
-                css={verticalAlign}
-              />
+              <Icons.PlusCircleOutlined iconSize="xs" css={verticalAlign} />
             </StyledTooltip>
             <StyledTooltip
               tooltip={t('Remove query from log')}
               onClick={() => removeQuery(query)}
             >
-              <Icons.Trash iconSize="x-small" />
+              <Icons.Trash iconSize="xs" />
             </StyledTooltip>
           </div>
         );
         return q;
       })
       .reverse();
-  }, [props]);
+  }, [
+    queries,
+    onUserClicked,
+    onDbClicked,
+    user,
+    displayLimit,
+    actions,
+    clearQueryResults,
+    cloneQueryToNewTab,
+    fetchQueryResults,
+    queryEditorSetSql,
+    removeQuery,
+  ]);
 
   return (
     <div className="QueryTable">
       <TableView
-        columns={columns}
+        columns={columnsOfTable}
         data={data}
         className="table-condensed"
         pageSize={50}
@@ -303,7 +327,4 @@ const QueryTable = props => {
   );
 };
 
-QueryTable.propTypes = propTypes;
-QueryTable.defaultProps = defaultProps;
-
 export default QueryTable;
diff --git a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
index 80f9e2d..f7efc04 100644
--- a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
@@ -44,7 +44,14 @@ interface SouthPanePropTypes {
   editorQueries: any[];
   latestQueryId?: string;
   dataPreviewQueries: any[];
-  actions: Record<string, Function>;
+  actions: {
+    queryEditorSetSql: Function;
+    cloneQueryToNewTab: Function;
+    fetchQueryResults: Function;
+    clearQueryResults: Function;
+    removeQuery: Function;
+    setActiveSouthPaneTab: Function;
+  };
   activeSouthPaneTab?: string;
   height: number;
   databases: Record<string, any>;
diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts
index 1308f58..27f08f5 100644
--- a/superset-frontend/src/SqlLab/types.ts
+++ b/superset-frontend/src/SqlLab/types.ts
@@ -18,6 +18,8 @@
  */
 import { SupersetError } from 'src/components/ErrorMessage/types';
 import { CtasEnum } from 'src/SqlLab/actions/sqlLab';
+import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
+import { ToastType } from 'src/components/MessageToasts/types';
 
 export type Column = {
   name: string;
@@ -68,6 +70,19 @@ export type Query = {
   rows: number;
   queryLimit: number;
   limitingFactor: string;
+  endDttm: number;
+  duration: string;
+  startDttm: number;
+  time: Record<string, any>;
+  user: Record<string, any>;
+  userId: number;
+  db: Record<string, any>;
+  started: string;
+  querylink: Record<string, any>;
+  queryId: number;
+  executedSql: string;
+  output: string | Record<string, any>;
+  actions: Record<string, any>;
 };
 
 export interface QueryEditor {
@@ -82,3 +97,29 @@ export interface QueryEditor {
     errors: SupersetError[];
   };
 }
+
+export type toastState = {
+  id: string;
+  toastType: ToastType;
+  text: string;
+  duration: number;
+  noDuplicate: boolean;
+};
+
+export type RootState = {
+  sqlLab: {
+    activeSouthPaneTab: string | number; // default is string; action.newQuery.id is number
+    alerts: any[];
+    databases: Record<string, any>;
+    offline: boolean;
+    queries: Query[];
+    queryEditors: QueryEditor[];
+    tabHistory: string[]; // default is activeTab ? [activeTab.id.toString()] : []
+    tables: Record<string, any>[];
+    queriesLastUpdate: number;
+    user: UserWithPermissionsAndRoles;
+  };
+  localStorageUsageInKilobytes: number;
+  messageToasts: toastState[];
+  common: {};
+};
diff --git a/superset-frontend/src/components/Icons/IconType.ts b/superset-frontend/src/components/Icons/IconType.ts
index 4687af6..7371007 100644
--- a/superset-frontend/src/components/Icons/IconType.ts
+++ b/superset-frontend/src/components/Icons/IconType.ts
@@ -22,7 +22,7 @@ type AntdIconType = IconComponentProps;
 type IconType = AntdIconType & {
   iconColor?: string;
   twoToneColor?: string;
-  iconSize?: 's' | 'm' | 'l' | 'xl' | 'xxl';
+  iconSize?: 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
 };
 
 export default IconType;