diff --git a/superset/assets/cypress/integration/sqllab/tabs.js
b/superset/assets/cypress/integration/sqllab/tabs.js
index cf49c184ca..1862974390 100644
--- a/superset/assets/cypress/integration/sqllab/tabs.js
+++ b/superset/assets/cypress/integration/sqllab/tabs.js
@@ -10,9 +10,9 @@ export default () => {
cy.get('#a11y-query-editor-tabs > ul > li').then((tabList) => {
const initialTabCount = tabList.length;
- // add tab
+ // add tab (second to last tab)
cy.get('#a11y-query-editor-tabs > ul > li')
- .last()
+ .eq(initialTabCount - 2)
.click();
cy.get('#a11y-query-editor-tabs > ul > li').should('have.length',
initialTabCount + 1);
diff --git a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
index 111aba9499..670a914a74 100644
--- a/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/TabbedSqlEditors_spec.jsx
@@ -147,11 +147,16 @@ describe('TabbedSqlEditors', () => {
it('should handle select', () => {
wrapper = getWrapper();
sinon.spy(wrapper.instance(), 'newQueryEditor');
+ sinon.spy(wrapper.instance(), 'removeQueryEditor');
sinon.stub(wrapper.instance().props.actions, 'setActiveQueryEditor');
+ sinon.stub(window, 'confirm').returns(true);
wrapper.instance().handleSelect('add_tab');
expect(wrapper.instance().newQueryEditor.callCount).toBe(1);
+ wrapper.instance().onConfirmClose();
+ expect(wrapper.instance().removeQueryEditor.callCount).toBe(1);
+
wrapper.instance().handleSelect('123');
expect(wrapper.instance().props.actions.setActiveQueryEditor.getCall(0).args[0].id)
.toContain(123);
@@ -166,7 +171,7 @@ describe('TabbedSqlEditors', () => {
expect(firstTab.find(SqlEditor)).toHaveLength(1);
const lastTab = wrapper.find(Tab).last();
- expect(lastTab.props().eventKey).toContain('add_tab');
+ expect(lastTab.props().eventKey).toContain('close_all_tabs');
});
it('should disable new tab when offline', () => {
wrapper = getWrapper();
diff --git a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
index da0d9d127c..847d52bb0e 100644
--- a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
+++ b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { DropdownButton, MenuItem, Tab, Tabs } from 'react-bootstrap';
+import { Alert, DropdownButton, MenuItem, Tab, Tabs } from 'react-bootstrap';
+import Dialog from 'react-bootstrap-dialog';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import URI from 'urijs';
@@ -41,6 +42,8 @@ class TabbedSqlEditors extends React.PureComponent {
dataPreviewQueries: [],
hideLeftBar: false,
};
+ this.onConfirmClose = this.onConfirmClose.bind(this);
+ this.setDialogRef = this.setDialogRef.bind(this);
}
componentDidMount() {
const query = URI(window.location).search(true);
@@ -145,16 +148,50 @@ class TabbedSqlEditors extends React.PureComponent {
handleSelect(key) {
if (key === 'add_tab') {
this.newQueryEditor();
+ } else if (key === 'close_all_tabs') {
+ this.dialog.show({
+ title: t('Confirm closing all query editor tabs'),
+ bsSize: 'medium',
+ actions: [
+ Dialog.CancelAction(),
+ Dialog.OKAction(this.onConfirmClose),
+ ],
+ body: this.renderSaveDialog(),
+ });
} else {
this.props.actions.setActiveQueryEditor({ id: key });
}
}
+ onConfirmClose() {
+ for (let i = 0; i < this.props.queryEditors.length; i++) {
+ const qe = this.props.queryEditors[i];
+ this.removeQueryEditor(qe);
+ }
+ queryCount = 0;
+ }
removeQueryEditor(qe) {
this.props.actions.removeQueryEditor(qe);
}
+ setDialogRef(ref) {
+ this.dialog = ref;
+ }
toggleLeftBar() {
this.setState({ hideLeftBar: !this.state.hideLeftBar });
}
+ renderSaveDialog() {
+ return (
+ <div>
+ <Alert bsStyle="warning" className="pointer" onClick={this.hideAlert}>
+ <div>
+ <i className="fa fa-exclamation-triangle" />{' '}
+ {t(`Once all query editor tabs are closed, they cannot
+ be reopened.`)}
+ </div>
+ </Alert>
+ {t('Are you sure you want to close all query editor tabs?')}
+ </div>
+ );
+ }
render() {
const editors = this.props.queryEditors.map((qe, i) => {
const isSelected = qe.id === this.activeQueryEditor().id;
@@ -241,10 +278,21 @@ class TabbedSqlEditors extends React.PureComponent {
eventKey="add_tab"
disabled={this.props.offline}
/>
+ <Tab
+ title={
+ <div>
+ <i className="fa fa-times-circle" />
+ </div>
+ }
+ eventKey="close_all_tabs"
+ disabled={this.props.offline}
+ />
+ <Dialog ref={this.setDialogRef} />
</Tabs>
);
}
}
+
TabbedSqlEditors.propTypes = propTypes;
TabbedSqlEditors.defaultProps = defaultProps;
With regards,
Apache Git Services