chore: Remove actions prop and refactor code in SQL Lab (#22231)

This commit is contained in:
EugeneTorap 2022-12-05 14:12:52 +03:00 committed by GitHub
parent b2d909f529
commit f3bf3ec2ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 127 additions and 247 deletions

View File

@ -296,6 +296,7 @@ export type Query = {
errorMessage: string | null; errorMessage: string | null;
extra: { extra: {
progress: string | null; progress: string | null;
errors?: SupersetError[];
}; };
id: string; id: string;
isDataPreview: boolean; isDataPreview: boolean;

View File

@ -20,16 +20,8 @@ import React from 'react';
import { render, screen } from 'spec/helpers/testing-library'; import { render, screen } from 'spec/helpers/testing-library';
import QueryHistory from 'src/SqlLab/components/QueryHistory'; import QueryHistory from 'src/SqlLab/components/QueryHistory';
const NOOP = () => {};
const mockedProps = { const mockedProps = {
queries: [], queries: [],
actions: {
queryEditorSetAndSaveSql: NOOP,
cloneQueryToNewTab: NOOP,
fetchQueryResults: NOOP,
clearQueryResults: NOOP,
removeQuery: NOOP,
},
displayLimit: 1000, displayLimit: 1000,
latestQueryId: 'yhMUZCGb', latestQueryId: 'yhMUZCGb',
}; };

View File

@ -23,13 +23,6 @@ import QueryTable from 'src/SqlLab/components/QueryTable';
interface QueryHistoryProps { interface QueryHistoryProps {
queries: QueryResponse[]; queries: QueryResponse[];
actions: {
queryEditorSetAndSaveSql: Function;
cloneQueryToNewTab: Function;
fetchQueryResults: Function;
clearQueryResults: Function;
removeQuery: Function;
};
displayLimit: number; displayLimit: number;
latestQueryId: string | undefined; latestQueryId: string | undefined;
} }
@ -47,7 +40,6 @@ const StyledEmptyStateWrapper = styled.div`
const QueryHistory = ({ const QueryHistory = ({
queries, queries,
actions,
displayLimit, displayLimit,
latestQueryId, latestQueryId,
}: QueryHistoryProps) => }: QueryHistoryProps) =>
@ -64,7 +56,6 @@ const QueryHistory = ({
'actions', 'actions',
]} ]}
queries={queries} queries={queries}
actions={actions}
displayLimit={displayLimit} displayLimit={displayLimit}
latestQueryId={latestQueryId} latestQueryId={latestQueryId}
/> />

View File

@ -43,7 +43,6 @@ fetchMock.get(DATABASE_ENDPOINT, []);
describe('QuerySearch', () => { describe('QuerySearch', () => {
const mockedProps = { const mockedProps = {
actions: { addDangerToast: jest.fn() },
displayLimit: 50, displayLimit: 50,
}; };

View File

@ -17,6 +17,9 @@
* under the License. * under the License.
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { setDatabases, addDangerToast } from 'src/SqlLab/actions/sqlLab';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import Select from 'src/components/DeprecatedSelect'; import Select from 'src/components/DeprecatedSelect';
import { styled, t, SupersetClient, QueryResponse } from '@superset-ui/core'; import { styled, t, SupersetClient, QueryResponse } from '@superset-ui/core';
@ -33,15 +36,6 @@ import { STATUS_OPTIONS, TIME_OPTIONS } from 'src/SqlLab/constants';
import QueryTable from '../QueryTable'; import QueryTable from '../QueryTable';
interface QuerySearchProps { interface QuerySearchProps {
actions: {
addDangerToast: (msg: string) => void;
setDatabases: (data: Record<string, any>) => Record<string, any>;
queryEditorSetAndSaveSql: Function;
cloneQueryToNewTab: Function;
fetchQueryResults: Function;
clearQueryResults: Function;
removeQuery: Function;
};
displayLimit: number; displayLimit: number;
} }
@ -77,7 +71,10 @@ const TableStyles = styled.div`
const StyledTableStylesContainer = styled.div` const StyledTableStylesContainer = styled.div`
overflow: auto; overflow: auto;
`; `;
function QuerySearch({ actions, displayLimit }: QuerySearchProps) {
const QuerySearch = ({ displayLimit }: QuerySearchProps) => {
const dispatch = useDispatch();
const [databaseId, setDatabaseId] = useState<string>(''); const [databaseId, setDatabaseId] = useState<string>('');
const [userId, setUserId] = useState<string>(''); const [userId, setUserId] = useState<string>('');
const [searchText, setSearchText] = useState<string>(''); const [searchText, setSearchText] = useState<string>('');
@ -133,7 +130,7 @@ function QuerySearch({ actions, displayLimit }: QuerySearchProps) {
const queries = Object.values(response.json); const queries = Object.values(response.json);
setQueriesArray(queries); setQueriesArray(queries);
} catch (err) { } catch (err) {
actions.addDangerToast(t('An error occurred when refreshing queries')); dispatch(addDangerToast(t('An error occurred when refreshing queries')));
} finally { } finally {
setQueriesLoading(false); setQueriesLoading(false);
} }
@ -178,10 +175,10 @@ function QuerySearch({ actions, displayLimit }: QuerySearchProps) {
value: id, value: id,
label: database_name, label: database_name,
})); }));
actions.setDatabases(result); dispatch(setDatabases(result));
if (result.length === 0) { if (result.length === 0) {
actions.addDangerToast( dispatch(
t("It seems you don't have access to any database"), addDangerToast(t("It seems you don't have access to any database")),
); );
} }
return options; return options;
@ -280,7 +277,6 @@ function QuerySearch({ actions, displayLimit }: QuerySearchProps) {
onUserClicked={onUserClicked} onUserClicked={onUserClicked}
onDbClicked={onDbClicked} onDbClicked={onDbClicked}
queries={queriesArray} queries={queriesArray}
actions={actions}
displayLimit={displayLimit} displayLimit={displayLimit}
/> />
</TableStyles> </TableStyles>
@ -288,5 +284,6 @@ function QuerySearch({ actions, displayLimit }: QuerySearchProps) {
</StyledTableStylesContainer> </StyledTableStylesContainer>
</TableWrapper> </TableWrapper>
); );
} };
export default QuerySearch; export default QuerySearch;

