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 = ({ )} - + ); }; diff --git a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx index 8e4372d1091be..1e1b22a81d245 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx @@ -41,6 +41,12 @@ const TabTitle = styled.span` text-transform: none; `; +const IconContainer = styled.div` + display: inline-block; + width: ${({ theme }) => theme.gridUnit * 8}px; + text-align: center; +`; + interface Props { queryEditor: QueryEditor; } @@ -91,9 +97,9 @@ const SqlEditorTabHeader: React.FC = ({ queryEditor }) => { onClick={() => actions.removeQueryEditor(qe)} data-test="close-tab-menu-option" > -
+ -
+ {t('Close tab')} = ({ queryEditor }) => { onClick={renameTab} data-test="rename-tab-menu-option" > -
+ -
+ {t('Rename tab')}
= ({ queryEditor }) => { onClick={() => actions.toggleLeftBar(qe)} data-test="toggle-menu-option" > -
+ -
+ {qe.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')}
= ({ queryEditor }) => { onClick={() => actions.removeAllOtherQueryEditors(qe)} data-test="close-all-other-menu-option" > -
+ -
+ {t('Close all other tabs')}
= ({ queryEditor }) => { onClick={() => actions.cloneQueryToNewTab(qe, false)} data-test="clone-tab-menu-option" > -
+ -
+ {t('Duplicate tab')}
diff --git a/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx b/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx index ab6348e835026..f40e946866586 100644 --- a/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx +++ b/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx @@ -17,13 +17,42 @@ * under the License. */ import React from 'react'; -import { QueryState, styled } from '@superset-ui/core'; +import { css, QueryState, styled } from '@superset-ui/core'; import Icons, { IconType } from 'src/components/Icons'; const IconContainer = styled.span` position: absolute; - top: -7px; - left: 0px; + top: -6px; + left: 1px; +`; + +const Circle = styled.div` + ${({ theme }) => css` + border-radius: 50%; + width: ${theme.gridUnit * 3}px; + height: ${theme.gridUnit * 3}px; + + display: inline-block; + background-color: ${theme.colors.grayscale.light2}; + text-align: center; + vertical-align: middle; + font-size: ${theme.typography.sizes.m}px; + font-weight: ${theme.typography.weights.bold}; + color: ${theme.colors.grayscale.light5}; + position: relative; + + &.running { + background-color: ${theme.colors.info.base}; + } + + &.success { + background-color: ${theme.colors.success.base}; + } + + &.failed { + background-color: ${theme.colors.error.base}; + } + `} `; interface TabStatusIconProps { @@ -38,12 +67,12 @@ const STATE_ICONS: Record> = { export default function TabStatusIcon({ tabState }: TabStatusIconProps) { const StatusIcon = STATE_ICONS[tabState]; return ( -
+ {StatusIcon && ( )} -
+ ); } diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx index 73ff450b3827c..105751a4d0ebd 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/TabbedSqlEditors.test.jsx @@ -212,9 +212,9 @@ describe('TabbedSqlEditors', () => { }); it('should disable new tab when offline', () => { wrapper = getWrapper(); - expect(wrapper.find(EditableTabs).props().hideAdd).toBe(false); + expect(wrapper.find('#a11y-query-editor-tabs').props().hideAdd).toBe(false); wrapper.setProps({ offline: true }); - expect(wrapper.find(EditableTabs).props().hideAdd).toBe(true); + expect(wrapper.find('#a11y-query-editor-tabs').props().hideAdd).toBe(true); }); it('should have an empty state when query editors is empty', () => { wrapper = getWrapper(); diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index ca53fed709476..43259a37eedb9 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -54,6 +54,12 @@ const defaultProps = { scheduleQueryWarning: null, }; +const StyledEditableTabs = styled(EditableTabs)` + height: 100%; + display: flex; + flex-direction: column; +`; + const StyledTab = styled.span` line-height: 24px; `; @@ -303,7 +309,7 @@ class TabbedSqlEditors extends React.PureComponent { ); return ( - {editors} {noQueryEditors && emptyTabState} - + ); } } diff --git a/superset-frontend/src/SqlLab/components/TableElement/index.tsx b/superset-frontend/src/SqlLab/components/TableElement/index.tsx index 44fbe6e1cc0cd..7d6e1ec6cd28c 100644 --- a/superset-frontend/src/SqlLab/components/TableElement/index.tsx +++ b/superset-frontend/src/SqlLab/components/TableElement/index.tsx @@ -21,7 +21,7 @@ import { useDispatch } from 'react-redux'; import Collapse from 'src/components/Collapse'; import Card from 'src/components/Card'; import ButtonGroup from 'src/components/ButtonGroup'; -import { t, styled } from '@superset-ui/core'; +import { css, t, styled } from '@superset-ui/core'; import { debounce } from 'lodash'; import { removeDataPreview, removeTables } from 'src/SqlLab/actions/sqlLab'; @@ -61,7 +61,7 @@ export interface TableElementProps { const StyledSpan = styled.span` color: ${({ theme }) => theme.colors.primary.dark1}; - &: hover { + &:hover { color: ${({ theme }) => theme.colors.primary.dark2}; } cursor: pointer; @@ -72,6 +72,39 @@ const Fade = styled.div` opacity: ${(props: { hovered: boolean }) => (props.hovered ? 1 : 0)}; `; +const StyledCollapsePanel = styled(Collapse.Panel)` + ${({ theme }) => css` + & { + .ws-el-controls { + margin-right: ${-theme.gridUnit}px; + display: flex; + } + + .header-container { + display: flex; + flex: 1; + align-items: center; + width: 100%; + + .table-name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: ${theme.typography.sizes.l}px; + flex: 1; + } + + .header-right-side { + margin-left: auto; + display: flex; + align-items: center; + margin-right: ${theme.gridUnit * 8}px; + } + } + } + `} +`; + const TableElement = ({ table, ...props }: TableElementProps) => { const dispatch = useDispatch(); @@ -287,7 +320,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => { }; return ( - { forceRender > {renderBody()} - + ); }; diff --git a/superset-frontend/src/SqlLab/hooks/useQueryEditor/index.ts b/superset-frontend/src/SqlLab/hooks/useQueryEditor/index.ts index 7044e77798fd2..7959d82b5f981 100644 --- a/superset-frontend/src/SqlLab/hooks/useQueryEditor/index.ts +++ b/superset-frontend/src/SqlLab/hooks/useQueryEditor/index.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { useMemo } from 'react'; import pick from 'lodash/pick'; import { shallowEqual, useSelector } from 'react-redux'; import { SqlLabRootState, QueryEditor } from 'src/SqlLab/types'; @@ -24,7 +25,10 @@ export default function useQueryEditor( sqlEditorId: string, attributes: ReadonlyArray, ) { - return useSelector>( + const queryEditor = useSelector< + SqlLabRootState, + Pick + >( ({ sqlLab: { unsavedQueryEditor, queryEditors } }) => pick( { @@ -32,7 +36,32 @@ export default function useQueryEditor( ...(sqlEditorId === unsavedQueryEditor.id && unsavedQueryEditor), }, ['id'].concat(attributes), - ) as Pick, + ) as Pick, shallowEqual, ); + const { schema, schemaOptions } = useSelector< + SqlLabRootState, + Pick + >( + ({ sqlLab: { unsavedQueryEditor, queryEditors } }) => + pick( + { + ...queryEditors.find(({ id }) => id === sqlEditorId), + ...(sqlEditorId === unsavedQueryEditor.id && unsavedQueryEditor), + }, + ['schema', 'schemaOptions'], + ) as Pick, + shallowEqual, + ); + + const schemaOptionsMap = useMemo( + () => new Set(schemaOptions?.map(({ value }) => value)), + [schemaOptions], + ); + + if ('schema' in queryEditor && schema && !schemaOptionsMap.has(schema)) { + delete queryEditor.schema; + } + + return queryEditor as Pick; } diff --git a/superset-frontend/src/SqlLab/hooks/useQueryEditor/useQueryEditor.test.ts b/superset-frontend/src/SqlLab/hooks/useQueryEditor/useQueryEditor.test.ts index 23de4d68226cf..e8db4bd361fbd 100644 --- a/superset-frontend/src/SqlLab/hooks/useQueryEditor/useQueryEditor.test.ts +++ b/superset-frontend/src/SqlLab/hooks/useQueryEditor/useQueryEditor.test.ts @@ -70,7 +70,7 @@ test('includes id implicitly', () => { test('returns updated values from unsaved change', () => { const expectedSql = 'SELECT updated_column\nFROM updated_table\nWHERE'; const { result } = renderHook( - () => useQueryEditor(defaultQueryEditor.id, ['id', 'sql']), + () => useQueryEditor(defaultQueryEditor.id, ['id', 'sql', 'schema']), { wrapper: createWrapper({ useRedux: true, @@ -88,5 +88,31 @@ test('returns updated values from unsaved change', () => { }, ); expect(result.current.id).toEqual(defaultQueryEditor.id); + expect(result.current.schema).toEqual(defaultQueryEditor.schema); expect(result.current.sql).toEqual(expectedSql); }); + +test('skips the deprecated schema option', () => { + const expectedSql = 'SELECT updated_column\nFROM updated_table\nWHERE'; + const { result } = renderHook( + () => useQueryEditor(defaultQueryEditor.id, ['schema']), + { + wrapper: createWrapper({ + useRedux: true, + store: mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + unsavedQueryEditor: { + id: defaultQueryEditor.id, + sql: expectedSql, + schema: 'deprecated schema', + }, + }, + }), + }), + }, + ); + expect(result.current.schema).not.toEqual(defaultQueryEditor.schema); + expect(result.current.schema).toBeUndefined(); +}); diff --git a/superset-frontend/src/SqlLab/main.less b/superset-frontend/src/SqlLab/main.less deleted file mode 100644 index aa75c0ec00b63..0000000000000 --- a/superset-frontend/src/SqlLab/main.less +++ /dev/null @@ -1,491 +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 '../assets/stylesheets/less/variables.less'; - -body { - min-height: ~'max(100vh, 500px)'; // Set a min height so the gutter is always visible when resizing - overflow: hidden; -} - -.inlineBlock { - display: inline-block; -} - -.valignTop { - vertical-align: top; -} - -.inline { - display: inline; -} - -.nopadding { - padding: 0px; -} - -.pane-cell { - padding: 10px; - overflow: auto; - width: 100%; - height: 100%; -} - -.ant-tabs-content-holder { - /* This is needed for Safari */ - height: 100%; -} - -.ant-tabs-content { - height: 100%; - position: relative; - background-color: @lightest; - overflow-x: auto; - overflow-y: auto; - - > .ant-tabs-tabpane { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} - -.Workspace .btn-sm { - box-shadow: 1px 1px 2px fade(@darkest, @opacity-light); - margin-top: 2px; - padding: 4px; -} - -.Workspace hr { - margin-top: 10px; - margin-bottom: 10px; -} - -div.Workspace { - height: 100%; - margin: 0px; -} - -.padded { - padding: 10px; -} - -.p-t-10 { - padding-top: 10px; -} - -.p-t-5 { - padding-top: 5px; -} - -.m-r-5 { - margin-right: 5px; -} - -.m-r-3 { - margin-right: 3px; -} - -.m-l-1 { - margin-left: 1px; -} - -.m-l-2 { - margin-left: 2px; -} - -.m-r-10 { - margin-right: 10px; -} - -.m-l-10 { - margin-left: 10px; -} - -.m-l-5 { - margin-left: 5px; -} - -.m-b-10 { - margin-bottom: 10px; -} - -.m-t-5 { - margin-top: 5px; -} - -.m-t-10 { - margin-top: 10px; -} - -.p-t-10 { - padding-top: 10px; -} - -.no-shadow { - box-shadow: none; - background-color: transparent; -} - -.pane-west { - height: 100%; - overflow: auto; -} - -.circle { - @circle-diameter: 10px; - border-radius: (@circle-diameter / 2); - width: @circle-diameter; - height: @circle-diameter; - - display: inline-block; - background-color: @gray-light; - text-align: center; - vertical-align: middle; - font-size: @font-size-m; - font-weight: @font-weight-bold; - color: @lightest; - position: relative; -} - -.running { - background-color: @info; -} - -.success { - background-color: @success; -} - -.failed { - background-color: @danger; -} - -.handle { - cursor: move; -} - -#a11y-query-editor-tabs { - height: 100%; - display: flex; - flex-direction: column; -} - -.SqlLab { - position: absolute; - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; - padding: 0 10px; - - pre { - padding: 0px !important; - margin: 0px; - border: none; - font-size: @font-size-s; - background-color: transparent !important; - } - - .north-pane { - display: flex; - flex-direction: column; - } - - #ace-editor { - height: calc(100% - 51px); - flex-grow: 1; - } - - .ace_content { - height: 100%; - } -} - -.SqlEditorTabs li { - a:focus { - outline: 0; - } - - .ddbtn-tab { - font-size: inherit; - color: black; - - &:active { - background: none; - } - - svg { - vertical-align: middle; - } - } - - .dropdown.btn-group.btn-group-sm { - width: 3px; - height: 3px; - border-radius: 1.5px; - background: #bababa; - margin-right: 8px; - font-weight: @font-weight-normal; - display: inline-flex; - - &:hover { - background-color: @primary-color; - - &:before, - &:after { - background-color: @primary-color; - } - } - - &:before, - &:after { - position: absolute; - content: ' '; - width: 3px; - height: 3px; - border-radius: 1.5px; - background-color: #bababa; - } - &:before { - transform: translateY(-5px); - } - &:after { - transform: translateY(5px); - } - } - - ul.dropdown-menu { - margin-top: 10px; - } - - .dropdown-toggle { - padding-top: 2px; - } -} - -.SqlEditor { - display: flex; - flex-direction: row; - height: 100%; - - .schemaPane { - transition: transform @timing-normal ease-in-out; - } - - .queryPane { - flex: 1 1 auto; - padding: 10px; - overflow-y: none; - overflow-x: scroll; - } - - .schemaPane-enter-done, - .schemaPane-exit { - transform: translateX(0); - z-index: 7; - } - - .schemaPane-exit-active { - transform: translateX(-120%); - } - - .schemaPane-enter-active { - transform: translateX(0); - max-width: 300px; - } - - .schemaPane-enter, - .schemaPane-exit-done { - max-width: 0; - transform: translateX(-120%); - overflow: hidden; - } - - .schemaPane-exit-done + .queryPane { - margin-left: 0; - } - - .gutter { - border-top: 1px solid @gray-light; - border-bottom: 1px solid @gray-light; - width: 3%; - margin: 3px 47%; - } - - .gutter.gutter-vertical { - cursor: row-resize; - } -} - -.SqlEditorLeftBar { - height: 100%; - display: flex; - flex-direction: column; - - .divider { - border-bottom: 1px solid @gray-bg; - margin: 15px 0; - } -} - -.popover { - max-width: 400px; -} - -.table-label { - margin-top: 5px; - margin-right: 10px; - float: left; -} - -div.tablePopover { - opacity: 0.7 !important; - - &:hover { - opacity: 1 !important; - } -} - -.ace_editor.ace_editor { - //double class is better than !important - border: 1px solid @gray-light; - font-feature-settings: @font-feature-settings; - // 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: 520px !important; - } -} - -.Select__menu-outer { - min-width: 100%; - width: inherit; - z-index: @z-index-dropdown; -} - -.Select__clear-indicator { - margin-top: -2px; -} - -.Select__arrow { - margin-top: 5px; -} - -.ace_scroller { - background-color: @gray-bg; -} - -.TableElement { - .well { - margin-top: 5px; - margin-bottom: 5px; - padding: 5px 10px; - } - - .ws-el-controls { - margin-right: -0.3em; - display: flex; - } - - .header-container { - display: flex; - flex: 1; - align-items: center; - width: 100%; - - .table-name { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-size: 16px; - flex: 1; - } - - .header-right-side { - margin-left: auto; - display: flex; - align-items: center; - margin-right: 33px; - } - } -} - -.QueryTable .label { - display: inline-block; -} - -.QueryTable .ant-btn { - position: static; -} - -.ResultsModal .ant-modal-body { - min-height: 560px; -} - -.ant-modal-body { - overflow: auto; -} - -a.Link { - cursor: pointer; -} - -.QueryTable .well { - padding: 3px 5px; - margin: 3px 5px; -} - -.nav-tabs .ddbtn-tab { - padding: 0; - border: none; - background: none; - position: relative; - top: 2px; - - &:focus { - outline: 0; - } - - &:active { - box-shadow: none; - } -} - -.icon-container { - display: inline-block; - width: 30px; - text-align: center; -} - -.search-date-filter-container { - display: flex; - - .Select { - margin-right: 3px; - } -} - -.cost-estimate { - font-size: @font-size-s; -} diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index 7317ef0789d5a..14f21ee68a3b4 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -35,7 +35,7 @@ export interface QueryEditor { id: string; dbId?: number; name: string; - schema: string; + schema?: string; autorun: boolean; sql: string; remoteId: number | null; diff --git a/superset-frontend/src/assets/stylesheets/superset.less b/superset-frontend/src/assets/stylesheets/superset.less index 5808d0144bc73..39bd719d272d3 100644 --- a/superset-frontend/src/assets/stylesheets/superset.less +++ b/superset-frontend/src/assets/stylesheets/superset.less @@ -42,10 +42,6 @@ input.form-control { background-color: @lightest; } -.chart-header a.danger { - color: @danger; -} - .disabledButton { pointer-events: none; } @@ -165,16 +161,6 @@ img.viz-thumb-option { max-height: 700px; } -.chart-header .header-text { - font-size: @font-size-xl; - line-height: 22px; - padding-bottom: 8px; - border-bottom: 1px solid @gray; - margin-top: 10px; - margin-left: 10px; - margin-right: 10px; -} - #is_cached { display: none; } @@ -327,6 +313,10 @@ table.table-no-hover tr:hover { margin-bottom: 10px; } +.m-l-2 { + margin-left: 2px; +} + .m-l-4 { margin-left: 4px; } diff --git a/superset-frontend/src/components/Chart/Chart.jsx b/superset-frontend/src/components/Chart/Chart.jsx index 8be36994505a7..aac44a5186c57 100644 --- a/superset-frontend/src/components/Chart/Chart.jsx +++ b/superset-frontend/src/components/Chart/Chart.jsx @@ -113,6 +113,10 @@ const Styles = styled.div` .pivot_table tbody tr { font-feature-settings: 'tnum' 1; } + + .alert { + margin: ${({ theme }) => theme.gridUnit * 2}px; + } } `; diff --git a/superset-frontend/src/components/Select/Select.tsx b/superset-frontend/src/components/Select/Select.tsx index 70ca4e6903342..ac119423c88a7 100644 --- a/superset-frontend/src/components/Select/Select.tsx +++ b/superset-frontend/src/components/Select/Select.tsx @@ -467,6 +467,7 @@ const Select = forwardRef( {selectAllEnabled && (