diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx index 3e8ee84f6c500..c48594d304841 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx +++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx @@ -18,15 +18,16 @@ */ /* eslint-disable jsx-a11y/anchor-is-valid */ /* eslint-disable jsx-a11y/no-static-element-interactions */ -import React, { useState, useEffect, useMemo, useRef } from 'react'; +import React from 'react'; import { CSSTransition } from 'react-transition-group'; -import { useDispatch, useSelector } from 'react-redux'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import PropTypes from 'prop-types'; import Split from 'react-split'; -import { t, styled, useTheme } from '@superset-ui/core'; +import { t, styled, withTheme } from '@superset-ui/core'; import debounce from 'lodash/debounce'; import throttle from 'lodash/throttle'; -import Modal from 'src/components/Modal'; +import StyledModal from 'src/components/Modal'; import Mousetrap from 'mousetrap'; import Button from 'src/components/Button'; import Timer from 'src/components/Timer'; @@ -47,6 +48,7 @@ import { queryEditorSetAndSaveSql, queryEditorSetTemplateParams, runQueryFromSqlEditor, + runQuery, saveQuery, addSavedQueryToTabState, scheduleQuery, @@ -60,12 +62,6 @@ import { SQL_EDITOR_GUTTER_MARGIN, SQL_TOOLBAR_HEIGHT, SQL_EDITOR_LEFTBAR_WIDTH, - SQL_EDITOR_PADDING, - INITIAL_NORTH_PERCENT, - INITIAL_SOUTH_PERCENT, - SET_QUERY_EDITOR_SQL_DEBOUNCE_MS, - VALIDATION_DEBOUNCE_MS, - WINDOW_RESIZE_THROTTLE_MS, } from 'src/SqlLab/constants'; import { getItem, @@ -87,6 +83,13 @@ import RunQueryActionButton from '../RunQueryActionButton'; import { newQueryTabName } from '../../utils/newQueryTabName'; import QueryLimitSelect from '../QueryLimitSelect'; +const SQL_EDITOR_PADDING = 10; +const INITIAL_NORTH_PERCENT = 30; +const INITIAL_SOUTH_PERCENT = 70; +const SET_QUERY_EDITOR_SQL_DEBOUNCE_MS = 2000; +const VALIDATION_DEBOUNCE_MS = 600; +const WINDOW_RESIZE_THROTTLE_MS = 100; + const appContainer = document.getElementById('app'); const bootstrapData = JSON.parse( appContainer.getAttribute('data-bootstrap') || '{}', @@ -129,7 +132,7 @@ const StyledToolbar = styled.div` const StyledSidebar = styled.div` flex: 0 0 ${({ width }) => width}px; width: ${({ width }) => width}px; - padding: ${({ theme, hide }) => (hide ? 0 : theme.gridUnit * 2.5)}px; + padding: ${({ hide }) => (hide ? 0 : 10)}px; border-right: 1px solid ${({ theme, hide }) => hide ? 'transparent' : theme.colors.grayscale.light2}; @@ -137,10 +140,13 @@ const StyledSidebar = styled.div` const propTypes = { actions: PropTypes.object.isRequired, + database: PropTypes.object, + latestQuery: PropTypes.object, tables: PropTypes.array.isRequired, editorQueries: PropTypes.array.isRequired, dataPreviewQueries: PropTypes.array.isRequired, queryEditor: PropTypes.object.isRequired, + hideLeftBar: PropTypes.bool, defaultQueryLimit: PropTypes.number.isRequired, maxRow: PropTypes.number.isRequired, displayLimit: PropTypes.number.isRequired, @@ -148,102 +154,158 @@ const propTypes = { scheduleQueryWarning: PropTypes.string, }; -const SqlEditor = ({ - actions, - tables, - editorQueries, - dataPreviewQueries, - queryEditor, - defaultQueryLimit, - maxRow, - displayLimit, - saveQueryWarning, - scheduleQueryWarning = null, -}) => { - const theme = useTheme(); - const dispatch = useDispatch(); - - const { currentQueryEditor, database, latestQuery, hideLeftBar } = - useSelector(({ sqlLab: { unsavedQueryEditor, databases, queries } }) => { - const currentQueryEditor = { - ...queryEditor, - ...(queryEditor.id === unsavedQueryEditor.id && unsavedQueryEditor), - }; +const defaultProps = { + database: null, + latestQuery: null, + hideLeftBar: false, + scheduleQueryWarning: null, +}; - let { dbId, latestQueryId, hideLeftBar } = queryEditor; - if (unsavedQueryEditor.id === queryEditor.id) { - dbId = unsavedQueryEditor.dbId || dbId; - latestQueryId = unsavedQueryEditor.latestQueryId || latestQueryId; - hideLeftBar = unsavedQueryEditor.hideLeftBar || hideLeftBar; - } - return { - currentQueryEditor, - database: databases[dbId], - latestQuery: queries[latestQueryId], - hideLeftBar, - }; +class SqlEditor extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + autorun: props.queryEditor.autorun, + ctas: '', + northPercent: props.queryEditor.northPercent || INITIAL_NORTH_PERCENT, + southPercent: props.queryEditor.southPercent || INITIAL_SOUTH_PERCENT, + autocompleteEnabled: getItem( + LocalStorageKeys.sqllab__is_autocomplete_enabled, + true, + ), + showCreateAsModal: false, + createAs: '', + showEmptyState: false, + }; + this.sqlEditorRef = React.createRef(); + this.northPaneRef = React.createRef(); + + this.elementStyle = this.elementStyle.bind(this); + this.onResizeStart = this.onResizeStart.bind(this); + this.onResizeEnd = this.onResizeEnd.bind(this); + this.canValidateQuery = this.canValidateQuery.bind(this); + this.runQuery = this.runQuery.bind(this); + this.setEmptyState = this.setEmptyState.bind(this); + this.stopQuery = this.stopQuery.bind(this); + this.saveQuery = this.saveQuery.bind(this); + this.onSqlChanged = this.onSqlChanged.bind(this); + this.setQueryEditorAndSaveSql = this.setQueryEditorAndSaveSql.bind(this); + this.setQueryEditorAndSaveSqlWithDebounce = debounce( + this.setQueryEditorAndSaveSql.bind(this), + SET_QUERY_EDITOR_SQL_DEBOUNCE_MS, + ); + this.queryPane = this.queryPane.bind(this); + this.getHotkeyConfig = this.getHotkeyConfig.bind(this); + this.getAceEditorAndSouthPaneHeights = + this.getAceEditorAndSouthPaneHeights.bind(this); + this.getSqlEditorHeight = this.getSqlEditorHeight.bind(this); + this.requestValidation = debounce( + this.requestValidation.bind(this), + VALIDATION_DEBOUNCE_MS, + ); + this.getQueryCostEstimate = this.getQueryCostEstimate.bind(this); + this.handleWindowResize = throttle( + this.handleWindowResize.bind(this), + WINDOW_RESIZE_THROTTLE_MS, + ); + + this.onBeforeUnload = this.onBeforeUnload.bind(this); + this.renderDropdown = this.renderDropdown.bind(this); + } + + UNSAFE_componentWillMount() { + if (this.state.autorun) { + this.setState({ autorun: false }); + this.props.queryEditorSetAutorun(this.props.queryEditor, false); + this.startQuery(); + } + } + + componentDidMount() { + // We need to measure the height of the sql editor post render to figure the height of + // the south pane so it gets rendered properly + // eslint-disable-next-line react/no-did-mount-set-state + const db = this.props.database; + this.setState({ height: this.getSqlEditorHeight() }); + if (!db || isEmpty(db)) { + this.setEmptyState(true); + } + + window.addEventListener('resize', this.handleWindowResize); + window.addEventListener('beforeunload', this.onBeforeUnload); + + // setup hotkeys + const hotkeys = this.getHotkeyConfig(); + hotkeys.forEach(keyConfig => { + Mousetrap.bind([keyConfig.key], keyConfig.func); }); + } - const queryEditors = useSelector(({ sqlLab }) => sqlLab.queryEditors); + componentWillUnmount() { + window.removeEventListener('resize', this.handleWindowResize); + window.removeEventListener('beforeunload', this.onBeforeUnload); + } - const [height, setHeight] = useState(0); - const [autorun, setAutorun] = useState(queryEditor.autorun); - const [ctas, setCtas] = useState(''); - const [northPercent, setNorthPercent] = useState( - queryEditor.northPercent || INITIAL_NORTH_PERCENT, - ); - const [southPercent, setSouthPercent] = useState( - queryEditor.southPercent || INITIAL_SOUTH_PERCENT, - ); - const [autocompleteEnabled, setAutocompleteEnabled] = useState( - getItem(LocalStorageKeys.sqllab__is_autocomplete_enabled, true), - ); - const [showCreateAsModal, setShowCreateAsModal] = useState(false); - const [createAs, setCreateAs] = useState(''); - const [showEmptyState, setShowEmptyState] = useState(false); + onResizeStart() { + // Set the heights on the ace editor and the ace content area after drag starts + // to smooth out the visual transition to the new heights when drag ends + document.getElementsByClassName('ace_content')[0].style.height = '100%'; + } - const sqlEditorRef = useRef(null); - const northPaneRef = useRef(null); + onResizeEnd([northPercent, southPercent]) { + this.setState({ northPercent, southPercent }); - const startQuery = (ctasArg = false, ctas_method = CtasEnum.TABLE) => { - if (!database) { - return; + if (this.northPaneRef.current && this.northPaneRef.current.clientHeight) { + this.props.persistEditorHeight( + this.props.queryEditor, + northPercent, + southPercent, + ); } + } - dispatch( - runQueryFromSqlEditor( - database, - queryEditor, - defaultQueryLimit, - ctasArg ? ctas : '', - ctasArg, - ctas_method, - ), - ); - dispatch(setActiveSouthPaneTab('Results')); - }; - - const stopQuery = () => { - if (latestQuery && ['running', 'pending'].indexOf(latestQuery.state) >= 0) { - dispatch(postStopQuery(latestQuery)); + onBeforeUnload(event) { + if ( + this.props.database?.extra_json?.cancel_query_on_windows_unload && + this.props.latestQuery?.state === 'running' + ) { + event.preventDefault(); + this.stopQuery(); } - }; + } - useState(() => { - if (autorun) { - setAutorun(false); - dispatch(queryEditorSetAutorun(queryEditor, false)); - startQuery(); + onSqlChanged(sql) { + this.props.queryEditorSetSql(this.props.queryEditor, sql); + this.setQueryEditorAndSaveSqlWithDebounce(sql); + // Request server-side validation of the query text + if (this.canValidateQuery()) { + // NB. requestValidation is debounced + this.requestValidation(sql); } - }); + } // One layer of abstraction for easy spying in unit tests - const getSqlEditorHeight = () => - sqlEditorRef.current - ? sqlEditorRef.current.clientHeight - SQL_EDITOR_PADDING * 2 + getSqlEditorHeight() { + return this.sqlEditorRef.current + ? this.sqlEditorRef.current.clientHeight - SQL_EDITOR_PADDING * 2 : 0; + } - const getHotkeyConfig = () => { + // Return the heights for the ace editor and the south pane as an object + // given the height of the sql editor, north pane percent and south pane percent. + getAceEditorAndSouthPaneHeights(height, northPercent, southPercent) { + return { + aceEditorHeight: + (height * northPercent) / 100 - + (SQL_EDITOR_GUTTER_HEIGHT / 2 + SQL_EDITOR_GUTTER_MARGIN) - + SQL_TOOLBAR_HEIGHT, + southPaneHeight: + (height * southPercent) / 100 - + (SQL_EDITOR_GUTTER_HEIGHT / 2 + SQL_EDITOR_GUTTER_MARGIN), + }; + } + + getHotkeyConfig() { // Get the user's OS const userOS = detectOS(); @@ -253,8 +315,8 @@ const SqlEditor = ({ key: 'ctrl+r', descr: t('Run query'), func: () => { - if (queryEditor.sql.trim() !== '') { - startQuery(); + if (this.props.queryEditor.sql.trim() !== '') { + this.runQuery(); } }, }, @@ -263,8 +325,8 @@ const SqlEditor = ({ key: 'ctrl+enter', descr: t('Run query'), func: () => { - if (queryEditor.sql.trim() !== '') { - startQuery(); + if (this.props.queryEditor.sql.trim() !== '') { + this.runQuery(); } }, }, @@ -273,20 +335,18 @@ const SqlEditor = ({ key: userOS === 'Windows' ? 'ctrl+q' : 'ctrl+t', descr: t('New tab'), func: () => { - const name = newQueryTabName(queryEditors || []); - dispatch( - addQueryEditor({ - ...queryEditor, - name, - }), - ); + const name = newQueryTabName(this.props.queryEditors || []); + this.props.addQueryEditor({ + ...this.props.queryEditor, + name, + }); }, }, { name: 'stopQuery', key: userOS === 'MacOS' ? 'ctrl+x' : 'ctrl+e', descr: t('Stop query'), - func: stopQuery, + func: this.stopQuery, }, ]; @@ -302,170 +362,176 @@ const SqlEditor = ({ } return base; - }; - - const handleWindowResize = () => { - setHeight(getSqlEditorHeight()); - }; + } - const handleWindowResizeWithThrottle = useMemo( - () => throttle(handleWindowResize, WINDOW_RESIZE_THROTTLE_MS), - [], - ); + setEmptyState(bool) { + this.setState({ showEmptyState: bool }); + } - const onBeforeUnload = event => { - if ( - database?.extra_json?.cancel_query_on_windows_unload && - latestQuery?.state === 'running' - ) { - event.preventDefault(); - stopQuery(); - } - }; + setQueryEditorAndSaveSql(sql) { + this.props.queryEditorSetAndSaveSql(this.props.queryEditor, sql); + } - useEffect(() => { - // We need to measure the height of the sql editor post render to figure the height of - // the south pane so it gets rendered properly - setHeight(getSqlEditorHeight()); - if (!database || isEmpty(database)) { - setShowEmptyState(true); + getQueryCostEstimate() { + if (this.props.database) { + const qe = this.props.queryEditor; + this.props.estimateQueryCost(qe); } + } - window.addEventListener('resize', handleWindowResizeWithThrottle); - window.addEventListener('beforeunload', onBeforeUnload); - - // setup hotkeys - const hotkeys = getHotkeyConfig(); - hotkeys.forEach(keyConfig => { - Mousetrap.bind([keyConfig.key], keyConfig.func); + handleToggleAutocompleteEnabled = () => { + this.setState(prevState => { + setItem( + LocalStorageKeys.sqllab__is_autocomplete_enabled, + !prevState.autocompleteEnabled, + ); + return { + autocompleteEnabled: !prevState.autocompleteEnabled, + }; }); - - return () => { - window.removeEventListener('resize', handleWindowResizeWithThrottle); - window.removeEventListener('beforeunload', onBeforeUnload); - }; - }, []); - - const onResizeStart = () => { - // Set the heights on the ace editor and the ace content area after drag starts - // to smooth out the visual transition to the new heights when drag ends - document.getElementsByClassName('ace_content')[0].style.height = '100%'; }; - const onResizeEnd = ([northPercent, southPercent]) => { - setNorthPercent(northPercent); - setSouthPercent(southPercent); - - if (northPaneRef.current?.clientHeight) { - dispatch(persistEditorHeight(queryEditor, northPercent, southPercent)); - } - }; + handleWindowResize() { + this.setState({ height: this.getSqlEditorHeight() }); + } - const setQueryEditorAndSaveSql = sql => { - dispatch(queryEditorSetAndSaveSql(queryEditor, sql)); - }; + elementStyle(dimension, elementSize, gutterSize) { + return { + [dimension]: `calc(${elementSize}% - ${ + gutterSize + SQL_EDITOR_GUTTER_MARGIN + }px)`, + }; + } - const setQueryEditorAndSaveSqlWithDebounce = useMemo( - () => debounce(setQueryEditorAndSaveSql, SET_QUERY_EDITOR_SQL_DEBOUNCE_MS), - [], - ); + requestValidation(sql) { + const { database, queryEditor, validateQuery } = this.props; + if (database) { + validateQuery(queryEditor, sql); + } + } - const canValidateQuery = () => { + canValidateQuery() { // Check whether or not we can validate the current query based on whether // or not the backend has a validator configured for it. - if (database) { - return validatorMap.hasOwnProperty(database.backend); + if (this.props.database) { + return validatorMap.hasOwnProperty(this.props.database.backend); } return false; - }; + } - const requestValidation = sql => { - if (database) { - dispatch(validateQuery(queryEditor, sql)); + runQuery() { + if (this.props.database) { + this.startQuery(); } - }; + } - const requestValidationWithDebounce = useMemo( - () => debounce(requestValidation, VALIDATION_DEBOUNCE_MS), - [], - ); + convertToNumWithSpaces(num) { + return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 '); + } - const onSqlChanged = sql => { - dispatch(queryEditorSetSql(queryEditor, sql)); - setQueryEditorAndSaveSqlWithDebounce(sql); - // Request server-side validation of the query text - if (canValidateQuery()) { - // NB. requestValidation is debounced - requestValidationWithDebounce(sql); - } - }; + startQuery(ctas = false, ctas_method = CtasEnum.TABLE) { + const { + database, + runQueryFromSqlEditor, + setActiveSouthPaneTab, + queryEditor, + defaultQueryLimit, + } = this.props; + runQueryFromSqlEditor( + database, + queryEditor, + defaultQueryLimit, + ctas ? this.state.ctas : '', + ctas, + ctas_method, + ); + setActiveSouthPaneTab('Results'); + } - // Return the heights for the ace editor and the south pane as an object - // given the height of the sql editor, north pane percent and south pane percent. - const getAceEditorAndSouthPaneHeights = ( - height, - northPercent, - southPercent, - ) => ({ - aceEditorHeight: - (height * northPercent) / (theme.gridUnit * 25) - - (SQL_EDITOR_GUTTER_HEIGHT / 2 + SQL_EDITOR_GUTTER_MARGIN) - - SQL_TOOLBAR_HEIGHT, - southPaneHeight: - (height * southPercent) / (theme.gridUnit * 25) - - (SQL_EDITOR_GUTTER_HEIGHT / 2 + SQL_EDITOR_GUTTER_MARGIN), - }); - - const getQueryCostEstimate = () => { - if (database) { - dispatch(estimateQueryCost(queryEditor)); + stopQuery() { + if ( + this.props.latestQuery && + ['running', 'pending'].indexOf(this.props.latestQuery.state) >= 0 + ) { + this.props.postStopQuery(this.props.latestQuery); } - }; - - const handleToggleAutocompleteEnabled = () => { - setItem( - LocalStorageKeys.sqllab__is_autocomplete_enabled, - !autocompleteEnabled, - ); - setAutocompleteEnabled(!autocompleteEnabled); - }; + } - const elementStyle = (dimension, elementSize, gutterSize) => ({ - [dimension]: `calc(${elementSize}% - ${ - gutterSize + SQL_EDITOR_GUTTER_MARGIN - }px)`, - }); + createTableAs() { + this.startQuery(true, CtasEnum.TABLE); + this.setState({ showCreateAsModal: false, ctas: '' }); + } - const createTableAs = () => { - startQuery(true, CtasEnum.TABLE); - setShowCreateAsModal(false); - setCtas(''); - }; + createViewAs() { + this.startQuery(true, CtasEnum.VIEW); + this.setState({ showCreateAsModal: false, ctas: '' }); + } - const createViewAs = () => { - startQuery(true, CtasEnum.VIEW); - setShowCreateAsModal(false); - setCtas(''); - }; + ctasChanged(event) { + this.setState({ ctas: event.target.value }); + } - const ctasChanged = event => { - setCtas(event.target.value); - }; + queryPane() { + const hotkeys = this.getHotkeyConfig(); + const { aceEditorHeight, southPaneHeight } = + this.getAceEditorAndSouthPaneHeights( + this.state.height, + this.state.northPercent, + this.state.southPercent, + ); + return ( + +
+ + {this.renderEditorBottomBar(hotkeys)} +
+ +
+ ); + } - const renderDropdown = () => { - const qe = queryEditor; - const successful = latestQuery?.state === 'success'; + renderDropdown() { + const qe = this.props.queryEditor; + const successful = this.props.latestQuery?.state === 'success'; const scheduleToolTip = successful ? t('Schedule the query periodically') : t('You must run the query successfully first'); return ( - - + + {' '} {t('Autocomplete')}{' '} {' '} @@ -474,7 +540,7 @@ const SqlEditor = ({ { - dispatch(queryEditorSetTemplateParams(qe, params)); + this.props.actions.queryEditorSetTemplateParams(qe, params); }} queryEditor={qe} /> @@ -485,10 +551,10 @@ const SqlEditor = ({ dispatch(scheduleQuery(query))} + onSchedule={this.props.actions.scheduleQuery} schema={qe.schema} dbId={qe.dbId} - scheduleQueryWarning={scheduleQueryWarning} + scheduleQueryWarning={this.props.scheduleQueryWarning} tooltip={scheduleToolTip} disabled={!successful} /> @@ -496,24 +562,31 @@ const SqlEditor = ({ )} ); - }; + } - const onSaveQuery = async query => { - const savedQuery = await dispatch(saveQuery(query)); - dispatch(addSavedQueryToTabState(queryEditor, savedQuery)); - }; + async saveQuery(query) { + const { queryEditor: qe, actions } = this.props; + const savedQuery = await actions.saveQuery(query); + actions.addSavedQueryToTabState(qe, savedQuery); + } + + renderEditorBottomBar() { + const { queryEditor: qe } = this.props; - const renderEditorBottomBar = () => { - const { allow_ctas: allowCTAS, allow_cvas: allowCVAS } = database || {}; + const { allow_ctas: allowCTAS, allow_cvas: allowCVAS } = + this.props.database || {}; const showMenu = allowCTAS || allowCVAS; + const { theme } = this.props; const runMenuBtn = ( {allowCTAS && ( { - setShowCreateAsModal(true); - setCreateAs(CtasEnum.TABLE); + this.setState({ + showCreateAsModal: true, + createAs: CtasEnum.TABLE, + }); }} key="1" > @@ -523,8 +596,10 @@ const SqlEditor = ({ {allowCVAS && ( { - setShowCreateAsModal(true); - setCreateAs(CtasEnum.VIEW); + this.setState({ + showCreateAsModal: true, + createAs: CtasEnum.VIEW, + }); }} key="2" > @@ -539,189 +614,214 @@ const SqlEditor = ({
{isFeatureEnabled(FeatureFlag.ESTIMATE_QUERY_COST) && - database?.allows_cost_estimate && ( + this.props.database && + this.props.database.allows_cost_estimate && ( )} - {latestQuery && ( + {this.props.latestQuery && ( )}
dispatch(updateSavedQuery(query))} - saveQueryWarning={saveQueryWarning} - database={database} + queryEditor={qe} + columns={this.props.latestQuery?.results?.columns || []} + onSave={this.saveQuery} + onUpdate={this.props.actions.updateSavedQuery} + saveQueryWarning={this.props.saveQueryWarning} + database={this.props.database} /> - + - +
); - }; + } - const queryPane = () => { - const hotkeys = getHotkeyConfig(); - const { aceEditorHeight, southPaneHeight } = - getAceEditorAndSouthPaneHeights(height, northPercent, southPercent); + render() { + const createViewModalTitle = + this.state.createAs === CtasEnum.VIEW + ? 'CREATE VIEW AS' + : 'CREATE TABLE AS'; + + const createModalPlaceHolder = + this.state.createAs === CtasEnum.VIEW + ? 'Specify name to CREATE VIEW AS schema in: public' + : 'Specify name to CREATE TABLE AS schema in: public'; + const leftBarStateClass = this.props.hideLeftBar + ? 'schemaPane-exit-done' + : 'schemaPane-enter-done'; return ( - -
- - {renderEditorBottomBar(hotkeys)} -
- -
- ); - }; - - const createViewModalTitle = - createAs === CtasEnum.VIEW ? 'CREATE VIEW AS' : 'CREATE TABLE AS'; - - const createModalPlaceHolder = - createAs === CtasEnum.VIEW - ? t('Specify name to CREATE VIEW AS schema in: public') - : t('Specify name to CREATE TABLE AS schema in: public'); - - const leftBarStateClass = hideLeftBar - ? 'schemaPane-exit-done' - : 'schemaPane-enter-done'; - return ( -
- - + - {adjustedWidth => ( - - setShowEmptyState(bool)} - /> - - )} - - - {showEmptyState ? ( - - ) : ( - queryPane() - )} - setShowCreateAsModal(false)} - footer={ - <> - - {createAs === CtasEnum.TABLE && ( - + + + )} + + + {this.state.showEmptyState ? ( + + ) : ( + this.queryPane() + )} + { + this.setState({ showCreateAsModal: false }); + }} + footer={ + <> - )} - - } - > - {t('Name')} - - -
- ); -}; - + {this.state.createAs === CtasEnum.TABLE && ( + + )} + {this.state.createAs === CtasEnum.VIEW && ( + + )} + + } + > + Name + + + + ); + } +} +SqlEditor.defaultProps = defaultProps; SqlEditor.propTypes = propTypes; -export default SqlEditor; +function mapStateToProps({ sqlLab }, { queryEditor }) { + let { latestQueryId, dbId, hideLeftBar } = queryEditor; + if (sqlLab.unsavedQueryEditor.id === queryEditor.id) { + const { + latestQueryId: unsavedQID, + dbId: unsavedDBID, + hideLeftBar: unsavedHideLeftBar, + } = sqlLab.unsavedQueryEditor; + latestQueryId = unsavedQID || latestQueryId; + dbId = unsavedDBID || dbId; + hideLeftBar = unsavedHideLeftBar || hideLeftBar; + } + const database = sqlLab.databases[dbId]; + const latestQuery = sqlLab.queries[latestQueryId]; + + return { + hideLeftBar, + queryEditors: sqlLab.queryEditors, + latestQuery, + database, + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators( + { + addQueryEditor, + estimateQueryCost, + persistEditorHeight, + postStopQuery, + queryEditorSetAutorun, + queryEditorSetSql, + queryEditorSetAndSaveSql, + queryEditorSetTemplateParams, + runQueryFromSqlEditor, + runQuery, + saveQuery, + addSavedQueryToTabState, + scheduleQuery, + setActiveSouthPaneTab, + updateSavedQuery, + validateQuery, + }, + dispatch, + ); +} + +const themedSqlEditor = withTheme(SqlEditor); +export default connect(mapStateToProps, mapDispatchToProps)(themedSqlEditor); diff --git a/superset-frontend/src/SqlLab/constants.ts b/superset-frontend/src/SqlLab/constants.ts index 29b0f6cf6be0b..11d990032d0db 100644 --- a/superset-frontend/src/SqlLab/constants.ts +++ b/superset-frontend/src/SqlLab/constants.ts @@ -49,12 +49,6 @@ export const SQL_EDITOR_GUTTER_HEIGHT = 5; export const SQL_EDITOR_GUTTER_MARGIN = 3; export const SQL_TOOLBAR_HEIGHT = 51; export const SQL_EDITOR_LEFTBAR_WIDTH = 400; -export const SQL_EDITOR_PADDING = 10; -export const INITIAL_NORTH_PERCENT = 30; -export const INITIAL_SOUTH_PERCENT = 70; -export const SET_QUERY_EDITOR_SQL_DEBOUNCE_MS = 2000; -export const VALIDATION_DEBOUNCE_MS = 600; -export const WINDOW_RESIZE_THROTTLE_MS = 100; // kilobyte storage export const KB_STORAGE = 1024;