View File

@ -25,13 +25,11 @@ import TableView from 'src/components/TableView';
import TableCollection from 'src/components/TableCollection'; import TableCollection from 'src/components/TableCollection';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { queries, user } from 'src/SqlLab/fixtures'; import { queries, user } from 'src/SqlLab/fixtures';
import * as actions from 'src/SqlLab/actions/sqlLab';
describe('QueryTable', () => { describe('QueryTable', () => {
const mockedProps = { const mockedProps = {
queries, queries,
displayLimit: 100, displayLimit: 100,
actions,
latestQueryId: 'ryhMUZCGb', latestQueryId: 'ryhMUZCGb',
}; };
it('is valid', () => { it('is valid', () => {

View File

@ -22,7 +22,15 @@ import Card from 'src/components/Card';
import ProgressBar from 'src/components/ProgressBar'; import ProgressBar from 'src/components/ProgressBar';
import Label from 'src/components/Label'; import Label from 'src/components/Label';
import { t, useTheme, QueryResponse } from '@superset-ui/core'; import { t, useTheme, QueryResponse } from '@superset-ui/core';
import { useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import {
queryEditorSetAndSaveSql,
cloneQueryToNewTab,
fetchQueryResults,
clearQueryResults,
removeQuery,
} from 'src/SqlLab/actions/sqlLab';
import TableView from 'src/components/TableView'; import TableView from 'src/components/TableView';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { fDuration } from 'src/utils/dates'; import { fDuration } from 'src/utils/dates';
@ -45,13 +53,6 @@ interface QueryTableQuery
interface QueryTableProps { interface QueryTableProps {
columns?: string[]; columns?: string[];
actions: {
queryEditorSetAndSaveSql: Function;
cloneQueryToNewTab: Function;
fetchQueryResults: Function;
clearQueryResults: Function;
removeQuery: Function;
};
queries?: QueryResponse[]; queries?: QueryResponse[];
onUserClicked?: Function; onUserClicked?: Function;
onDbClicked?: Function; onDbClicked?: Function;
@ -66,7 +67,6 @@ const openQuery = (id: number) => {
const QueryTable = ({ const QueryTable = ({
columns = ['started', 'duration', 'rows'], columns = ['started', 'duration', 'rows'],
actions,
queries = [], queries = [],
onUserClicked = () => undefined, onUserClicked = () => undefined,
onDbClicked = () => undefined, onDbClicked = () => undefined,
@ -74,6 +74,7 @@ const QueryTable = ({
latestQueryId, latestQueryId,
}: QueryTableProps) => { }: QueryTableProps) => {
const theme = useTheme(); const theme = useTheme();
const dispatch = useDispatch();
const setHeaders = (column: string) => { const setHeaders = (column: string) => {
if (column === 'sql') { if (column === 'sql') {
@ -93,25 +94,17 @@ const QueryTable = ({
const user = useSelector<SqlLabRootState, User>(state => state.sqlLab.user); const user = useSelector<SqlLabRootState, User>(state => state.sqlLab.user);
const {
queryEditorSetAndSaveSql,
cloneQueryToNewTab,
fetchQueryResults,
clearQueryResults,
removeQuery,
} = actions;
const data = useMemo(() => { const data = useMemo(() => {
const restoreSql = (query: QueryResponse) => { const restoreSql = (query: QueryResponse) => {
queryEditorSetAndSaveSql({ id: query.sqlEditorId }, query.sql); dispatch(queryEditorSetAndSaveSql({ id: query.sqlEditorId }, query.sql));
}; };
const openQueryInNewTab = (query: QueryResponse) => { const openQueryInNewTab = (query: QueryResponse) => {
cloneQueryToNewTab(query, true); dispatch(cloneQueryToNewTab(query, true));
}; };
const openAsyncResults = (query: QueryResponse, displayLimit: number) => { const openAsyncResults = (query: QueryResponse, displayLimit: number) => {
fetchQueryResults(query, displayLimit); dispatch(fetchQueryResults(query, displayLimit));
}; };
const statusAttributes = { const statusAttributes = {
@ -239,7 +232,7 @@ const QueryTable = ({
} }
modalTitle={t('Data preview')} modalTitle={t('Data preview')}
beforeOpen={() => openAsyncResults(query, displayLimit)} beforeOpen={() => openAsyncResults(query, displayLimit)}
onExit={() => clearQueryResults(query)} onExit={() => dispatch(clearQueryResults(query))}
modalBody={ modalBody={
<ResultSet <ResultSet
showSql showSql
@ -293,7 +286,7 @@ const QueryTable = ({
{q.id !== latestQueryId && ( {q.id !== latestQueryId && (
<StyledTooltip <StyledTooltip
tooltip={t('Remove query from log')} tooltip={t('Remove query from log')}
onClick={() => removeQuery(query)} onClick={() => dispatch(removeQuery(query))}
> >
<Icons.Trash iconSize="xl" /> <Icons.Trash iconSize="xl" />
</StyledTooltip> </StyledTooltip>
@ -303,19 +296,7 @@ const QueryTable = ({
return q; return q;
}) })
.reverse(); .reverse();
}, [ }, [queries, onUserClicked, onDbClicked, user, displayLimit]);
queries,
onUserClicked,
onDbClicked,
user,
displayLimit,
actions,
clearQueryResults,
cloneQueryToNewTab,
fetchQueryResults,
queryEditorSetAndSaveSql,
removeQuery,
]);
return ( return (
<div className="QueryTable"> <div className="QueryTable">

View File

@ -23,15 +23,15 @@ import { DropdownButton } from 'src/components/DropdownButton';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { DropdownButtonProps } from 'antd/lib/dropdown'; import { DropdownButtonProps } from 'antd/lib/dropdown';
interface Props { interface SaveDatasetActionButtonProps {
setShowSave: (arg0: boolean) => void; setShowSave: (arg0: boolean) => void;
overlayMenu: JSX.Element | null; overlayMenu: JSX.Element | null;
} }
export default function SaveDatasetActionButton({ const SaveDatasetActionButton = ({
setShowSave, setShowSave,
overlayMenu, overlayMenu,
}: Props) { }: SaveDatasetActionButtonProps) => {
const theme = useTheme(); const theme = useTheme();
const StyledDropdownButton = styled( const StyledDropdownButton = styled(
@ -80,4 +80,6 @@ export default function SaveDatasetActionButton({
{t('Save')} {t('Save')}
</StyledDropdownButton> </StyledDropdownButton>
); );
} };
export default SaveDatasetActionButton;

View File

@ -19,47 +19,30 @@
import React from 'react'; import React from 'react';
import configureStore from 'redux-mock-store'; import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import { styledShallow as shallow } from 'spec/helpers/theming'; import { render, screen, waitFor } from 'spec/helpers/testing-library';
import { render, screen, act } from 'spec/helpers/testing-library'; import SouthPane from 'src/SqlLab/components/SouthPane';
import SouthPaneContainer from 'src/SqlLab/components/SouthPane/state';
import ResultSet from 'src/SqlLab/components/ResultSet';
import '@testing-library/jest-dom/extend-expect'; import '@testing-library/jest-dom/extend-expect';
import { STATUS_OPTIONS } from 'src/SqlLab/constants'; import { STATUS_OPTIONS } from 'src/SqlLab/constants';
import { initialState, table, defaultQueryEditor } from 'src/SqlLab/fixtures'; import { initialState, table, defaultQueryEditor } from 'src/SqlLab/fixtures';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
const NOOP = () => {};
const mockedProps = { const mockedProps = {
queryEditorId: defaultQueryEditor.id, queryEditorId: defaultQueryEditor.id,
latestQueryId: 'LCly_kkIN', latestQueryId: 'LCly_kkIN',
actions: {},
activeSouthPaneTab: '',
height: 1, height: 1,
displayLimit: 1, displayLimit: 1,
databases: {},
defaultQueryLimit: 100, defaultQueryLimit: 100,
}; };
const mockedEmptyProps = { const mockedEmptyProps = {
queryEditorId: 'random_id', queryEditorId: 'random_id',
latestQueryId: '', latestQueryId: '',
actions: {
queryEditorSetAndSaveSql: NOOP,
cloneQueryToNewTab: NOOP,
fetchQueryResults: NOOP,
clearQueryResults: NOOP,
removeQuery: NOOP,
setActiveSouthPaneTab: NOOP,
},
activeSouthPaneTab: '',
height: 100, height: 100,
databases: '',
displayLimit: 100, displayLimit: 100,
user: UserWithPermissionsAndRoles,
defaultQueryLimit: 100, defaultQueryLimit: 100,
}; };
const latestQueryProgressMsg = 'LATEST QUERY MESSAGE - LCly_kkIN';
const middlewares = [thunk]; const middlewares = [thunk];
const mockStore = configureStore(middlewares); const mockStore = configureStore(middlewares);
const store = mockStore({ const store = mockStore({
@ -84,6 +67,7 @@ const store = mockStore({
id: 'LCly_kkIN', id: 'LCly_kkIN',
startDttm: Date.now(), startDttm: Date.now(),
sqlEditorId: defaultQueryEditor.id, sqlEditorId: defaultQueryEditor.id,
extra: { progress: latestQueryProgressMsg },
}, },
lXJa7F9_r: { lXJa7F9_r: {
cached: false, cached: false,
@ -115,48 +99,40 @@ const store = mockStore({
}, },
}, },
}); });
const setup = (overrides = {}) => ( const setup = (props, store) =>
<SouthPaneContainer store={store} {...mockedProps} {...overrides} /> render(<SouthPane {...props} />, {
); useRedux: true,
...(store && { store }),
describe('SouthPane - Enzyme', () => {
const getWrapper = () => shallow(setup()).dive();
let wrapper;
it('should render offline when the state is offline', () => {
wrapper = getWrapper().dive();
wrapper.setProps({ offline: true });
expect(wrapper.childAt(0).text()).toBe(STATUS_OPTIONS.offline);
}); });
it('should pass latest query down to ResultSet component', () => { describe('SouthPane', () => {
wrapper = getWrapper().dive(); const renderAndWait = (props, store) =>
expect(wrapper.find(ResultSet)).toExist(); waitFor(async () => setup(props, store));
// for editorQueries
expect(wrapper.find(ResultSet).first().props().query.id).toEqual(
mockedProps.latestQueryId,
);
// for dataPreviewQueries
expect(wrapper.find(ResultSet).last().props().query.id).toEqual(
'2g2_iRFMl',
);
});
});
describe('SouthPane - RTL', () => {
const renderAndWait = overrides => {
const mounted = act(async () => {
render(setup(overrides));
});
return mounted;
};
it('Renders an empty state for results', async () => { it('Renders an empty state for results', async () => {
await renderAndWait(mockedEmptyProps); await renderAndWait(mockedEmptyProps, store);
const emptyStateText = screen.getByText(/run a query to display results/i); const emptyStateText = screen.getByText(/run a query to display results/i);
expect(emptyStateText).toBeVisible(); expect(emptyStateText).toBeVisible();
}); });
it('should render offline when the state is offline', async () => {
await renderAndWait(
mockedEmptyProps,
mockStore({
...initialState,
sqlLab: {
...initialState.sqlLab,
offline: true,
},
}),
);
expect(screen.getByText(STATUS_OPTIONS.offline)).toBeVisible();
});
it('should pass latest query down to ResultSet component', async () => {
await renderAndWait(mockedProps, store);
expect(screen.getByText(latestQueryProgressMsg)).toBeVisible();
});
}); });

View File

@ -17,16 +17,18 @@
* under the License. * under the License.
*/ */
import React, { createRef } from 'react'; import React, { createRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import shortid from 'shortid'; import shortid from 'shortid';
import Alert from 'src/components/Alert'; import Alert from 'src/components/Alert';
import Tabs from 'src/components/Tabs'; import Tabs from 'src/components/Tabs';
import { EmptyStateMedium } from 'src/components/EmptyState'; import { EmptyStateMedium } from 'src/components/EmptyState';
import { t, styled } from '@superset-ui/core'; import { t, styled } from '@superset-ui/core';
import { setActiveSouthPaneTab } from 'src/SqlLab/actions/sqlLab';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import Label from 'src/components/Label'; import Label from 'src/components/Label';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { SqlLabRootState } from 'src/SqlLab/types';
import QueryHistory from '../QueryHistory'; import QueryHistory from '../QueryHistory';
import ResultSet from '../ResultSet'; import ResultSet from '../ResultSet';
import { import {
@ -39,27 +41,13 @@ const TAB_HEIGHT = 140;
/* /*
editorQueries are queries executed by users passed from SqlEditor component editorQueries are queries executed by users passed from SqlEditor component
dataPrebiewQueries are all queries executed for preview of table data (from SqlEditorLeft) dataPreviewQueries are all queries executed for preview of table data (from SqlEditorLeft)
*/ */
export interface SouthPanePropTypes { export interface SouthPaneProps {
queryEditorId: string; queryEditorId: string;
editorQueries: any[];
latestQueryId?: string; latestQueryId?: string;
dataPreviewQueries: any[];
actions: {
queryEditorSetAndSaveSql: Function;
cloneQueryToNewTab: Function;
fetchQueryResults: Function;
clearQueryResults: Function;
removeQuery: Function;
setActiveSouthPaneTab: Function;
};
activeSouthPaneTab?: string;
height: number; height: number;
databases: Record<string, any>;
offline?: boolean;
displayLimit: number; displayLimit: number;
user: UserWithPermissionsAndRoles;
defaultQueryLimit: number; defaultQueryLimit: number;
} }
@ -111,23 +99,49 @@ const StyledEmptyStateWrapper = styled.div`
} }
`; `;
export default function SouthPane({ const SouthPane = ({
editorQueries, queryEditorId,
latestQueryId, latestQueryId,
dataPreviewQueries,
actions,
activeSouthPaneTab = 'Results',
height, height,
databases,
offline = false,
displayLimit, displayLimit,
user,
defaultQueryLimit, defaultQueryLimit,
}: SouthPanePropTypes) { }: SouthPaneProps) => {
const dispatch = useDispatch();
const { editorQueries, dataPreviewQueries, databases, offline, user } =
useSelector(({ sqlLab }: SqlLabRootState) => {
const { databases, offline, user, queries, tables } = sqlLab;
const dataPreviewQueries = tables
.filter(
({ dataPreviewQueryId, queryEditorId: qeId }) =>
dataPreviewQueryId &&
queryEditorId === qeId &&
queries[dataPreviewQueryId],
)
.map(({ name, dataPreviewQueryId }) => ({
...queries[dataPreviewQueryId],
tableName: name,
}));
const editorQueries = Object.values(queries).filter(
({ sqlEditorId }) => sqlEditorId === queryEditorId,
);
return {
editorQueries,
dataPreviewQueries,
databases,
offline: offline ?? false,
user,
};
});
const activeSouthPaneTab =
useSelector<SqlLabRootState, string>(
state => state.sqlLab.activeSouthPaneTab as string,
) ?? 'Results';
const innerTabContentHeight = height - TAB_HEIGHT; const innerTabContentHeight = height - TAB_HEIGHT;
const southPaneRef = createRef<HTMLDivElement>(); const southPaneRef = createRef<HTMLDivElement>();
const switchTab = (id: string) => { const switchTab = (id: string) => {
actions.setActiveSouthPaneTab(id); dispatch(setActiveSouthPaneTab(id));
}; };
const renderOfflineStatus = () => ( const renderOfflineStatus = () => (
<Label className="m-r-3" type={STATE_TYPE_MAP[STATUS_OPTIONS.offline]}> <Label className="m-r-3" type={STATE_TYPE_MAP[STATUS_OPTIONS.offline]}>
@ -209,7 +223,12 @@ export default function SouthPane({
return offline ? ( return offline ? (
renderOfflineStatus() renderOfflineStatus()
) : ( ) : (
<StyledPane className="SouthPane" height={height} ref={southPaneRef}> <StyledPane
data-test="south-pane"
className="SouthPane"
height={height}
ref={southPaneRef}
>
<Tabs <Tabs
activeKey={activeSouthPaneTab} activeKey={activeSouthPaneTab}
className="SouthPaneTabs" className="SouthPaneTabs"
@ -224,7 +243,6 @@ export default function SouthPane({
<Tabs.TabPane tab={t('Query history')} key="History"> <Tabs.TabPane tab={t('Query history')} key="History">
<QueryHistory <QueryHistory
queries={editorQueries} queries={editorQueries}
actions={actions}
displayLimit={displayLimit} displayLimit={displayLimit}
latestQueryId={latestQueryId} latestQueryId={latestQueryId}
/> />
@ -233,4 +251,6 @@ export default function SouthPane({
</Tabs> </Tabs>
</StyledPane> </StyledPane>
); );
} };
export default SouthPane;

View File

@ -1,61 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import * as Actions from 'src/SqlLab/actions/sqlLab';
import { SqlLabRootState } from 'src/SqlLab/types';
import SouthPane, { SouthPanePropTypes } from '.';
function mapStateToProps(
{ sqlLab }: SqlLabRootState,
{ queryEditorId }: SouthPanePropTypes,
) {
const { databases, activeSouthPaneTab, offline, user, queries, tables } =
sqlLab;
const dataPreviewQueries = tables
.filter(
({ dataPreviewQueryId, queryEditorId: qeId }) =>
dataPreviewQueryId &&
queryEditorId === qeId &&
queries[dataPreviewQueryId],
)
.map(({ name, dataPreviewQueryId }) => ({
...queries[dataPreviewQueryId],
tableName: name,
}));
const editorQueries = Object.values(queries).filter(
({ sqlEditorId }) => sqlEditorId === queryEditorId,
);
return {
editorQueries,
dataPreviewQueries,
activeSouthPaneTab,
databases,
offline,
user,
};
}
function mapDispatchToProps(dispatch: Dispatch) {
return {
actions: bindActionCreators<any, any>(Actions, dispatch),
};
}
export default connect<any>(mapStateToProps, mapDispatchToProps)(SouthPane);

View File

@ -30,14 +30,9 @@ import {
SQL_TOOLBAR_HEIGHT, SQL_TOOLBAR_HEIGHT,
} from 'src/SqlLab/constants'; } from 'src/SqlLab/constants';
import AceEditorWrapper from 'src/SqlLab/components/AceEditorWrapper'; import AceEditorWrapper from 'src/SqlLab/components/AceEditorWrapper';
import ConnectedSouthPane from 'src/SqlLab/components/SouthPane/state'; import SouthPane from 'src/SqlLab/components/SouthPane';
import SqlEditor from 'src/SqlLab/components/SqlEditor'; import SqlEditor from 'src/SqlLab/components/SqlEditor';
import QueryProvider from 'src/views/QueryProvider'; import QueryProvider from 'src/views/QueryProvider';
import {
queryEditorSetFunctionNames,
queryEditorSetSelectedText,
queryEditorSetSchemaOptions,
} from 'src/SqlLab/actions/sqlLab';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import { import {
initialState, initialState,
@ -97,13 +92,6 @@ const setup = (props = {}, store) =>
describe('SqlEditor', () => { describe('SqlEditor', () => {
const mockedProps = { const mockedProps = {
actions: {
queryEditorSetFunctionNames,
queryEditorSetSelectedText,
queryEditorSetSchemaOptions,
addDangerToast: jest.fn(),
removeDataPreview: jest.fn(),
},
queryEditor: initialState.sqlLab.queryEditors[0], queryEditor: initialState.sqlLab.queryEditors[0],
latestQuery: queries[0], latestQuery: queries[0],
tables: [table], tables: [table],
@ -238,7 +226,7 @@ describe('SqlEditor', () => {
await waitForComponentToPaint(wrapper); await waitForComponentToPaint(wrapper);
const totalSize = const totalSize =
parseFloat(wrapper.find(AceEditorWrapper).props().height) + parseFloat(wrapper.find(AceEditorWrapper).props().height) +
wrapper.find(ConnectedSouthPane).props().height + wrapper.find(SouthPane).props().height +
SQL_TOOLBAR_HEIGHT + SQL_TOOLBAR_HEIGHT +
SQL_EDITOR_GUTTER_MARGIN * 2 + SQL_EDITOR_GUTTER_MARGIN * 2 +
SQL_EDITOR_GUTTER_HEIGHT; SQL_EDITOR_GUTTER_HEIGHT;
@ -252,7 +240,7 @@ describe('SqlEditor', () => {
await waitForComponentToPaint(wrapper); await waitForComponentToPaint(wrapper);
const totalSize = const totalSize =
parseFloat(wrapper.find(AceEditorWrapper).props().height) + parseFloat(wrapper.find(AceEditorWrapper).props().height) +
wrapper.find(ConnectedSouthPane).props().height + wrapper.find(SouthPane).props().height +
SQL_TOOLBAR_HEIGHT + SQL_TOOLBAR_HEIGHT +
SQL_EDITOR_GUTTER_MARGIN * 2 + SQL_EDITOR_GUTTER_MARGIN * 2 +
SQL_EDITOR_GUTTER_HEIGHT; SQL_EDITOR_GUTTER_HEIGHT;

View File

@ -76,7 +76,7 @@ import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { EmptyStateBig } from 'src/components/EmptyState'; import { EmptyStateBig } from 'src/components/EmptyState';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import TemplateParamsEditor from '../TemplateParamsEditor'; import TemplateParamsEditor from '../TemplateParamsEditor';
import ConnectedSouthPane from '../SouthPane/state'; import SouthPane from '../SouthPane';
import SaveQuery from '../SaveQuery'; import SaveQuery from '../SaveQuery';
import ScheduleQueryButton from '../ScheduleQueryButton'; import ScheduleQueryButton from '../ScheduleQueryButton';
import EstimateQueryCostButton from '../EstimateQueryCostButton'; import EstimateQueryCostButton from '../EstimateQueryCostButton';
@ -135,7 +135,6 @@ const StyledSidebar = styled.div`
`; `;
const propTypes = { const propTypes = {
actions: PropTypes.object.isRequired,
tables: PropTypes.array.isRequired, tables: PropTypes.array.isRequired,
queryEditor: PropTypes.object.isRequired, queryEditor: PropTypes.object.isRequired,
defaultQueryLimit: PropTypes.number.isRequired, defaultQueryLimit: PropTypes.number.isRequired,
@ -146,7 +145,6 @@ const propTypes = {
}; };
const SqlEditor = ({ const SqlEditor = ({
actions,
tables, tables,
queryEditor, queryEditor,
defaultQueryLimit, defaultQueryLimit,
@ -617,10 +615,9 @@ const SqlEditor = ({
/> />
{renderEditorBottomBar(hotkeys)} {renderEditorBottomBar(hotkeys)}
</div> </div>
<ConnectedSouthPane <SouthPane
queryEditorId={queryEditor.id} queryEditorId={queryEditor.id}
latestQueryId={latestQuery?.id} latestQueryId={latestQuery?.id}
actions={actions}
height={southPaneHeight} height={southPaneHeight}
displayLimit={displayLimit} displayLimit={displayLimit}
defaultQueryLimit={defaultQueryLimit} defaultQueryLimit={defaultQueryLimit}

View File

@ -264,7 +264,6 @@ class TabbedSqlEditors extends React.PureComponent {
<SqlEditor <SqlEditor
tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)} tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)}
queryEditor={qe} queryEditor={qe}
actions={this.props.actions}
defaultQueryLimit={this.props.defaultQueryLimit} defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow} maxRow={this.props.maxRow}
displayLimit={this.props.displayLimit} displayLimit={this.props.displayLimit}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { JsonObject, Query, QueryResponse } from '@superset-ui/core'; import { JsonObject, QueryResponse } from '@superset-ui/core';
import { SupersetError } from 'src/components/ErrorMessage/types'; import { SupersetError } from 'src/components/ErrorMessage/types';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import { ToastType } from 'src/components/MessageToasts/types'; import { ToastType } from 'src/components/MessageToasts/types';
@ -69,7 +69,7 @@ export type SqlLabRootState = {
databases: Record<string, any>; databases: Record<string, any>;
dbConnect: boolean; dbConnect: boolean;
offline: boolean; offline: boolean;
queries: Record<string, Query>; queries: Record<string, QueryResponse>;
queryEditors: QueryEditor[]; queryEditors: QueryEditor[];
tabHistory: string[]; // default is activeTab ? [activeTab.id.toString()] : [] tabHistory: string[]; // default is activeTab ? [activeTab.id.toString()] : []
tables: Record<string, any>[]; tables: Record<string, any>[];