eschutho commented on a change in pull request #17944:
URL: https://github.com/apache/superset/pull/17944#discussion_r780599289



##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';

Review comment:
       this is a carry over from the old file, but while you're here, do you 
mind changing this import to be 'src/components/ModalTrigger' (absolute instead 
of relative) since it's going up more than one level? Thanks!

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';

Review comment:
       same thing here with `src/SqlLab/reducers' (don't need the index.

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';

Review comment:
       same thing here with `src/SqlLab/reducers' (don't need the index).

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';

Review comment:
       same thing - absolute import- here with `src/SqlLab/reducers' (don't 
need the index).

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
+
+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 = Infinity,
+}: 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((state: RootState) => state.sqlLab.user);

Review comment:
       can we instead use the generics for useSelector, which are the RootState 
`useSelector<RootState, User>`and then the type for the return value, in this 
case, user. Also for the RootState, do you mind create a type file for SqlLab 
like we have for dashboards? 
https://github.com/apache/superset/blob/master/superset-frontend/src/dashboard/types.ts#L96

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;

Review comment:
       to be consistent, you can write this like `string[]`

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];

Review comment:
       since there's a default value below, it looks like this will be an 
optional property.

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;

Review comment:
       Also it has a default value in the function parameters, so the type can 
be optional.

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
+
+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 = Infinity,
+}: 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((state: RootState) => state.sqlLab.user);
 
   const data = useMemo(() => {
-    const restoreSql = query => {
-      props.actions.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    const restoreSql = (query: Query) => {
+      actions?.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    };
+
+    const openQueryInNewTab = (query: Query) => {
+      actions?.cloneQueryToNewTab(query, true);
     };
 
-    const openQueryInNewTab = query => {
-      props.actions.cloneQueryToNewTab(query, true);
+    const openAsyncResults = (query: Query, displayLimit: number) => {
+      actions?.fetchQueryResults(query, displayLimit);
     };
 
-    const openAsyncResults = (query, displayLimit) => {
-      props.actions.fetchQueryResults(query, displayLimit);
+    const clearQueryResults = (query: Query) => {
+      actions?.clearQueryResults(query);
     };
 
-    const clearQueryResults = query => {
-      props.actions.clearQueryResults(query);
+    const removeQuery = (query: Query) => {
+      actions?.removeQuery(query);

Review comment:
       these 5 actions are all required, so to be sure that they are imported 
correctly, I'd suggest destructuring them in the function parameters. 

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
+
+const openQuery = (id: number) => {
   const url = `/superset/sqllab?queryId=${id}`;
   window.open(url);
 };
 
-const QueryTable = props => {
+const QueryTable = ({
+  columns = ['started', 'duration', 'rows'],
+  actions,

Review comment:
       I'd suggest destructuring these (see note below)

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;

Review comment:
       since you know exactly which ones you want, you can prob make a type for 
this. 

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,156 @@ 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 } from 'src/SqlLab/types';
 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: () => {},
-};
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
+
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
+
+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 = Infinity,
+}: 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((state: any) => state.sqlLab.user);
 
   const data = useMemo(() => {
-    const restoreSql = query => {
-      props.actions.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    const restoreSql = (query: Record<string, any>) => {
+      actions?.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    };
+
+    const openQueryInNewTab = (query: Record<string, any>) => {
+      actions?.cloneQueryToNewTab(query, true);
     };
 
-    const openQueryInNewTab = query => {
-      props.actions.cloneQueryToNewTab(query, true);
+    const openAsyncResults = (
+      query: Record<string, any>,
+      displayLimit: number,
+    ) => {
+      actions?.fetchQueryResults(query, displayLimit);
     };
 
-    const openAsyncResults = (query, displayLimit) => {
-      props.actions.fetchQueryResults(query, displayLimit);
+    const clearQueryResults = (query: Record<string, any>) => {
+      actions?.clearQueryResults(query);
     };
 
-    const clearQueryResults = query => {
-      props.actions.clearQueryResults(query);
+    const removeQuery = (query: Record<string, any>) => {
+      actions?.removeQuery(query);
     };
 
-    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;
+        // query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+        const q: QueryTableQuery = { ...query };
+        let status: any;

Review comment:
       I think this may be easier if you use query as the original value and q 
as the new object that you are creating. So you'll have `query: Query` and `q: 
QueryTableValue`. `query.state` is a string and q.state is an object. 
   
   and only bring in the properties that you don't overwrite, like:
   const { state, ...q } = query;
   
   Then you can do `status = statusAttributes[query.state] || 
