diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx index c48594d304841..d6947c1ba5f15 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx +++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx @@ -18,16 +18,15 @@ */ /* eslint-disable jsx-a11y/anchor-is-valid */ /* eslint-disable jsx-a11y/no-static-element-interactions */ -import React from 'react'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; import { CSSTransition } from 'react-transition-group'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; +import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import Split from 'react-split'; -import { t, styled, withTheme } from '@superset-ui/core'; +import { t, styled, useTheme } from '@superset-ui/core'; import debounce from 'lodash/debounce'; import throttle from 'lodash/throttle'; -import StyledModal from 'src/components/Modal'; +import Modal from 'src/components/Modal'; import Mousetrap from 'mousetrap'; import Button from 'src/components/Button'; import Timer from 'src/components/Timer'; @@ -48,7 +47,6 @@ import { queryEditorSetAndSaveSql, queryEditorSetTemplateParams, runQueryFromSqlEditor, - runQuery, saveQuery, addSavedQueryToTabState, scheduleQuery, @@ -62,6 +60,12 @@ 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, @@ -83,13 +87,6 @@ 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') || '{}', @@ -132,7 +129,7 @@ const StyledToolbar = styled.div` const StyledSidebar = styled.div` flex: 0 0 ${({ width }) => width}px; width: ${({ width }) => width}px; - padding: ${({ hide }) => (hide ? 0 : 10)}px; + padding: ${({ theme, hide }) => (hide ? 0 : theme.gridUnit * 2.5)}px; border-right: 1px solid ${({ theme, hide }) => hide ? 'transparent' : theme.colors.grayscale.light2}; @@ -140,13 +137,10 @@ 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, @@ -154,158 +148,97 @@ const propTypes = { scheduleQueryWarning: PropTypes.string, }; -const defaultProps = { - database: null, - latestQuery: null, - hideLeftBar: false, - scheduleQueryWarning: null, -}; +const SqlEditor = ({ + actions, + tables, + editorQueries, + dataPreviewQueries, + queryEditor, + defaultQueryLimit, + maxRow, + displayLimit, + saveQueryWarning, + scheduleQueryWarning = null, +}) => { + const theme = useTheme(); + const dispatch = useDispatch(); + + const { database, latestQuery, hideLeftBar } = useSelector( + ({ sqlLab: { unsavedQueryEditor, databases, queries } }) => { + let { dbId, latestQueryId, hideLeftBar } = queryEditor; + if (unsavedQueryEditor.id === queryEditor.id) { + dbId = unsavedQueryEditor.dbId || dbId; + latestQueryId = unsavedQueryEditor.latestQueryId || latestQueryId; + hideLeftBar = unsavedQueryEditor.hideLeftBar || hideLeftBar; + } + return { + 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, - ); + const queryEditors = useSelector(({ sqlLab }) => sqlLab.queryEditors); - this.onBeforeUnload = this.onBeforeUnload.bind(this); - this.renderDropdown = this.renderDropdown.bind(this); - } + 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); - UNSAFE_componentWillMount() { - if (this.state.autorun) { - this.setState({ autorun: false }); - this.props.queryEditorSetAutorun(this.props.queryEditor, false); - this.startQuery(); - } - } + const sqlEditorRef = useRef(null); + const northPaneRef = useRef(null); - 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); + const startQuery = (ctasArg = false, ctas_method = CtasEnum.TABLE) => { + if (!database) { + return; } - 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); - }); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.handleWindowResize); - window.removeEventListener('beforeunload', this.onBeforeUnload); - } - - 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%'; - } - - onResizeEnd([northPercent, southPercent]) { - this.setState({ northPercent, southPercent }); - - 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')); + }; - onBeforeUnload(event) { - if ( - this.props.database?.extra_json?.cancel_query_on_windows_unload && - this.props.latestQuery?.state === 'running' - ) { - event.preventDefault(); - this.stopQuery(); + const stopQuery = () => { + if (latestQuery && ['running', 'pending'].indexOf(latestQuery.state) >= 0) { + dispatch(postStopQuery(latestQuery)); } - } + }; - 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); + useState(() => { + if (autorun) { + setAutorun(false); + dispatch(queryEditorSetAutorun(queryEditor, false)); + startQuery(); } - } + }); // One layer of abstraction for easy spying in unit tests - getSqlEditorHeight() { - return this.sqlEditorRef.current - ? this.sqlEditorRef.current.clientHeight - SQL_EDITOR_PADDING * 2 + const getSqlEditorHeight = () => + sqlEditorRef.current + ? sqlEditorRef.current.clientHeight - SQL_EDITOR_PADDING * 2 : 0; - } - // 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() { + const getHotkeyConfig = () => { // Get the user's OS const userOS = detectOS(); @@ -315,8 +248,8 @@ class SqlEditor extends React.PureComponent { key: 'ctrl+r', descr: t('Run query'), func: () => { - if (this.props.queryEditor.sql.trim() !== '') { - this.runQuery(); + if (queryEditor.sql.trim() !== '') { + startQuery(); } }, }, @@ -325,8 +258,8 @@ class SqlEditor extends React.PureComponent { key: 'ctrl+enter', descr: t('Run query'), func: () => { - if (this.props.queryEditor.sql.trim() !== '') { - this.runQuery(); + if (queryEditor.sql.trim() !== '') { + startQuery(); } }, }, @@ -335,18 +268,20 @@ class SqlEditor extends React.PureComponent { key: userOS === 'Windows' ? 'ctrl+q' : 'ctrl+t', descr: t('New tab'), func: () => { - const name = newQueryTabName(this.props.queryEditors || []); - this.props.addQueryEditor({ - ...this.props.queryEditor, - name, - }); + const name = newQueryTabName(queryEditors || []); + dispatch( + addQueryEditor({ + ...queryEditor, + name, + }), + ); }, }, { name: 'stopQuery', key: userOS === 'MacOS' ? 'ctrl+x' : 'ctrl+e', descr: t('Stop query'), - func: this.stopQuery, + func: stopQuery, }, ]; @@ -362,176 +297,170 @@ class SqlEditor extends React.PureComponent { } return base; - } + }; - setEmptyState(bool) { - this.setState({ showEmptyState: bool }); - } + const handleWindowResize = () => { + setHeight(getSqlEditorHeight()); + }; - setQueryEditorAndSaveSql(sql) { - this.props.queryEditorSetAndSaveSql(this.props.queryEditor, sql); - } + const handleWindowResizeWithThrottle = useMemo( + () => throttle(handleWindowResize, WINDOW_RESIZE_THROTTLE_MS), + [], + ); - getQueryCostEstimate() { - if (this.props.database) { - const qe = this.props.queryEditor; - this.props.estimateQueryCost(qe); + const onBeforeUnload = event => { + if ( + database?.extra_json?.cancel_query_on_windows_unload && + latestQuery?.state === 'running' + ) { + event.preventDefault(); + stopQuery(); } - } - - handleToggleAutocompleteEnabled = () => { - this.setState(prevState => { - setItem( - LocalStorageKeys.sqllab__is_autocomplete_enabled, - !prevState.autocompleteEnabled, - ); - return { - autocompleteEnabled: !prevState.autocompleteEnabled, - }; - }); }; - handleWindowResize() { - this.setState({ height: this.getSqlEditorHeight() }); - } + 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); + } + + window.addEventListener('resize', handleWindowResizeWithThrottle); + window.addEventListener('beforeunload', onBeforeUnload); - elementStyle(dimension, elementSize, gutterSize) { - return { - [dimension]: `calc(${elementSize}% - ${ - gutterSize + SQL_EDITOR_GUTTER_MARGIN - }px)`, + // setup hotkeys + const hotkeys = getHotkeyConfig(); + hotkeys.forEach(keyConfig => { + Mousetrap.bind([keyConfig.key], keyConfig.func); + }); + + return () => { + window.removeEventListener('resize', handleWindowResizeWithThrottle); + window.removeEventListener('beforeunload', onBeforeUnload); }; - } + }, []); - requestValidation(sql) { - const { database, queryEditor, validateQuery } = this.props; - if (database) { - validateQuery(queryEditor, sql); + 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)); } - } + }; + + const setQueryEditorAndSaveSql = sql => { + dispatch(queryEditorSetAndSaveSql(queryEditor, sql)); + }; + + const setQueryEditorAndSaveSqlWithDebounce = useMemo( + () => debounce(setQueryEditorAndSaveSql, SET_QUERY_EDITOR_SQL_DEBOUNCE_MS), + [], + ); - canValidateQuery() { + const 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 (this.props.database) { - return validatorMap.hasOwnProperty(this.props.database.backend); + if (database) { + return validatorMap.hasOwnProperty(database.backend); } return false; - } + }; - runQuery() { - if (this.props.database) { - this.startQuery(); + const requestValidation = sql => { + if (database) { + dispatch(validateQuery(queryEditor, sql)); } - } + }; - convertToNumWithSpaces(num) { - return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 '); - } + const requestValidationWithDebounce = useMemo( + () => debounce(requestValidation, VALIDATION_DEBOUNCE_MS), + [], + ); - 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'); - } + 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); + } + }; - stopQuery() { - if ( - this.props.latestQuery && - ['running', 'pending'].indexOf(this.props.latestQuery.state) >= 0 - ) { - this.props.postStopQuery(this.props.latestQuery); + // 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)); } - } + }; - createTableAs() { - this.startQuery(true, CtasEnum.TABLE); - this.setState({ showCreateAsModal: false, ctas: '' }); - } + const handleToggleAutocompleteEnabled = () => { + setItem( + LocalStorageKeys.sqllab__is_autocomplete_enabled, + !autocompleteEnabled, + ); + setAutocompleteEnabled(!autocompleteEnabled); + }; - createViewAs() { - this.startQuery(true, CtasEnum.VIEW); - this.setState({ showCreateAsModal: false, ctas: '' }); - } + const elementStyle = (dimension, elementSize, gutterSize) => ({ + [dimension]: `calc(${elementSize}% - ${ + gutterSize + SQL_EDITOR_GUTTER_MARGIN + }px)`, + }); - ctasChanged(event) { - this.setState({ ctas: event.target.value }); - } + const createTableAs = () => { + startQuery(true, CtasEnum.TABLE); + setShowCreateAsModal(false); + setCtas(''); + }; - queryPane() { - const hotkeys = this.getHotkeyConfig(); - const { aceEditorHeight, southPaneHeight } = - this.getAceEditorAndSouthPaneHeights( - this.state.height, - this.state.northPercent, - this.state.southPercent, - ); - return ( - -
- - {this.renderEditorBottomBar(hotkeys)} -
- -
- ); - } + const createViewAs = () => { + startQuery(true, CtasEnum.VIEW); + setShowCreateAsModal(false); + setCtas(''); + }; + + const ctasChanged = event => { + setCtas(event.target.value); + }; - renderDropdown() { - const qe = this.props.queryEditor; - const successful = this.props.latestQuery?.state === 'success'; + const renderDropdown = () => { + const qe = queryEditor; + const successful = latestQuery?.state === 'success'; const scheduleToolTip = successful ? t('Schedule the query periodically') : t('You must run the query successfully first'); return ( - - + + {' '} {t('Autocomplete')}{' '} {' '} @@ -540,7 +469,7 @@ class SqlEditor extends React.PureComponent { { - this.props.actions.queryEditorSetTemplateParams(qe, params); + dispatch(queryEditorSetTemplateParams(qe, params)); }} queryEditor={qe} /> @@ -551,10 +480,10 @@ class SqlEditor extends React.PureComponent { dispatch(scheduleQuery(query))} schema={qe.schema} dbId={qe.dbId} - scheduleQueryWarning={this.props.scheduleQueryWarning} + scheduleQueryWarning={scheduleQueryWarning} tooltip={scheduleToolTip} disabled={!successful} /> @@ -562,31 +491,24 @@ class SqlEditor extends React.PureComponent { )} ); - } - - 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 onSaveQuery = async query => { + const savedQuery = await dispatch(saveQuery(query)); + dispatch(addSavedQueryToTabState(queryEditor, savedQuery)); + }; - const { allow_ctas: allowCTAS, allow_cvas: allowCVAS } = - this.props.database || {}; + const renderEditorBottomBar = () => { + const { allow_ctas: allowCTAS, allow_cvas: allowCVAS } = database || {}; const showMenu = allowCTAS || allowCVAS; - const { theme } = this.props; const runMenuBtn = ( {allowCTAS && ( { - this.setState({ - showCreateAsModal: true, - createAs: CtasEnum.TABLE, - }); + setShowCreateAsModal(true); + setCreateAs(CtasEnum.TABLE); }} key="1" > @@ -596,10 +518,8 @@ class SqlEditor extends React.PureComponent { {allowCVAS && ( { - this.setState({ - showCreateAsModal: true, - createAs: CtasEnum.VIEW, - }); + setShowCreateAsModal(true); + setCreateAs(CtasEnum.VIEW); }} key="2" > @@ -614,214 +534,190 @@ class SqlEditor extends React.PureComponent {
{isFeatureEnabled(FeatureFlag.ESTIMATE_QUERY_COST) && - this.props.database && - this.props.database.allows_cost_estimate && ( + database?.allows_cost_estimate && ( )} - {this.props.latestQuery && ( + {latestQuery && ( )}
dispatch(updateSavedQuery(query))} + saveQueryWarning={saveQueryWarning} + database={database} /> - + - +
); - } + }; - 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'; + const queryPane = () => { + const hotkeys = getHotkeyConfig(); + const { aceEditorHeight, southPaneHeight } = + getAceEditorAndSouthPaneHeights(height, northPercent, southPercent); 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.createAs === CtasEnum.TABLE && ( - - )} - {this.state.createAs === CtasEnum.VIEW && ( - - )} - - } - > - Name - - -
- ); - } -} -SqlEditor.defaultProps = defaultProps; -SqlEditor.propTypes = propTypes; - -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, + )} + + } + > + {t('Name')} + + +
); -} +}; + +SqlEditor.propTypes = propTypes; -const themedSqlEditor = withTheme(SqlEditor); -export default connect(mapStateToProps, mapDispatchToProps)(themedSqlEditor); +export default SqlEditor; diff --git a/superset-frontend/src/SqlLab/constants.ts b/superset-frontend/src/SqlLab/constants.ts index 11d990032d0db..29b0f6cf6be0b 100644 --- a/superset-frontend/src/SqlLab/constants.ts +++ b/superset-frontend/src/SqlLab/constants.ts @@ -49,6 +49,12 @@ 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;