Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL editor layout makeover #7102

Merged
merged 1 commit into from
Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion superset/assets/cypress/integration/sqllab/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default () => {
const initialTabCount = tabListA.length;

// open the tab dropdown to remove
cy.get('#a11y-query-editor-tabs > ul > li:first button').click();
cy.get('#a11y-query-editor-tabs > ul > li:first button:nth-child(2)').click();

// first item is close
cy.get('#a11y-query-editor-tabs > ul > li:first ul li a')
Expand Down
221 changes: 101 additions & 120 deletions superset/assets/package-lock.json

Large diffs are not rendered by default.

16 changes: 4 additions & 12 deletions superset/assets/src/SqlLab/components/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,26 +71,18 @@ class App extends React.PureComponent {
render() {
let content;
if (this.state.hash) {
content = (
<div className="container-fluid">
<div className="row">
<div className="col-md-12">
<QuerySearch height={this.state.contentHeight} actions={this.props.actions} />
</div>
</div>
</div>
);
content = <QuerySearch height={this.state.contentHeight} actions={this.props.actions} />;
} else {
content = (
<div>
<React.Fragment>
<QueryAutoRefresh />
<TabbedSqlEditors />
</div>
</React.Fragment>
);
}
return (
<div className="App SqlLab">
<div className="container-fluid">{content}</div>
{content}
<ToastPresenter />
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions superset/assets/src/SqlLab/components/ResultSet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export default class ResultSet extends React.PureComponent {
}
if (data && data.length > 0) {
return (
<div>
<React.Fragment>
{this.renderControls.bind(this)()}
{sql}
<FilterableTable
Expand All @@ -216,7 +216,7 @@ export default class ResultSet extends React.PureComponent {
height={height}
filterText={this.state.searchText}
/>
</div>
</React.Fragment>
);
} else if (data && data.length === 0) {
return <Alert bsStyle="warning">The query returned no data</Alert>;
Expand Down
13 changes: 7 additions & 6 deletions superset/assets/src/SqlLab/components/SouthPane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
import { STATUS_OPTIONS, STATE_BSSTYLE_MAP } from '../constants';

const TAB_HEIGHT = 44;

/*
editorQueries are queries executed by users passed from SqlEditor component
dataPrebiewQueries are all queries executed for preview of table data (from SqlEditorLeft)
Expand Down Expand Up @@ -76,7 +78,7 @@ export class SouthPane extends React.PureComponent {
{ STATUS_OPTIONS.offline }
</Label>);
}
const innerTabHeight = this.state.height - 55;
const innerTabContentHeight = this.state.height - TAB_HEIGHT;
let latestQuery;
const props = this.props;
if (props.editorQueries.length > 0) {
Expand All @@ -90,7 +92,7 @@ export class SouthPane extends React.PureComponent {
search
query={latestQuery}
actions={props.actions}
height={innerTabHeight}
height={innerTabContentHeight}
database={this.props.databases[latestQuery.dbId]}
/>
);
Expand All @@ -109,7 +111,7 @@ export class SouthPane extends React.PureComponent {
csv={false}
actions={props.actions}
cache
height={innerTabHeight}
height={innerTabContentHeight}
/>
</Tab>
));
Expand All @@ -119,6 +121,7 @@ export class SouthPane extends React.PureComponent {
<Tabs
bsStyle="tabs"
animation={false}
className="SouthPaneTabs"
id={shortid.generate()}
activeKey={this.props.activeSouthPaneTab}
onSelect={this.switchTab}
Expand All @@ -133,9 +136,7 @@ export class SouthPane extends React.PureComponent {
title={t('Query History')}
eventKey="History"
>
<div style={{ height: `${innerTabHeight}px`, overflow: 'auto' }}>
<QueryHistory queries={props.editorQueries} actions={props.actions} />
</div>
<QueryHistory queries={props.editorQueries} actions={props.actions} />
</Tab>
{dataPreviewTabs}
</Tabs>
Expand Down
72 changes: 41 additions & 31 deletions superset/assets/src/SqlLab/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ import AceEditorWrapper from './AceEditorWrapper';
import { STATE_BSSTYLE_MAP } from '../constants';
import RunQueryActionButton from './RunQueryActionButton';

const SQL_EDITOR_PADDING = 10;
const SQL_TOOLBAR_HEIGHT = 51;
const GUTTER_HEIGHT = 5;
const GUTTER_MARGIN = 3;
const INITIAL_NORTH_PERCENT = 30;
const INITIAL_SOUTH_PERCENT = 70;

Expand Down Expand Up @@ -81,6 +83,7 @@ class SqlEditor extends React.PureComponent {
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.runQuery = this.runQuery.bind(this);
Expand Down Expand Up @@ -124,14 +127,16 @@ class SqlEditor extends React.PureComponent {
}
// One layer of abstraction for easy spying in unit tests
getSqlEditorHeight() {
return this.sqlEditorRef.current ? this.sqlEditorRef.current.clientHeight : 0;
return this.sqlEditorRef.current ?
(this.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_TOOLBAR_HEIGHT - GUTTER_HEIGHT / 2,
southPaneHeight: height * southPercent / 100,
aceEditorHeight: height * northPercent / 100 - (GUTTER_HEIGHT / 2 + GUTTER_MARGIN)
- SQL_TOOLBAR_HEIGHT,
southPaneHeight: height * southPercent / 100 - (GUTTER_HEIGHT / 2 + GUTTER_MARGIN),
};
}
getHotkeyConfig() {
Expand Down Expand Up @@ -174,6 +179,11 @@ class SqlEditor extends React.PureComponent {
setQueryLimit(queryLimit) {
this.props.actions.queryEditorSetQueryLimit(this.props.queryEditor, queryLimit);
}
elementStyle(dimension, elementSize, gutterSize) {
return {
[dimension]: `calc(${elementSize}% - ${gutterSize + GUTTER_MARGIN}px)`,
};
}
runQuery() {
if (this.props.database) {
this.startQuery(this.props.database.allow_run_async);
Expand Down Expand Up @@ -212,36 +222,36 @@ class SqlEditor extends React.PureComponent {
const { aceEditorHeight, southPaneHeight } = this.getAceEditorAndSouthPaneHeights(
this.state.height, INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT);
return (
<div className="queryPane">
<Split
sizes={[INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT]}
minSize={200}
direction="vertical"
gutterSize={GUTTER_HEIGHT}
onDragStart={this.onResizeStart}
onDragEnd={this.onResizeEnd}
>
<div ref={this.northPaneRef}>
<AceEditorWrapper
actions={this.props.actions}
onBlur={this.setQueryEditorSql}
onChange={this.onSqlChanged}
queryEditor={this.props.queryEditor}
sql={this.props.queryEditor.sql}
tables={this.props.tables}
height={`${this.state.aceEditorHeight || aceEditorHeight}px`}
hotkeys={hotkeys}
/>
{this.renderEditorBottomBar(hotkeys)}
</div>
<SouthPane
editorQueries={this.props.editorQueries}
dataPreviewQueries={this.props.dataPreviewQueries}
<Split
className="queryPane"
sizes={[INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT]}
elementStyle={this.elementStyle}
minSize={200}
direction="vertical"
gutterSize={GUTTER_HEIGHT}
onDragStart={this.onResizeStart}
onDragEnd={this.onResizeEnd}
>
<div ref={this.northPaneRef}>
<AceEditorWrapper
actions={this.props.actions}
height={this.state.southPaneHeight || southPaneHeight}
onBlur={this.setQueryEditorSql}
onChange={this.onSqlChanged}
queryEditor={this.props.queryEditor}
sql={this.props.queryEditor.sql}
tables={this.props.tables}
height={`${this.state.aceEditorHeight || aceEditorHeight}px`}
hotkeys={hotkeys}
/>
</Split>
</div>
{this.renderEditorBottomBar(hotkeys)}
</div>
<SouthPane
editorQueries={this.props.editorQueries}
dataPreviewQueries={this.props.dataPreviewQueries}
actions={this.props.actions}
height={this.state.southPaneHeight || southPaneHeight}
/>
</Split>
);
}
renderEditorBottomBar(hotkeys) {
Expand Down
16 changes: 7 additions & 9 deletions superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default class SqlEditorLeftBar extends React.PureComponent {
const tableMetaDataHeight = this.props.height - 130; // 130 is the height of the selects above
const qe = this.props.queryEditor;
return (
<div className="sqlEditorLeftBar">
<div className="SqlEditorLeftBar">
<TableSelector
dbId={qe.dbId}
schema={qe.schema}
Expand All @@ -117,14 +117,12 @@ export default class SqlEditorLeftBar extends React.PureComponent {
database={this.props.database}
handleError={this.props.actions.addDangerToast}
/>
<hr />
<div className="m-t-5">
<div className="scrollbar-container">
<div className="scrollbar-content" style={{ height: tableMetaDataHeight }}>
{this.props.tables.map(table => (
<TableElement table={table} key={table.id} actions={this.props.actions} />
))}
</div>
<div className="divider" />
<div className="scrollbar-container">
<div className="scrollbar-content" style={{ height: tableMetaDataHeight }}>
{this.props.tables.map(table => (
<TableElement table={table} key={table.id} actions={this.props.actions} />
))}
</div>
</div>
{shouldShowReset &&
Expand Down
108 changes: 54 additions & 54 deletions superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import { DropdownButton, MenuItem, Tab, Tabs } from 'react-bootstrap';
import { MenuItem, SplitButton, Tab, Tabs } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import URI from 'urijs';
Expand Down Expand Up @@ -196,64 +196,62 @@ class TabbedSqlEditors extends React.PureComponent {
}
const state = latestQuery ? latestQuery.state : '';

const tabTitle = (
<div>
const title = (
<React.Fragment>
<TabStatusIcon onClose={() => this.removeQueryEditor(qe)} tabState={state} />{' '}
{qe.title}{' '}
<DropdownButton
bsSize="small"
id={'ddbtn-tab-' + i}
className="ddbtn-tab"
title=""
>
<MenuItem eventKey="1" onClick={() => this.removeQueryEditor(qe)}>
<div className="icon-container">
<i className="fa fa-close" />
</div>
{t('Close tab')}
</MenuItem>
<MenuItem eventKey="2" onClick={() => this.renameTab(qe)}>
<div className="icon-container">
<i className="fa fa-i-cursor" />
</div>
{t('Rename tab')}
</MenuItem>
<MenuItem eventKey="3" onClick={this.toggleLeftBar}>
<div className="icon-container">
<i className="fa fa-cogs" />
</div>
{this.state.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')}
</MenuItem>
<MenuItem eventKey="4" onClick={() => this.removeAllOtherQueryEditors(qe)}>
<div className="icon-container">
<i className="fa fa-times-circle-o" />
</div>
{t('Close all other tabs')}
</MenuItem>
</DropdownButton>
</div>
</React.Fragment>
);
const tabTitle = (
<SplitButton
bsSize="small"
id={'ddbtn-tab-' + i}
className="ddbtn-tab"
title={title}
>
<MenuItem eventKey="1" onClick={() => this.removeQueryEditor(qe)}>
<div className="icon-container">
<i className="fa fa-close" />
</div>
{t('Close tab')}
</MenuItem>
<MenuItem eventKey="2" onClick={() => this.renameTab(qe)}>
<div className="icon-container">
<i className="fa fa-i-cursor" />
</div>
{t('Rename tab')}
</MenuItem>
<MenuItem eventKey="3" onClick={this.toggleLeftBar}>
<div className="icon-container">
<i className="fa fa-cogs" />
</div>
{this.state.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')}
</MenuItem>
<MenuItem eventKey="4" onClick={() => this.removeAllOtherQueryEditors(qe)}>
<div className="icon-container">
<i className="fa fa-times-circle-o" />
</div>
{t('Close all other tabs')}
</MenuItem>
</SplitButton>
);
return (
<Tab key={qe.id} title={tabTitle} eventKey={qe.id}>
<div className="panel panel-default">
<div className="panel-body">
{isSelected && (
<SqlEditor
tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)}
queryEditor={qe}
editorQueries={this.state.queriesArray}
dataPreviewQueries={this.state.dataPreviewQueries}
latestQuery={latestQuery}
database={database}
actions={this.props.actions}
hideLeftBar={this.state.hideLeftBar}
defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow}
saveQueryWarning={this.props.saveQueryWarning}
/>
)}
</div>
</div>
{isSelected && (
<SqlEditor
tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)}
queryEditor={qe}
editorQueries={this.state.queriesArray}
dataPreviewQueries={this.state.dataPreviewQueries}
latestQuery={latestQuery}
database={database}
actions={this.props.actions}
hideLeftBar={this.state.hideLeftBar}
defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow}
saveQueryWarning={this.props.saveQueryWarning}
/>
)}
</Tab>
);
});
Expand All @@ -264,6 +262,7 @@ class TabbedSqlEditors extends React.PureComponent {
activeKey={this.props.tabHistory[this.props.tabHistory.length - 1]}
onSelect={this.handleSelect.bind(this)}
id="a11y-query-editor-tabs"
className="SqlEditorTabs"
>
{editors}
<Tab
Expand All @@ -272,6 +271,7 @@ class TabbedSqlEditors extends React.PureComponent {
<i className="fa fa-plus-circle" />&nbsp;
</div>
}
className="addEditorTab"
eventKey="add_tab"
disabled={this.props.offline}
/>
Expand Down
Loading