statusAttributes.error;`
   later you can assign q.state to an object. 

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,156 @@ 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 } from 'src/SqlLab/types';
 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: () => {},
-};
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
+
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
+
+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 = Infinity,
+}: 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((state: any) => state.sqlLab.user);
 
   const data = useMemo(() => {
-    const restoreSql = query => {
-      props.actions.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    const restoreSql = (query: Record<string, any>) => {
+      actions?.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    };
+
+    const openQueryInNewTab = (query: Record<string, any>) => {
+      actions?.cloneQueryToNewTab(query, true);
     };
 
-    const openQueryInNewTab = query => {
-      props.actions.cloneQueryToNewTab(query, true);
+    const openAsyncResults = (
+      query: Record<string, any>,
+      displayLimit: number,
+    ) => {
+      actions?.fetchQueryResults(query, displayLimit);
     };
 
-    const openAsyncResults = (query, displayLimit) => {
-      props.actions.fetchQueryResults(query, displayLimit);
+    const clearQueryResults = (query: Record<string, any>) => {
+      actions?.clearQueryResults(query);
     };
 
-    const clearQueryResults = query => {
-      props.actions.clearQueryResults(query);
+    const removeQuery = (query: Record<string, any>) => {
+      actions?.removeQuery(query);
     };
 
-    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;
+        // query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+        const q: QueryTableQuery = { ...query };
+        let status: any;

Review comment:
       I think this may be easier if you use query as the original value and q 
as the new object that you are creating. So you'll have `query: Query` and `q: 
QueryTableValue`. `query.state` is a string and q.state is an object. 
   
   and only bring in the properties that you don't overwrite, like:
   const { state, sql, ...q } = query;
   
   Then you can do `status = statusAttributes[query.state] || 
statusAttributes.error;`
   later you can assign q.state to an object. 

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -266,35 +290,32 @@ 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="s" css={verticalAlign} />

Review comment:
       `xs` looks to be a valid size in the typography.sizes. I think it would 
be safe to add `xs` to the list here: 
superset-frontend/src/components/Icons/IconType.ts

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -266,35 +290,32 @@ 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="s" css={verticalAlign} />
             </StyledTooltip>
             <StyledTooltip
               tooltip={t('Remove query from log')}
               onClick={() => removeQuery(query)}
             >
-              <Icons.Trash iconSize="x-small" />
+              <Icons.Trash iconSize="s" />

Review comment:
       same here for xs

##########
File path: superset-frontend/src/SqlLab/components/QueryTable/index.tsx
##########
@@ -29,133 +28,158 @@ 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 } from 'src/SqlLab/types';
 import ResultSet from '../ResultSet';
 import ModalTrigger from '../../../components/ModalTrigger';
 import HighlightedSql from '../HighlightedSql';
 import { StaticPosition, verticalAlign, StyledTooltip } from './styles';
+import rootReducer from '../../reducers/index';
 
-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: () => {},
-};
+// Acquire redux rootstate type, to be used in useSelector
+type RootState = ReturnType<typeof rootReducer>;
+
+// query's type is original Query; Shallow-copy of query, q's type is 
QueryTableQuery. So that prop, sql passed to another component will remain 
string, the type of original Query
+interface QueryTableQueryTemp1 extends Omit<Query, 'sql'> {
+  sql: string | Record<string, any>;
+}
+
+interface QueryTableQueryTemp2 extends Omit<QueryTableQueryTemp1, 'progress'> {
+  progress: number | Record<string, any>;
+}
 
-const openQuery = id => {
+interface QueryTableQuery extends Omit<QueryTableQueryTemp2, 'state'> {
+  state: string | Record<string, any>;
+}
+
+interface QueryTableProps {
+  columns: Array<string>;
+  actions: Record<string, any>;
+  queries: Query[];
+  onUserClicked?: Function;
+  onDbClicked?: Function;
+  displayLimit: number;
+}
+
+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 = Infinity,
+}: 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((state: RootState) => state.sqlLab.user);
 
   const data = useMemo(() => {
-    const restoreSql = query => {
-      props.actions.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    const restoreSql = (query: Query) => {
+      actions?.queryEditorSetSql({ id: query.sqlEditorId }, query.sql);
+    };
+
+    const openQueryInNewTab = (query: Query) => {
+      actions?.cloneQueryToNewTab(query, true);
     };
 
-    const openQueryInNewTab = query => {
-      props.actions.cloneQueryToNewTab(query, true);
+    const openAsyncResults = (query: Query, displayLimit: number) => {
+      actions?.fetchQueryResults(query, displayLimit);
     };
 
-    const openAsyncResults = (query, displayLimit) => {
-      props.actions.fetchQueryResults(query, displayLimit);
+    const clearQueryResults = (query: Query) => {
+      actions?.clearQueryResults(query);
     };
 
-    const clearQueryResults = query => {
-      props.actions.clearQueryResults(query);
+    const removeQuery = (query: Query) => {
+      actions?.removeQuery(query);
     };
 
-    const removeQuery = query => {
-      props.actions.removeQuery(query);
+    const statusAttributes = {
+      success: {
+        config: {
+          icon: <Icons.Check iconColor={theme.colors.success.base} />,

Review comment:
       the `theme` dependency won't change for this workspace at all, so you 
don't need to worry about memoizing for this dependency. 




-- 
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-unsubscr...@superset.apache.org

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



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

Reply via email to