diff --git a/setup.cfg b/setup.cfg
index c4cd568c51160..a9470d51bd8b0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -17,9 +17,9 @@
[metadata]
name = Superset
summary = a data exploration platform
-description-file = README.md
+description_file = README.md
author = Apache Superset Dev
-author-email = dev@superset.apache.org
+author_email = dev@superset.apache.org
license = Apache License, Version 2.0
[files]
diff --git a/superset-frontend/.storybook/preview.jsx b/superset-frontend/.storybook/preview.jsx
index a83b6e102e500..967e96a40ddc3 100644
--- a/superset-frontend/.storybook/preview.jsx
+++ b/superset-frontend/.storybook/preview.jsx
@@ -87,5 +87,5 @@ addParameters({
],
},
},
- controls: { expanded: true },
+ controls: { expanded: true, sort: 'alpha' },
});
diff --git a/superset-frontend/cypress-base/cypress/support/directories.ts b/superset-frontend/cypress-base/cypress/support/directories.ts
index fde9ee0cdeacf..d74aef607d76a 100644
--- a/superset-frontend/cypress-base/cypress/support/directories.ts
+++ b/superset-frontend/cypress-base/cypress/support/directories.ts
@@ -127,10 +127,11 @@ export const databasesPage = {
export const sqlLabView = {
sqlEditorLeftBar: {
- sqlEditorLeftBar: '[class="SqlEditorLeftBar"]',
- databaseSchemaTableSection: '[class="SqlEditorLeftBar"] > :nth-child(1)',
+ sqlEditorLeftBar: '[data-test="sql-editor-left-bar"]',
+ databaseSchemaTableSection:
+ '[data-test="sql-editor-left-bar"] > :nth-child(1)',
tableSchemaSection:
- '[class="SqlEditorLeftBar"] > :nth-child(1) > :nth-child(3) > :nth-child(1)',
+ '[data-test="sql-editor-left-bar"] > :nth-child(1) > :nth-child(3) > :nth-child(1)',
tableSchemaInputEmpty: '[aria-label="Select table or type table name"]',
},
databaseInput: '[data-test=DatabaseSelector] > :nth-child(1)',
diff --git a/superset-frontend/src/SqlLab/App.jsx b/superset-frontend/src/SqlLab/App.jsx
index 4a300958031d7..a3a939aabd3b6 100644
--- a/superset-frontend/src/SqlLab/App.jsx
+++ b/superset-frontend/src/SqlLab/App.jsx
@@ -42,9 +42,9 @@ import {
import { BYTES_PER_CHAR, KB_STORAGE } from './constants';
import setupApp from '../setup/setupApp';
-import './main.less';
import '../assets/stylesheets/reactable-pagination.less';
import { theme } from '../preamble';
+import { SqlLabGlobalStyles } from './SqlLabGlobalStyles';
setupApp();
setupExtensions();
@@ -141,6 +141,7 @@ const Application = () => (
+
diff --git a/superset-frontend/src/dashboard/stylesheets/index.less b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx
similarity index 66%
rename from superset-frontend/src/dashboard/stylesheets/index.less
rename to superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx
index 0b11c2bec8061..f1398e3be9ac4 100644
--- a/superset-frontend/src/dashboard/stylesheets/index.less
+++ b/superset-frontend/src/SqlLab/SqlLabGlobalStyles.tsx
@@ -16,13 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-@import '../../assets/stylesheets/less/variables.less';
-@import './builder.less';
-@import './dashboard.less';
-@import './dnd.less';
-@import './filter-scope-selector.less';
-@import './grid.less';
-@import './popover-menu.less';
-@import './resizable.less';
-@import './components/index.less';
+import React from 'react';
+import { Global } from '@emotion/react';
+import { css } from '@superset-ui/core';
+
+export const SqlLabGlobalStyles = () => (
+ css`
+ body {
+ min-height: max(
+ 100vh,
+ ${theme.gridUnit * 125}px
+ ); // Set a min height so the gutter is always visible when resizing
+ overflow: hidden;
+ }
+ `}
+ />
+);
diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.js b/superset-frontend/src/SqlLab/actions/sqlLab.js
index d6447e808c607..a2bdd65d5c03e 100644
--- a/superset-frontend/src/SqlLab/actions/sqlLab.js
+++ b/superset-frontend/src/SqlLab/actions/sqlLab.js
@@ -128,10 +128,22 @@ export function getUpToDateQuery(rootState, queryEditor, key) {
sqlLab: { unsavedQueryEditor },
} = rootState;
const id = key ?? queryEditor.id;
- return {
+ const updatedQueryEditor = {
...queryEditor,
...(id === unsavedQueryEditor.id && unsavedQueryEditor),
};
+
+ if (
+ 'schema' in updatedQueryEditor &&
+ !updatedQueryEditor.schemaOptions?.find(
+ ({ value }) => value === updatedQueryEditor.schema,
+ )
+ ) {
+ // remove the deprecated schema option
+ delete updatedQueryEditor.schema;
+ }
+
+ return updatedQueryEditor;
}
export function resetState() {
diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.test.js b/superset-frontend/src/SqlLab/actions/sqlLab.test.js
index fb6ff470b40f0..ca1e68b5b2879 100644
--- a/superset-frontend/src/SqlLab/actions/sqlLab.test.js
+++ b/superset-frontend/src/SqlLab/actions/sqlLab.test.js
@@ -316,6 +316,54 @@ describe('async actions', () => {
});
});
+ describe('getUpToDateQuery', () => {
+ const id = 'randomidvalue2';
+ const unsavedQueryEditor = {
+ id,
+ sql: 'some query here',
+ schemaOptions: [{ value: 'testSchema' }, { value: 'schema2' }],
+ };
+
+ it('returns payload with the updated queryEditor', () => {
+ const queryEditor = {
+ id,
+ name: 'Dummy query editor',
+ schema: 'testSchema',
+ };
+ const state = {
+ sqlLab: {
+ tabHistory: [id],
+ queryEditors: [queryEditor],
+ unsavedQueryEditor,
+ },
+ };
+ expect(actions.getUpToDateQuery(state, queryEditor)).toEqual({
+ ...queryEditor,
+ ...unsavedQueryEditor,
+ });
+ });
+
+ it('ignores the deprecated schema option', () => {
+ const queryEditor = {
+ id,
+ name: 'Dummy query editor',
+ schema: 'oldSchema',
+ };
+ const state = {
+ sqlLab: {
+ tabHistory: [id],
+ queryEditors: [queryEditor],
+ unsavedQueryEditor,
+ },
+ };
+ expect(actions.getUpToDateQuery(state, queryEditor)).toEqual({
+ ...queryEditor,
+ ...unsavedQueryEditor,
+ schema: undefined,
+ });
+ });
+ });
+
describe('postStopQuery', () => {
const stopQueryEndpoint = 'glob:*/api/v1/query/stop';
fetchMock.post(stopQueryEndpoint, {});
diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
index e241a121dbee8..0dd3385ea57e6 100644
--- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
+++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
@@ -18,6 +18,8 @@
*/
import React, { useState, useEffect, useRef } from 'react';
import { useDispatch } from 'react-redux';
+import { css, styled } from '@superset-ui/core';
+
import { usePrevious } from 'src/hooks/usePrevious';
import { areArraysShallowEqual } from 'src/reduxUtils';
import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
@@ -57,6 +59,28 @@ type AceEditorWrapperProps = {
hotkeys: HotKey[];
};
+const StyledAceEditor = styled(AceEditor)`
+ ${({ theme }) => css`
+ && {
+ // double class is better than !important
+ border: 1px solid ${theme.colors.grayscale.light2};
+ font-feature-settings: 'liga' off, 'calt' off;
+ // Fira Code causes problem with Ace under Firefox
+ font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono',
+ 'source-code-pro', 'Lucida Console', monospace;
+
+ &.ace_autocomplete {
+ // Use !important because Ace Editor applies extra CSS at the last second
+ // when opening the autocomplete.
+ width: ${theme.gridUnit * 130}px !important;
+ }
+
+ .ace_scroller {
+ background-color: ${theme.colors.grayscale.light4};
+ }
+ }
+ `}
+`;
const AceEditorWrapper = ({
autocomplete,
onBlur = () => {},
@@ -258,7 +282,7 @@ const AceEditorWrapper = ({
};
return (
- css`
+ &.SqlLab {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 0 ${theme.gridUnit * 2}px;
+
+ pre {
+ padding: 0 !important;
+ margin: 0;
+ border: none;
+ font-size: ${theme.typography.sizes.s}px;
+ background: transparent !important;
+ }
+
+ .north-pane {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .ace_editor {
+ flex-grow: 1;
+ }
+
+ .ace_content {
+ height: 100%;
+ }
+
+ .ant-tabs-content-holder {
+ /* This is needed for Safari */
+ height: 100%;
+ }
+
+ .ant-tabs-content {
+ height: 100%;
+ position: relative;
+ background-color: ${theme.colors.grayscale.light5};
+ overflow-x: auto;
+ overflow-y: auto;
+
+ > .ant-tabs-tabpane {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+ }
+
+ .ResultsModal .ant-modal-body {
+ min-height: ${theme.gridUnit * 140}px;
+ }
+
+ .ant-modal-body {
+ overflow: auto;
+ }
+ }
+ `};
+`;
+
class App extends React.PureComponent {
constructor(props) {
super(props);
@@ -99,7 +162,7 @@ class App extends React.PureComponent {
return window.location.replace('/superset/sqllab/history/');
}
return (
-
+
-
+
);
}
}
diff --git a/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx b/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx
index c0c92e3a5549a..4dd5c489582a6 100644
--- a/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx
+++ b/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/index.tsx
@@ -18,7 +18,7 @@
*/
import React, { useMemo } from 'react';
import { useSelector } from 'react-redux';
-import { t } from '@superset-ui/core';
+import { css, styled, t } from '@superset-ui/core';
import Alert from 'src/components/Alert';
import TableView from 'src/components/TableView';
@@ -36,6 +36,12 @@ export interface EstimateQueryCostButtonProps {
disabled?: boolean;
}
+const CostEstimateModalStyles = styled.div`
+ ${({ theme }) => css`
+ font-size: ${theme.typography.sizes.s};
+ `}
+`;
+
const EstimateQueryCostButton = ({
getEstimate,
queryEditorId,
@@ -76,13 +82,14 @@ const EstimateQueryCostButton = ({
}
if (queryCostEstimate?.completed) {
return (
-
+
+
+
);
}
return ;
diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx
index a53225d96abcd..a14e08a9fe722 100644
--- a/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx
+++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/QueryStateLabel.test.jsx
@@ -17,8 +17,7 @@
* under the License.
*/
import React from 'react';
-import { shallow } from 'enzyme';
-
+import { styledMount as mount } from 'spec/helpers/theming';
import Label from 'src/components/Label';
import QueryStateLabel from 'src/SqlLab/components/QueryStateLabel';
@@ -34,7 +33,7 @@ describe('SavedQuery', () => {
);
});
it('has an Overlay and a Popover', () => {
- const wrapper = shallow();
+ const wrapper = mount();
expect(wrapper.find(Label)).toExist();
});
});
diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx
index 6168a2af713a4..4a8c581a82b1a 100644
--- a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx
@@ -19,16 +19,18 @@
import React from 'react';
import Label from 'src/components/Label';
import { STATE_TYPE_MAP } from 'src/SqlLab/constants';
-import { Query } from '@superset-ui/core';
+import { styled, Query } from '@superset-ui/core';
interface QueryStateLabelProps {
query: Query;
}
+const StyledLabel = styled(Label)`
+ margin-right: ${({ theme }) => theme.gridUnit}px;
+`;
+
export default function QueryStateLabel({ query }: QueryStateLabelProps) {
return (
-
+ {query.state}
);
}
diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
index 10cdd8a39e29f..81a4e47a11368 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
@@ -22,7 +22,13 @@ import ButtonGroup from 'src/components/ButtonGroup';
import Alert from 'src/components/Alert';
import Button from 'src/components/Button';
import shortid from 'shortid';
-import { QueryResponse, QueryState, styled, t } from '@superset-ui/core';
+import {
+ QueryResponse,
+ QueryState,
+ styled,
+ t,
+ useTheme,
+} from '@superset-ui/core';
import { usePrevious } from 'src/hooks/usePrevious';
import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace';
import {
@@ -133,6 +139,7 @@ const ResultSet = ({
user,
defaultQueryLimit,
}: ResultSetProps) => {
+ const theme = useTheme();
const [searchText, setSearchText] = useState('');
const [cachedData, setCachedData] = useState[]>([]);
const [showSaveDatasetModal, setShowSaveDatasetModal] = useState(false);
@@ -449,7 +456,7 @@ const ResultSet = ({
{loadTables && !refresh && Loader(TABLE_LOADING_TEXT)}
- {dataset?.schema && !loadTables && !tableOptions.length && !searchVal && (
+ {schema && !loadTables && !tableOptions.length && !searchVal && (
)}
- {dataset?.schema && (tableOptions.length > 0 || searchVal.length > 0) && (
+ {schema && (tableOptions.length > 0 || searchVal.length > 0) && (
<>