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;