diff --git a/superset-frontend/cypress-base/cypress/e2e/sqllab/_skip.sourcePanel.index.test.js b/superset-frontend/cypress-base/cypress/e2e/sqllab/_skip.sourcePanel.index.test.js
index be455a4a99b7..ece158171411 100644
--- a/superset-frontend/cypress-base/cypress/e2e/sqllab/_skip.sourcePanel.index.test.js
+++ b/superset-frontend/cypress-base/cypress/e2e/sqllab/_skip.sourcePanel.index.test.js
@@ -20,7 +20,7 @@ import { selectResultsTab } from './sqllab.helper';
describe.skip('SqlLab datasource panel', () => {
beforeEach(() => {
- cy.visit('/superset/sqllab');
+ cy.visit('/sqllab');
});
// TODO the test bellow is flaky, and has been disabled for the time being
diff --git a/superset-frontend/cypress-base/cypress/e2e/sqllab/query.test.ts b/superset-frontend/cypress-base/cypress/e2e/sqllab/query.test.ts
index 0d36692b2ad0..86502e86555b 100644
--- a/superset-frontend/cypress-base/cypress/e2e/sqllab/query.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/sqllab/query.test.ts
@@ -25,7 +25,7 @@ function parseClockStr(node: JQuery) {
describe('SqlLab query panel', () => {
beforeEach(() => {
- cy.visit('/superset/sqllab');
+ cy.visit('/sqllab');
});
it.skip('supports entering and running a query', () => {
diff --git a/superset-frontend/cypress-base/cypress/e2e/sqllab/sqllab.applitools.test.ts b/superset-frontend/cypress-base/cypress/e2e/sqllab/sqllab.applitools.test.ts
index fdbaefb158f1..cc4cf7ac03f1 100644
--- a/superset-frontend/cypress-base/cypress/e2e/sqllab/sqllab.applitools.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/sqllab/sqllab.applitools.test.ts
@@ -19,7 +19,7 @@
describe('SqlLab view', () => {
beforeEach(() => {
- cy.visit('/superset/sqllab');
+ cy.visit('/sqllab');
});
it('should load the SqlLab', () => {
diff --git a/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts b/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts
index b2c7a180ad83..0deeabde8dc1 100644
--- a/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts
@@ -18,7 +18,7 @@
*/
describe('SqlLab query tabs', () => {
beforeEach(() => {
- cy.visit('/superset/sqllab');
+ cy.visit('/sqllab');
});
const tablistSelector = '[data-test="sql-editor-tabs"] > [role="tablist"]';
diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index 694cfd919328..4c0fa255e1e0 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -77984,7 +77984,7 @@
"@mapbox/geojson-extent": "^1.0.1",
"@math.gl/web-mercator": "^3.2.2",
"@types/d3-array": "^2.0.0",
- "@types/mapbox__geojson-extent": "*",
+ "@types/mapbox__geojson-extent": "^1.0.0",
"@types/underscore": "^1.11.6",
"@types/urijs": "^1.19.19",
"bootstrap-slider": "^10.0.0",
diff --git a/superset-frontend/spec/helpers/reducerIndex.ts b/superset-frontend/spec/helpers/reducerIndex.ts
index a9cadc4f8136..95fe4d3f1ccf 100644
--- a/superset-frontend/spec/helpers/reducerIndex.ts
+++ b/superset-frontend/spec/helpers/reducerIndex.ts
@@ -29,7 +29,6 @@ import messageToasts from 'src/components/MessageToasts/reducers';
import saveModal from 'src/explore/reducers/saveModalReducer';
import explore from 'src/explore/reducers/exploreReducer';
import sqlLab from 'src/SqlLab/reducers/sqlLab';
-import localStorageUsageInKilobytes from 'src/SqlLab/reducers/localStorageUsage';
import reports from 'src/features/reports/ReportModal/reducer';
import getBootstrapData from 'src/utils/getBootstrapData';
@@ -59,7 +58,7 @@ export default {
saveModal,
explore,
sqlLab,
- localStorageUsageInKilobytes,
+ localStorageUsageInKilobytes: noopReducer(0),
reports,
common: noopReducer(common),
user: noopReducer(user),
diff --git a/superset-frontend/src/SqlLab/App.jsx b/superset-frontend/src/SqlLab/App.jsx
deleted file mode 100644
index ae8b81f4a8a4..000000000000
--- a/superset-frontend/src/SqlLab/App.jsx
+++ /dev/null
@@ -1,84 +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 React from 'react';
-import { Provider } from 'react-redux';
-import { hot } from 'react-hot-loader/root';
-import {
- FeatureFlag,
- ThemeProvider,
- initFeatureFlags,
- isFeatureEnabled,
-} from '@superset-ui/core';
-import { GlobalStyles } from 'src/GlobalStyles';
-import { setupStore, userReducer } from 'src/views/store';
-import setupExtensions from 'src/setup/setupExtensions';
-import getBootstrapData from 'src/utils/getBootstrapData';
-import { persistSqlLabStateEnhancer } from 'src/SqlLab/middlewares/persistSqlLabStateEnhancer';
-import getInitialState from './reducers/getInitialState';
-import { reducers } from './reducers/index';
-import App from './components/App';
-import { rehydratePersistedState } from './utils/reduxStateToLocalStorageHelper';
-import setupApp from '../setup/setupApp';
-
-import '../assets/stylesheets/reactable-pagination.less';
-import { theme } from '../preamble';
-import { SqlLabGlobalStyles } from './SqlLabGlobalStyles';
-
-setupApp();
-setupExtensions();
-
-const bootstrapData = getBootstrapData();
-
-initFeatureFlags(bootstrapData.common.feature_flags);
-
-const initialState = getInitialState(bootstrapData);
-
-export const store = setupStore({
- initialState,
- rootReducers: { ...reducers, user: userReducer },
- ...(!isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) && {
- enhancers: [persistSqlLabStateEnhancer],
- }),
-});
-
-rehydratePersistedState(store.dispatch, initialState);
-
-// Highlight the navbar menu
-const menus = document.querySelectorAll('.nav.navbar-nav li.dropdown');
-const sqlLabMenu = Array.prototype.slice
- .apply(menus)
- .find(element => element.innerText.trim() === 'SQL Lab');
-if (sqlLabMenu) {
- const classes = sqlLabMenu.getAttribute('class');
- if (classes.indexOf('active') === -1) {
- sqlLabMenu.setAttribute('class', `${classes} active`);
- }
-}
-
-const Application = () => (
-
-
-
-
-
-
-
-);
-
-export default hot(Application);
diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.test.ts b/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.test.ts
index 12bd95b40236..7aa306d8bc25 100644
--- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.test.ts
+++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.test.ts
@@ -28,7 +28,7 @@ import { schemaApiUtil } from 'src/hooks/apiResources/schemas';
import { tableApiUtil } from 'src/hooks/apiResources/tables';
import { addTable } from 'src/SqlLab/actions/sqlLab';
import { initialState } from 'src/SqlLab/fixtures';
-import { reducers } from 'src/SqlLab/reducers';
+import reducers from 'spec/helpers/reducerIndex';
import {
SCHEMA_AUTOCOMPLETE_SCORE,
TABLE_AUTOCOMPLETE_SCORE,
diff --git a/superset-frontend/src/SqlLab/components/App/App.test.jsx b/superset-frontend/src/SqlLab/components/App/App.test.jsx
index d56ea4780e9f..d3db1d5fb87b 100644
--- a/superset-frontend/src/SqlLab/components/App/App.test.jsx
+++ b/superset-frontend/src/SqlLab/components/App/App.test.jsx
@@ -17,12 +17,13 @@
* under the License.
*/
import React from 'react';
+import { combineReducers } from 'redux';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { render } from 'spec/helpers/testing-library';
import App from 'src/SqlLab/components/App';
-import sqlLabReducer from 'src/SqlLab/reducers/index';
+import reducers from 'spec/helpers/reducerIndex';
import { LOCALSTORAGE_MAX_USAGE_KB } from 'src/SqlLab/constants';
import { LOG_EVENT } from 'src/logger/actions';
import {
@@ -37,6 +38,8 @@ jest.mock('src/SqlLab/components/QueryAutoRefresh', () => () => (
));
+const sqlLabReducer = combineReducers(reducers);
+
describe('SqlLab App', () => {
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
diff --git a/superset-frontend/src/SqlLab/components/App/index.jsx b/superset-frontend/src/SqlLab/components/App/index.jsx
index ff47e6173b85..aab4e78d4f23 100644
--- a/superset-frontend/src/SqlLab/components/App/index.jsx
+++ b/superset-frontend/src/SqlLab/components/App/index.jsx
@@ -20,9 +20,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom';
import { css, styled, t } from '@superset-ui/core';
import throttle from 'lodash/throttle';
-import ToastContainer from 'src/components/MessageToasts/ToastContainer';
import {
LOCALSTORAGE_MAX_USAGE_KB,
LOCALSTORAGE_WARNING_THRESHOLD,
@@ -186,7 +186,14 @@ class App extends React.PureComponent {
render() {
const { queries, queriesLastUpdate } = this.props;
if (this.state.hash && this.state.hash === '#search') {
- return window.location.replace('/superset/sqllab/history/');
+ return (
+
+ );
}
return (
@@ -195,7 +202,6 @@ class App extends React.PureComponent {
queriesLastUpdate={queriesLastUpdate}
/>
-
);
}
diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
index b5eaeb01e658..6ddae08e6852 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
@@ -61,7 +61,7 @@ interface QueryTableProps {
}
const openQuery = (id: number) => {
- const url = `/superset/sqllab?queryId=${id}`;
+ const url = `/sqllab?queryId=${id}`;
window.open(url);
};
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx b/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx
index 23424ff264ba..ff335e14ea77 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx
@@ -20,7 +20,7 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
import fetchMock from 'fetch-mock';
-import { reducers } from 'src/SqlLab/reducers';
+import reducers from 'spec/helpers/reducerIndex';
import SqlEditor from 'src/SqlLab/components/SqlEditor';
import { setupStore } from 'src/views/store';
import {
diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/SqlEditorLeftBar.test.jsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/SqlEditorLeftBar.test.jsx
index d12938a23508..6665091572d3 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/SqlEditorLeftBar.test.jsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/SqlEditorLeftBar.test.jsx
@@ -26,7 +26,7 @@ import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
import { table, initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
import { api } from 'src/hooks/apiResources/queryApi';
import { setupStore } from 'src/views/store';
-import { reducers } from 'src/SqlLab/reducers';
+import reducers from 'spec/helpers/reducerIndex';
const mockedProps = {
tables: [table],
diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx
index 90d1de2528a6..5d782590a1df 100644
--- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx
+++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx
@@ -110,23 +110,17 @@ describe('TabbedSqlEditors', () => {
it('should handle id', async () => {
uriStub.returns({ id: 1 });
await mountWithAct();
- expect(window.history.replaceState.getCall(0).args[2]).toBe(
- '/superset/sqllab',
- );
+ expect(window.history.replaceState.getCall(0).args[2]).toBe('/sqllab');
});
it('should handle savedQueryId', async () => {
uriStub.returns({ savedQueryId: 1 });
await mountWithAct();
- expect(window.history.replaceState.getCall(0).args[2]).toBe(
- '/superset/sqllab',
- );
+ expect(window.history.replaceState.getCall(0).args[2]).toBe('/sqllab');
});
it('should handle sql', async () => {
uriStub.returns({ sql: 1, dbid: 1 });
await mountWithAct();
- expect(window.history.replaceState.getCall(0).args[2]).toBe(
- '/superset/sqllab',
- );
+ expect(window.history.replaceState.getCall(0).args[2]).toBe('/sqllab');
});
it('should handle custom url params', async () => {
uriStub.returns({
@@ -137,7 +131,7 @@ describe('TabbedSqlEditors', () => {
});
await mountWithAct();
expect(window.history.replaceState.getCall(0).args[2]).toBe(
- '/superset/sqllab?custom_value=str&extra_attr1=true',
+ '/sqllab?custom_value=str&extra_attr1=true',
);
});
});
diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx
index 95d0c2529b24..166cce18f92b 100644
--- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx
+++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx
@@ -29,6 +29,7 @@ import { detectOS } from 'src/utils/common';
import * as Actions from 'src/SqlLab/actions/sqlLab';
import { EmptyStateBig } from 'src/components/EmptyState';
import getBootstrapData from 'src/utils/getBootstrapData';
+import { locationContext } from 'src/pages/SqlLab/LocationContext';
import SqlEditor from '../SqlEditor';
import SqlEditorTabHeader from '../SqlEditorTabHeader';
@@ -75,7 +76,7 @@ const userOS = detectOS();
class TabbedSqlEditors extends React.PureComponent {
constructor(props) {
super(props);
- const sqlLabUrl = '/superset/sqllab';
+ const sqlLabUrl = '/sqllab';
this.state = {
sqlLabUrl,
};
@@ -132,6 +133,7 @@ class TabbedSqlEditors extends React.PureComponent {
new: isNewQuery,
...urlParams
} = {
+ ...this.context.requestedQuery,
...bootstrapData.requested_query,
...queryParameters,
};
@@ -332,6 +334,7 @@ class TabbedSqlEditors extends React.PureComponent {
}
TabbedSqlEditors.propTypes = propTypes;
TabbedSqlEditors.defaultProps = defaultProps;
+TabbedSqlEditors.contextType = locationContext;
function mapStateToProps({ sqlLab, common }) {
return {
diff --git a/superset-frontend/src/SqlLab/index.tsx b/superset-frontend/src/SqlLab/index.tsx
deleted file mode 100644
index c257009e64fd..000000000000
--- a/superset-frontend/src/SqlLab/index.tsx
+++ /dev/null
@@ -1,23 +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 React from 'react';
-import ReactDOM from 'react-dom';
-import App from './App';
-
-ReactDOM.render(, document.getElementById('app'));
diff --git a/superset-frontend/src/SqlLab/reducers/common.js b/superset-frontend/src/SqlLab/reducers/common.js
deleted file mode 100644
index 05a7968a88ff..000000000000
--- a/superset-frontend/src/SqlLab/reducers/common.js
+++ /dev/null
@@ -1,21 +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.
- */
-export default function commonReducer(state = {}) {
- return state;
-}
diff --git a/superset-frontend/src/SqlLab/reducers/localStorageUsage.js b/superset-frontend/src/SqlLab/reducers/localStorageUsage.js
deleted file mode 100644
index eafbb078168c..000000000000
--- a/superset-frontend/src/SqlLab/reducers/localStorageUsage.js
+++ /dev/null
@@ -1,21 +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.
- */
-export default function localStorageUsageReducer(state = 0) {
- return state;
-}
diff --git a/superset-frontend/src/components/Chart/chartAction.js b/superset-frontend/src/components/Chart/chartAction.js
index d1dcfd3a001b..fcf45a4946b2 100644
--- a/superset-frontend/src/components/Chart/chartAction.js
+++ b/superset-frontend/src/components/Chart/chartAction.js
@@ -39,7 +39,6 @@ import { addDangerToast } from 'src/components/MessageToasts/actions';
import { logEvent } from 'src/logger/actions';
import { Logger, LOG_ACTIONS_LOAD_CHART } from 'src/logger/LogUtils';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
-import { safeStringify } from 'src/utils/safeStringify';
import { allowCrossDomain as domainShardingEnabled } from 'src/utils/hostNamesConfig';
import { updateDataMask } from 'src/dataMask/actions';
import { waitForAsyncData } from 'src/middleware/asyncEvent';
@@ -571,17 +570,20 @@ export function postChartFormData(
);
}
-export function redirectSQLLab(formData) {
+export function redirectSQLLab(formData, history) {
return dispatch => {
getChartDataRequest({ formData, resultFormat: 'json', resultType: 'query' })
.then(({ json }) => {
- const redirectUrl = '/superset/sqllab/';
+ const redirectUrl = '/sqllab/';
const payload = {
datasourceKey: formData.datasource,
sql: json.result[0].query,
};
- SupersetClient.postForm(redirectUrl, {
- form_data: safeStringify(payload),
+ history.push({
+ pathname: redirectUrl,
+ state: {
+ requestedQuery: payload,
+ },
});
})
.catch(() =>
diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
index 958aa16a3199..6e11eaf1c5ca 100644
--- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
+++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
@@ -17,6 +17,7 @@
* under the License.
*/
import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import { Tooltip } from 'src/components/Tooltip';
@@ -151,12 +152,22 @@ export const ExploreChartHeader = ({
[dispatch],
);
+ const history = useHistory();
+ const { redirectSQLLab } = actions;
+
+ const redirectToSQLLab = useCallback(
+ formData => {
+ redirectSQLLab(formData, history);
+ },
+ [redirectSQLLab, history],
+ );
+
const [menu, isDropdownVisible, setIsDropdownVisible] =
useExploreAdditionalActionsMenu(
latestQueryFormData,
canDownload,
slice,
- actions.redirectSQLLab,
+ redirectToSQLLab,
openPropertiesModal,
ownState,
metadata?.dashboards,
diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx
index 6def65d7d2d6..4531719246bb 100644
--- a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx
+++ b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx
@@ -18,6 +18,7 @@
*/
import React from 'react';
+import { Route } from 'react-router-dom';
import fetchMock from 'fetch-mock';
import userEvent from '@testing-library/user-event';
import { DatasourceType, JsonObject, SupersetClient } from '@superset-ui/core';
@@ -27,6 +28,17 @@ import DatasourceControl from '.';
const SupersetClientGet = jest.spyOn(SupersetClient, 'get');
+const mockDatasource = {
+ id: 25,
+ database: {
+ name: 'examples',
+ },
+ name: 'channels',
+ type: 'table',
+ columns: [],
+ owners: [{ first_name: 'john', last_name: 'doe', id: 1, username: 'jd' }],
+ sql: 'SELECT * FROM mock_datasource_sql',
+};
const createProps = (overrides: JsonObject = {}) => ({
hovered: false,
type: 'DatasourceControl',
@@ -35,16 +47,7 @@ const createProps = (overrides: JsonObject = {}) => ({
description: null,
value: '25__table',
form_data: {},
- datasource: {
- id: 25,
- database: {
- name: 'examples',
- },
- name: 'channels',
- type: 'table',
- columns: [],
- owners: [{ first_name: 'john', last_name: 'doe', id: 1, username: 'jd' }],
- },
+ datasource: mockDatasource,
validationErrors: [],
name: 'datasource',
actions: {
@@ -91,20 +94,20 @@ async function openAndSaveChanges(datasource: any) {
test('Should render', async () => {
const props = createProps();
- render();
+ render(, { useRouter: true });
expect(await screen.findByTestId('datasource-control')).toBeVisible();
});
test('Should have elements', async () => {
const props = createProps();
- render();
+ render(, { useRouter: true });
expect(await screen.findByText('channels')).toBeVisible();
expect(screen.getByTestId('datasource-menu-trigger')).toBeVisible();
});
test('Should open a menu', async () => {
const props = createProps();
- render();
+ render(, { useRouter: true });
expect(screen.queryByText('Edit dataset')).not.toBeInTheDocument();
expect(screen.queryByText('Swap dataset')).not.toBeInTheDocument();
@@ -131,7 +134,7 @@ test('Should not show SQL Lab for non sql_lab role', async () => {
username: 'gamma',
},
});
- render();
+ render(, { useRouter: true });
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
@@ -154,7 +157,7 @@ test('Should show SQL Lab for sql_lab role', async () => {
username: 'sql',
},
});
- render();
+ render(, { useRouter: true });
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
@@ -178,6 +181,7 @@ test('Click on Swap dataset option', async () => {
render(, {
useRedux: true,
+ useRouter: true,
});
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
@@ -198,6 +202,7 @@ test('Click on Edit dataset', async () => {
);
render(, {
useRedux: true,
+ useRouter: true,
});
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
@@ -223,6 +228,7 @@ test('Edit dataset should be disabled when user is not admin', async () => {
render(, {
useRedux: true,
+ useRouter: true,
});
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
@@ -235,21 +241,41 @@ test('Edit dataset should be disabled when user is not admin', async () => {
test('Click on View in SQL Lab', async () => {
const props = createProps();
- const postFormSpy = jest.spyOn(SupersetClient, 'postForm');
- postFormSpy.mockImplementation(jest.fn());
- render(, {
- useRedux: true,
- });
+ const { queryByTestId, getByTestId } = render(
+ <>
+ (
+
+ {JSON.stringify(location.state)}
+
+ )}
+ />
+
+ >,
+ {
+ useRedux: true,
+ useRouter: true,
+ },
+ );
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
- expect(postFormSpy).toBeCalledTimes(0);
+ expect(queryByTestId('mock-sqllab-route')).not.toBeInTheDocument();
await act(async () => {
userEvent.click(screen.getByText('View in SQL Lab'));
});
- expect(postFormSpy).toBeCalledTimes(1);
+ expect(getByTestId('mock-sqllab-route')).toBeInTheDocument();
+ expect(JSON.parse(`${getByTestId('mock-sqllab-route').textContent}`)).toEqual(
+ {
+ requestedQuery: {
+ datasourceKey: `${mockDatasource.id}__${mockDatasource.type}`,
+ sql: mockDatasource.sql,
+ },
+ },
+ );
});
test('Should open a different menu when datasource=query', async () => {
@@ -261,7 +287,7 @@ test('Should open a different menu when datasource=query', async () => {
type: DatasourceType.Query,
},
};
- render();
+ render(, { useRouter: true });
expect(screen.queryByText('Query preview')).not.toBeInTheDocument();
expect(screen.queryByText('View in SQL Lab')).not.toBeInTheDocument();
@@ -284,7 +310,10 @@ test('Click on Save as dataset', async () => {
},
};
- render(, { useRedux: true });
+ render(, {
+ useRedux: true,
+ useRouter: true,
+ });
userEvent.click(screen.getByTestId('datasource-menu-trigger'));
userEvent.click(screen.getByText('Save as dataset'));
@@ -327,6 +356,7 @@ test('should set the default temporal column', async () => {
};
render(, {
useRedux: true,
+ useRouter: true,
});
await openAndSaveChanges(overrideProps.datasource);
@@ -362,6 +392,7 @@ test('should set the first available temporal column', async () => {
};
render(, {
useRedux: true,
+ useRouter: true,
});
await openAndSaveChanges(overrideProps.datasource);
@@ -397,6 +428,7 @@ test('should not set the temporal column', async () => {
};
render(, {
useRedux: true,
+ useRouter: true,
});
await openAndSaveChanges(overrideProps.datasource);
@@ -410,7 +442,7 @@ test('should not set the temporal column', async () => {
test('should show missing params state', () => {
const props = createProps({ datasource: fallbackExploreInitialData.dataset });
- render(, { useRedux: true });
+ render(, { useRedux: true, useRouter: true });
expect(screen.getByText(/missing dataset/i)).toBeVisible();
expect(screen.getByText(/missing url parameters/i)).toBeVisible();
expect(
@@ -426,7 +458,7 @@ test('should show missing dataset state', () => {
// @ts-ignore
window.location = { search: '?slice_id=152' };
const props = createProps({ datasource: fallbackExploreInitialData.dataset });
- render(, { useRedux: true });
+ render(, { useRedux: true, useRouter: true });
expect(screen.getAllByText(/missing dataset/i)).toHaveLength(2);
expect(
screen.getByText(
diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
index bf85716206b9..707138d5067c 100644
--- a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
+++ b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
@@ -20,13 +20,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import {
- DatasourceType,
- SupersetClient,
- styled,
- t,
- withTheme,
-} from '@superset-ui/core';
+import { DatasourceType, styled, t, withTheme } from '@superset-ui/core';
import { getTemporalColumns } from '@superset-ui/chart-controls';
import { getUrlParam } from 'src/utils/urlUtils';
import { AntdDropdown } from 'src/components';
@@ -50,8 +44,8 @@ import ModalTrigger from 'src/components/ModalTrigger';
import ViewQueryModalFooter from 'src/explore/components/controls/ViewQueryModalFooter';
import ViewQuery from 'src/explore/components/controls/ViewQuery';
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
-import { safeStringify } from 'src/utils/safeStringify';
import { isString } from 'lodash';
+import { Link } from 'react-router-dom';
const propTypes = {
actions: PropTypes.object.isRequired,
@@ -126,7 +120,6 @@ const Styles = styled.div`
`;
const CHANGE_DATASET = 'change_dataset';
-const VIEW_IN_SQL_LAB = 'view_in_sql_lab';
const EDIT_DATASET = 'edit_dataset';
const QUERY_PREVIEW = 'query_preview';
const SAVE_AS_DATASET = 'save_as_dataset';
@@ -238,19 +231,6 @@ class DatasourceControl extends React.PureComponent {
this.toggleEditDatasourceModal();
break;
- case VIEW_IN_SQL_LAB:
- {
- const { datasource } = this.props;
- const payload = {
- datasourceKey: `${datasource.id}__${datasource.type}`,
- sql: datasource.sql,
- };
- SupersetClient.postForm('/superset/sqllab/', {
- form_data: safeStringify(payload),
- });
- }
- break;
-
case SAVE_AS_DATASET:
this.toggleSaveDatasetModal();
break;
@@ -286,6 +266,10 @@ class DatasourceControl extends React.PureComponent {
const canAccessSqlLab = userHasPermission(user, 'SQL Lab', 'menu_access');
const editText = t('Edit dataset');
+ const requestedQuery = {
+ datasourceKey: `${datasource.id}__${datasource.type}`,
+ sql: datasource.sql,
+ };
const defaultDatasourceMenu = (
);
@@ -340,7 +333,16 @@ class DatasourceControl extends React.PureComponent {
/>
{canAccessSqlLab && (
- {t('View in SQL Lab')}
+
+
+ {t('View in SQL Lab')}
+
+
)}
{t('Save as dataset')}
diff --git a/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx b/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx
index 4f4af039b1a2..fbc87d7f9f62 100644
--- a/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx
@@ -18,8 +18,9 @@
*/
import React from 'react';
import { isObject } from 'lodash';
-import { t, SupersetClient } from '@superset-ui/core';
+import { t } from '@superset-ui/core';
import Button from 'src/components/Button';
+import { useHistory } from 'react-router-dom';
interface SimpleDataSource {
id: string;
@@ -42,12 +43,18 @@ const ViewQueryModalFooter: React.FC = (props: {
changeDatasource: () => void;
datasource: SimpleDataSource;
}) => {
+ const history = useHistory();
const viewInSQLLab = (id: string, type: string, sql: string) => {
const payload = {
datasourceKey: `${id}__${type}`,
sql,
};
- SupersetClient.postForm('/superset/sqllab/', payload);
+ history.push({
+ pathname: '/sqllab',
+ state: {
+ requestedQuery: payload,
+ },
+ });
};
const openSQL = () => {
diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
index 555b21be79c8..0c1ac5636969 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.tsx
@@ -32,6 +32,7 @@ import React, {
useReducer,
Reducer,
} from 'react';
+import { useHistory } from 'react-router-dom';
import { setItem, LocalStorageKeys } from 'src/utils/localStorageHelpers';
import { UploadChangeParam, UploadFile } from 'antd/lib/upload/interface';
import Tabs from 'src/components/Tabs';
@@ -141,7 +142,6 @@ interface DatabaseModalProps {
show: boolean;
databaseId: number | undefined; // If included, will go into edit mode
dbEngine: string | undefined; // if included goto step 2 with engine already set
- history?: any;
}
export enum ActionType {
@@ -526,7 +526,6 @@ const DatabaseModal: FunctionComponent = ({
show,
databaseId,
dbEngine,
- history,
}) => {
const [db, setDB] = useReducer<
Reducer | null, DBReducerActionType>
@@ -627,6 +626,7 @@ const DatabaseModal: FunctionComponent = ({
(DB: DatabaseObject) => DB.backend === engine || DB.engine === engine,
)?.parameters !== undefined;
const showDBError = validationErrors || dbErrors;
+ const history = useHistory();
const dbModel: DatabaseForm =
availableDbs?.databases?.find(
@@ -700,13 +700,7 @@ const DatabaseModal: FunctionComponent = ({
};
const redirectURL = (url: string) => {
- /* TODO (lyndsiWilliams): This check and passing history
- as a prop can be removed once SQL Lab is in the SPA */
- if (!isEmpty(history)) {
- history?.push(url);
- } else {
- window.location.href = url;
- }
+ history.push(url);
};
// Database import logic
@@ -1583,7 +1577,7 @@ const DatabaseModal: FunctionComponent = ({
onClick={() => {
setLoading(true);
fetchAndSetDB();
- redirectURL(`/superset/sqllab/?db=true`);
+ redirectURL(`/sqllab?db=true`);
}}
>
{t('QUERY DATA IN SQL LAB')}
diff --git a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx
index 19262c91bc20..62fdc0dfd068 100644
--- a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx
@@ -45,7 +45,9 @@ jest.mock(
describe('DatasetPanel', () => {
test('renders a blank state DatasetPanel', () => {
- render();
+ render(, {
+ useRouter: true,
+ });
const blankDatasetImg = screen.getByRole('img', { name: /empty/i });
expect(blankDatasetImg).toBeVisible();
@@ -73,6 +75,9 @@ describe('DatasetPanel', () => {
columnList={[]}
loading={false}
/>,
+ {
+ useRouter: true,
+ },
);
const blankDatasetImg = screen.getByRole('img', { name: /empty/i });
@@ -91,6 +96,9 @@ describe('DatasetPanel', () => {
columnList={[]}
loading
/>,
+ {
+ useRouter: true,
+ },
);
const blankDatasetImg = screen.getByAltText(ALT_LOADING);
@@ -107,6 +115,9 @@ describe('DatasetPanel', () => {
columnList={[]}
loading={false}
/>,
+ {
+ useRouter: true,
+ },
);
const errorTitle = screen.getByText(ERROR_TITLE);
@@ -124,6 +135,9 @@ describe('DatasetPanel', () => {
columnList={exampleColumns}
loading={false}
/>,
+ {
+ useRouter: true,
+ },
);
expect(await screen.findByText(tableName)).toBeVisible();
expect(screen.getByText(COLUMN_TITLE)).toBeVisible();
@@ -148,6 +162,9 @@ describe('DatasetPanel', () => {
loading={false}
datasets={exampleDataset}
/>,
+ {
+ useRouter: true,
+ },
);
// This is text in the info banner
diff --git a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
index 5d0ef5eda736..6824e1c50117 100644
--- a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
@@ -20,6 +20,7 @@
import React from 'react';
import { t, styled } from '@superset-ui/core';
import { EmptyStateBig } from 'src/components/EmptyState';
+import { Link } from 'react-router-dom';
const StyledContainer = styled.div`
padding: ${({ theme }) => theme.gridUnit * 8}px
@@ -50,15 +51,11 @@ export const VIEW_DATASET_MESSAGE = t(
const renderEmptyDescription = () => (
<>
{SELECT_MESSAGE}
- {
- window.location.href = `/superset/sqllab`;
- }}
- tabIndex={0}
- >
- {CREATE_MESSAGE}
-
+
+
+ {CREATE_MESSAGE}
+
+
{VIEW_DATASET_MESSAGE}
>
);
diff --git a/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx b/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx
index 66cbf6f0c44f..36278ed3dd28 100644
--- a/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx
+++ b/superset-frontend/src/features/datasets/DatasetLayout/DatasetLayout.test.tsx
@@ -35,7 +35,7 @@ jest.mock('react-router-dom', () => ({
describe('DatasetLayout', () => {
it('renders nothing when no components are passed in', () => {
- render();
+ render(, { useRouter: true });
const layoutWrapper = screen.getByTestId('dataset-layout-wrapper');
expect(layoutWrapper).toHaveTextContent('');
@@ -55,7 +55,7 @@ describe('DatasetLayout', () => {
it('renders a LeftPanel when passed in', async () => {
render(
null} />} />,
- { useRedux: true },
+ { useRedux: true, useRouter: true },
);
expect(
@@ -65,7 +65,9 @@ describe('DatasetLayout', () => {
});
it('renders a DatasetPanel when passed in', () => {
- render(} />);
+ render(} />, {
+ useRouter: true,
+ });
const blankDatasetImg = screen.getByRole('img', { name: /empty/i });
const blankDatasetTitle = screen.getByText(/select dataset source/i);
@@ -75,13 +77,16 @@ describe('DatasetLayout', () => {
});
it('renders a RightPanel when passed in', () => {
- render();
+ render(, { useRouter: true });
expect(screen.getByText(/right panel/i)).toBeVisible();
});
it('renders a Footer when passed in', () => {
- render(} />, { useRedux: true });
+ render(} />, {
+ useRedux: true,
+ useRouter: true,
+ });
expect(screen.getByText(/Cancel/i)).toBeVisible();
});
diff --git a/superset-frontend/src/features/home/ActivityTable.tsx b/superset-frontend/src/features/home/ActivityTable.tsx
index cd38c021f84c..b3f43eac5e0c 100644
--- a/superset-frontend/src/features/home/ActivityTable.tsx
+++ b/superset-frontend/src/features/home/ActivityTable.tsx
@@ -105,7 +105,7 @@ const getEntityIcon = (entity: ActivityObject) => {
};
const getEntityUrl = (entity: ActivityObject) => {
- if ('sql' in entity) return `/superset/sqllab?savedQueryId=${entity.id}`;
+ if ('sql' in entity) return `/sqllab?savedQueryId=${entity.id}`;
if ('url' in entity) return entity.url;
return entity.item_url;
};
diff --git a/superset-frontend/src/features/home/EmptyState.tsx b/superset-frontend/src/features/home/EmptyState.tsx
index 47e7817ae374..d36d1bdbd6b1 100644
--- a/superset-frontend/src/features/home/EmptyState.tsx
+++ b/superset-frontend/src/features/home/EmptyState.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import React from 'react';
+import { Link } from 'react-router-dom';
import Button from 'src/components/Button';
import { Empty } from 'src/components';
import { TableTab } from 'src/views/CRUD/types';
@@ -81,7 +82,7 @@ export default function EmptyState({
const mineRedirects: Redirects = {
[WelcomeTable.Charts]: '/chart/add',
[WelcomeTable.Dashboards]: '/dashboard/new',
- [WelcomeTable.SavedQueries]: '/superset/sqllab?new=true',
+ [WelcomeTable.SavedQueries]: '/sqllab?new=true',
};
const favRedirects: Redirects = {
[WelcomeTable.Charts]: '/chart/list',
@@ -140,20 +141,17 @@ export default function EmptyState({
>
{tableName !== WelcomeTable.Recents && (
-
+
+
+
)}
diff --git a/superset-frontend/src/features/home/Menu.test.tsx b/superset-frontend/src/features/home/Menu.test.tsx
index b40a5ab07525..428a7366f024 100644
--- a/superset-frontend/src/features/home/Menu.test.tsx
+++ b/superset-frontend/src/features/home/Menu.test.tsx
@@ -62,7 +62,7 @@ const dropdownItems = [
},
{
label: 'SQL query',
- url: '/superset/sqllab?new=true',
+ url: '/sqllab?new=true',
icon: 'fa-fw fa-search',
perm: 'can_sqllab',
view: 'Superset',
diff --git a/superset-frontend/src/features/home/RightMenu.test.tsx b/superset-frontend/src/features/home/RightMenu.test.tsx
index 95d61def4c21..97b9fb20bd59 100644
--- a/superset-frontend/src/features/home/RightMenu.test.tsx
+++ b/superset-frontend/src/features/home/RightMenu.test.tsx
@@ -73,7 +73,7 @@ const dropdownItems = [
},
{
label: 'SQL query',
- url: '/superset/sqllab?new=true',
+ url: '/sqllab?new=true',
icon: 'fa-fw fa-search',
perm: 'can_sqllab',
view: 'Superset',
diff --git a/superset-frontend/src/features/home/RightMenu.tsx b/superset-frontend/src/features/home/RightMenu.tsx
index 831ae85ba39f..b79ebb65f888 100644
--- a/superset-frontend/src/features/home/RightMenu.tsx
+++ b/superset-frontend/src/features/home/RightMenu.tsx
@@ -210,7 +210,7 @@ const RightMenu = ({
},
{
label: t('SQL query'),
- url: '/superset/sqllab?new=true',
+ url: '/sqllab?new=true',
icon: 'fa-fw fa-search',
perm: 'can_sqllab',
view: 'Superset',
diff --git a/superset-frontend/src/features/home/SavedQueries.tsx b/superset-frontend/src/features/home/SavedQueries.tsx
index 9417f03bea45..f5ac37563fa1 100644
--- a/superset-frontend/src/features/home/SavedQueries.tsx
+++ b/superset-frontend/src/features/home/SavedQueries.tsx
@@ -17,6 +17,7 @@
* under the License.
*/
import React, { useState } from 'react';
+import { Link } from 'react-router-dom';
import { styled, SupersetClient, t, useTheme } from '@superset-ui/core';
import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light';
import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql';
@@ -193,12 +194,8 @@ const SavedQueries = ({
const renderMenu = (query: Query) => (