superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From christ...@apache.org
Subject [incubator-superset] branch master updated: Relayout SQL Editor (#6872)
Date Fri, 15 Feb 2019 02:03:50 GMT
This is an automated email from the ASF dual-hosted git repository.

christine pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new ec6657a  Relayout SQL Editor (#6872)
ec6657a is described below

commit ec6657ab2d07d6982a454e20745445ab673a6a0f
Author: Christine Chambers <christine.d.hang@gmail.com>
AuthorDate: Thu Feb 14 18:03:43 2019 -0800

    Relayout SQL Editor (#6872)
    
    * Relayout SQL Editor
    
    - Refactor SQL editor to remove usage of bootstrap col, row and collapse to simplify the
layout
    - Replace the react-split-pane libraray with react-split to allow custom styling of the
gutter area without sacrifice correctness of the ace editor height calculation
    - Rewrite the left pane animation via plain css transition and animate it to slide in
and out
    - General code and css clean up
    
    * Smooth out the visual transition during dragging
    
    (cherry picked from commit 19f82b729c7a939f12b1c5da6022c0fd76fa3ec9)
    
    * Adjust how the height of the south pane is computed, fixing cypress tests
---
 superset/assets/package-lock.json                  |  25 +++
 superset/assets/package.json                       |   3 +-
 .../spec/javascripts/sqllab/SouthPane_spec.jsx     |   9 +-
 .../spec/javascripts/sqllab/SqlEditor_spec.jsx     |   5 +
 superset/assets/src/SqlLab/components/App.jsx      |   2 +-
 .../assets/src/SqlLab/components/SouthPane.jsx     |  25 ++-
 .../assets/src/SqlLab/components/SqlEditor.jsx     | 186 ++++++++++-----------
 .../src/SqlLab/components/SqlEditorLeftBar.jsx     |   3 +-
 .../src/SqlLab/components/TabbedSqlEditors.jsx     |   2 -
 superset/assets/src/SqlLab/main.less               |  70 ++++++--
 superset/assets/src/components/AsyncSelect.jsx     |  20 +--
 superset/assets/src/components/TableSelector.css   |  18 +-
 superset/assets/src/components/TableSelector.jsx   |  87 ++++------
 13 files changed, 272 insertions(+), 183 deletions(-)

diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json
index f6ba04f..ba19bac 100644
--- a/superset/assets/package-lock.json
+++ b/superset/assets/package-lock.json
@@ -17339,6 +17339,15 @@
         "prop-types": "^15.5.7"
       }
     },
+    "react-split": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/react-split/-/react-split-2.0.4.tgz",
+      "integrity": "sha512-NBKm9MaqzG/00laMUaS8GS9RnItVSekNNwItgGLMbFTeUa9w4bIY8Co/LszNBnpza9n2am0MXIw3SmyiMnhs+w==",
+      "requires": {
+        "prop-types": "^15.5.7",
+        "split.js": "^1.5.9"
+      }
+    },
     "react-split-pane": {
       "version": "0.1.85",
       "resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.85.tgz",
@@ -17380,6 +17389,17 @@
         "refractor": "^2.4.1"
       }
     },
+    "react-transition-group": {
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.3.tgz",
+      "integrity": "sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg==",
+      "requires": {
+        "dom-helpers": "^3.3.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2",
+        "react-lifecycles-compat": "^3.0.4"
+      }
+    },
     "react-virtualized": {
       "version": "9.19.1",
       "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.19.1.tgz",
@@ -19391,6 +19411,11 @@
         }
       }
     },
+    "split.js": {
+      "version": "1.5.10",
+      "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.5.10.tgz",
+      "integrity": "sha512-/J52X5c4ZypVwu4WAhD8E1T9uXQtNokvG6mIBHauzyA1aKH6bmETVSv3RPjBXEz6Gcc4mIThgmjGQL39LD16jQ=="
+    },
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
diff --git a/superset/assets/package.json b/superset/assets/package.json
index c791fb6..02a9fdc 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -123,9 +123,10 @@
     "react-select": "1.2.1",
     "react-select-fast-filter-options": "^0.2.1",
     "react-sortable-hoc": "^0.8.3",
-    "react-split-pane": "^0.1.66",
+    "react-split": "^2.0.4",
     "react-sticky": "^6.0.2",
     "react-syntax-highlighter": "^7.0.4",
+    "react-transition-group": "^2.5.3",
     "react-virtualized": "9.19.1",
     "react-virtualized-select": "^3.1.3",
     "reactable-arc": "0.14.42",
diff --git a/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
index 7b920f6..da6da52 100644
--- a/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/SouthPane_spec.jsx
@@ -24,7 +24,7 @@ import { shallow } from 'enzyme';
 
 import { STATUS_OPTIONS } from '../../../src/SqlLab/constants';
 import { initialState } from './fixtures';
-import SouthPane from '../../../src/SqlLab/components/SouthPane';
+import SouthPaneContainer, { SouthPane } from '../../../src/SqlLab/components/SouthPane';
 
 describe('SouthPane', () => {
   const middlewares = [thunk];
@@ -42,11 +42,16 @@ describe('SouthPane', () => {
   };
 
   const getWrapper = () => (
-    shallow(<SouthPane {...mockedProps} />, {
+    shallow(<SouthPaneContainer {...mockedProps} />, {
       context: { store },
     }).dive());
 
   let wrapper;
+
+  beforeAll(() => {
+    jest.spyOn(SouthPane.prototype, 'getSouthPaneHeight').mockImplementation(() => 500);
+  });
+
   it('should render offline when the state is offline', () => {
     wrapper = getWrapper();
     wrapper.setProps({ offline: true });
diff --git a/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx b/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx
index 933524f..046b2e6 100644
--- a/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/SqlEditor_spec.jsx
@@ -38,6 +38,11 @@ describe('SqlEditor', () => {
     defaultQueryLimit: 1000,
     maxRow: 100000,
   };
+
+  beforeAll(() => {
+    jest.spyOn(SqlEditor.prototype, 'getSqlEditorHeight').mockImplementation(() => 500);
+  });
+
   it('is valid', () => {
     expect(
       React.isValidElement(<SqlEditor {...mockedProps} />),
diff --git a/superset/assets/src/SqlLab/components/App.jsx b/superset/assets/src/SqlLab/components/App.jsx
index bf50caf..25c61de 100644
--- a/superset/assets/src/SqlLab/components/App.jsx
+++ b/superset/assets/src/SqlLab/components/App.jsx
@@ -84,7 +84,7 @@ class App extends React.PureComponent {
       content = (
         <div>
           <QueryAutoRefresh />
-          <TabbedSqlEditors getHeight={this.getHeight} />
+          <TabbedSqlEditors />
         </div>
       );
     }
diff --git a/superset/assets/src/SqlLab/components/SouthPane.jsx b/superset/assets/src/SqlLab/components/SouthPane.jsx
index 0dd1626..40d46da 100644
--- a/superset/assets/src/SqlLab/components/SouthPane.jsx
+++ b/superset/assets/src/SqlLab/components/SouthPane.jsx
@@ -48,7 +48,24 @@ const defaultProps = {
   offline: false,
 };
 
-class SouthPane extends React.PureComponent {
+export class SouthPane extends React.PureComponent {
+  constructor(props) {
+    super(props);
+    this.state = {
+      height: props.height,
+    };
+    this.southPaneRef = React.createRef();
+    this.getSouthPaneHeight = this.getSouthPaneHeight.bind(this);
+    this.switchTab = this.switchTab.bind(this);
+  }
+  componentWillReceiveProps() {
+    // south pane expands the entire height of the tab content on mount
+    this.setState({ height: this.getSouthPaneHeight() });
+  }
+  // One layer of abstraction for easy spying in unit tests
+  getSouthPaneHeight() {
+    return this.southPaneRef.current.clientHeight;
+  }
   switchTab(id) {
     this.props.actions.setActiveSouthPaneTab(id);
   }
@@ -59,7 +76,7 @@ class SouthPane extends React.PureComponent {
           { STATUS_OPTIONS.offline }
         </Label>);
     }
-    const innerTabHeight = this.props.height - 55;
+    const innerTabHeight = this.state.height - 55;
     let latestQuery;
     const props = this.props;
     if (props.editorQueries.length > 0) {
@@ -98,12 +115,12 @@ class SouthPane extends React.PureComponent {
     ));
 
     return (
-      <div className="SouthPane">
+      <div className="SouthPane" ref={this.southPaneRef}>
         <Tabs
           bsStyle="tabs"
           id={shortid.generate()}
           activeKey={this.props.activeSouthPaneTab}
-          onSelect={this.switchTab.bind(this)}
+          onSelect={this.switchTab}
         >
           <Tab
             title={t('Results')}
diff --git a/superset/assets/src/SqlLab/components/SqlEditor.jsx b/superset/assets/src/SqlLab/components/SqlEditor.jsx
index 07344a5..dccad0b 100644
--- a/superset/assets/src/SqlLab/components/SqlEditor.jsx
+++ b/superset/assets/src/SqlLab/components/SqlEditor.jsx
@@ -17,21 +17,18 @@
  * under the License.
  */
 import React from 'react';
+import { CSSTransition } from 'react-transition-group';
 import PropTypes from 'prop-types';
-import { throttle } from 'lodash';
 import {
-  Col,
   FormGroup,
   InputGroup,
   Form,
   FormControl,
   Label,
   OverlayTrigger,
-  Row,
   Tooltip,
-  Collapse,
 } from 'react-bootstrap';
-import SplitPane from 'react-split-pane';
+import Split from 'react-split';
 import { t } from '@superset-ui/translation';
 
 import Button from '../../components/Button';
@@ -47,9 +44,13 @@ import AceEditorWrapper from './AceEditorWrapper';
 import { STATE_BSSTYLE_MAP } from '../constants';
 import RunQueryActionButton from './RunQueryActionButton';
 
+const SQL_TOOLBAR_HEIGHT = 51;
+const GUTTER_HEIGHT = 5;
+const INITIAL_NORTH_PERCENT = 30;
+const INITIAL_SOUTH_PERCENT = 70;
+
 const propTypes = {
   actions: PropTypes.object.isRequired,
-  getHeight: PropTypes.func.isRequired,
   database: PropTypes.object,
   latestQuery: PropTypes.object,
   tables: PropTypes.array.isRequired,
@@ -75,13 +76,18 @@ class SqlEditor extends React.PureComponent {
       ctas: '',
       sql: props.queryEditor.sql,
     };
+    this.sqlEditorRef = React.createRef();
+    this.northPaneRef = React.createRef();
 
-    this.onResize = this.onResize.bind(this);
-    this.throttledResize = throttle(this.onResize, 250);
+    this.onResizeStart = this.onResizeStart.bind(this);
+    this.onResizeEnd = this.onResizeEnd.bind(this);
     this.runQuery = this.runQuery.bind(this);
     this.stopQuery = this.stopQuery.bind(this);
     this.onSqlChanged = this.onSqlChanged.bind(this);
     this.setQueryEditorSql = this.setQueryEditorSql.bind(this);
+    this.queryPane = this.queryPane.bind(this);
+    this.getAceEditorAndSouthPaneHeights = this.getAceEditorAndSouthPaneHeights.bind(this);
+    this.getSqlEditorHeight = this.getSqlEditorHeight.bind(this);
   }
   componentWillMount() {
     if (this.state.autorun) {
@@ -91,29 +97,41 @@ class SqlEditor extends React.PureComponent {
     }
   }
   componentDidMount() {
-    this.onResize();
-    window.addEventListener('resize', this.throttledResize);
+    // 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
+    this.setState({ height: this.getSqlEditorHeight() });
   }
-  componentWillUnmount() {
-    window.removeEventListener('resize', this.throttledResize);
+  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.getElementById('brace-editor').style.height = `calc(100% - ${SQL_TOOLBAR_HEIGHT}px)`;
+    document.getElementsByClassName('ace_content')[0].style.height = '100%';
   }
-  onResize() {
-    const height = this.sqlEditorHeight();
-    const editorPaneHeight = this.props.queryEditor.height || 200;
-    const splitPaneHandlerHeight = 8; // 4px of height + 4px of top-margin
-    this.setState({
-      editorPaneHeight,
-      southPaneHeight: height - editorPaneHeight - splitPaneHandlerHeight,
-      height,
-    });
+  onResizeEnd([northPercent, southPercent]) {
+    this.setState(this.getAceEditorAndSouthPaneHeights(
+      this.state.height, northPercent, southPercent));
 
-    if (this.refs.ace && this.refs.ace.clientHeight) {
-      this.props.actions.persistEditorHeight(this.props.queryEditor, this.refs.ace.clientHeight);
+    if (this.northPaneRef.current && this.northPaneRef.current.clientHeight) {
+      this.props.actions.persistEditorHeight(this.props.queryEditor,
+        this.northPaneRef.current.clientHeight);
     }
   }
   onSqlChanged(sql) {
     this.setState({ sql });
   }
+  // One layer of abstraction for easy spying in unit tests
+  getSqlEditorHeight() {
+    return this.sqlEditorRef.current.clientHeight;
+  }
+  // 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,
+    };
+  }
   getHotkeyConfig() {
     return [
       {
@@ -187,9 +205,42 @@ class SqlEditor extends React.PureComponent {
   ctasChanged(event) {
     this.setState({ ctas: event.target.value });
   }
-  sqlEditorHeight() {
-    const horizontalScrollbarHeight = 25;
-    return parseInt(this.props.getHeight(), 10) - horizontalScrollbarHeight;
+  queryPane() {
+    const hotkeys = this.getHotkeyConfig();
+    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}
+            actions={this.props.actions}
+            height={this.state.southPaneHeight || southPaneHeight}
+          />
+        </Split>
+      </div>
+    );
   }
   renderEditorBottomBar(hotkeys) {
     let ctasControls;
@@ -305,74 +356,23 @@ class SqlEditor extends React.PureComponent {
     );
   }
   render() {
-    const height = this.sqlEditorHeight();
-    const defaultNorthHeight = this.props.queryEditor.height || 200;
-    const hotkeys = this.getHotkeyConfig();
     return (
-      <div
-        className="SqlEditor"
-        style={{
-          height: height + 'px',
-        }}
-      >
-        <Row>
-          <Collapse
-            in={!this.props.hideLeftBar}
-          >
-            <Col
-              xs={6}
-              sm={5}
-              md={4}
-              lg={3}
-            >
-              <SqlEditorLeftBar
-                height={height}
-                database={this.props.database}
-                queryEditor={this.props.queryEditor}
-                tables={this.props.tables}
-                actions={this.props.actions}
-              />
-            </Col>
-          </Collapse>
-          <Col
-            xs={this.props.hideLeftBar ? 12 : 6}
-            sm={this.props.hideLeftBar ? 12 : 7}
-            md={this.props.hideLeftBar ? 12 : 8}
-            lg={this.props.hideLeftBar ? 12 : 9}
-            style={{ height: this.state.height }}
-          >
-            <SplitPane
-              split="horizontal"
-              defaultSize={defaultNorthHeight}
-              minSize={100}
-              onChange={this.onResize}
-            >
-              <div ref="ace" style={{ width: '100%' }}>
-                <div>
-                  <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.editorPaneHeight || defaultNorthHeight) - 50) +
'px'}
-                    hotkeys={hotkeys}
-                  />
-                  {this.renderEditorBottomBar(hotkeys)}
-                </div>
-              </div>
-              <div ref="south">
-                <SouthPane
-                  editorQueries={this.props.editorQueries}
-                  dataPreviewQueries={this.props.dataPreviewQueries}
-                  actions={this.props.actions}
-                  height={this.state.southPaneHeight || 0}
-                />
-              </div>
-            </SplitPane>
-          </Col>
-        </Row>
+      <div ref={this.sqlEditorRef} className="SqlEditor">
+        <CSSTransition
+          classNames="schemaPane"
+          in={!this.props.hideLeftBar}
+          timeout={300}
+        >
+          <div className="schemaPane">
+            <SqlEditorLeftBar
+              database={this.props.database}
+              queryEditor={this.props.queryEditor}
+              tables={this.props.tables}
+              actions={this.props.actions}
+            />
+          </div>
+        </CSSTransition>
+        {this.queryPane()}
       </div>
     );
   }
diff --git a/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx b/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
index 16170a6..0de18b0 100644
--- a/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
+++ b/superset/assets/src/SqlLab/components/SqlEditorLeftBar.jsx
@@ -20,7 +20,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { Button } from 'react-bootstrap';
 import { t } from '@superset-ui/translation';
-
 import TableElement from './TableElement';
 import TableSelector from '../../components/TableSelector';
 
@@ -106,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="clearfix">
+      <div className="sqlEditorLeftBar">
         <TableSelector
           dbId={qe.dbId}
           schema={qe.schema}
diff --git a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
index a90a2bf..a0a991c 100644
--- a/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
+++ b/superset/assets/src/SqlLab/components/TabbedSqlEditors.jsx
@@ -39,7 +39,6 @@ const propTypes = {
   queryEditors: PropTypes.array,
   tabHistory: PropTypes.array.isRequired,
   tables: PropTypes.array.isRequired,
-  getHeight: PropTypes.func.isRequired,
   offline: PropTypes.bool,
 };
 const defaultProps = {
@@ -238,7 +237,6 @@ class TabbedSqlEditors extends React.PureComponent {
             <div className="panel-body">
               {isSelected && (
                 <SqlEditor
-                  getHeight={this.props.getHeight}
                   tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)}
                   queryEditor={qe}
                   editorQueries={this.state.queriesArray}
diff --git a/superset/assets/src/SqlLab/main.less b/superset/assets/src/SqlLab/main.less
index cd2cdd8..28755a3 100644
--- a/superset/assets/src/SqlLab/main.less
+++ b/superset/assets/src/SqlLab/main.less
@@ -135,7 +135,8 @@ div.Workspace {
     background-color: #e8e8e8;
     display: flex;
     justify-content: space-between;
-    border-bottom: 2px solid #ccc;
+    border: 1px solid #ccc;
+    border-top: 0;
 
     form {
         margin-block-end: 0;
@@ -193,21 +194,67 @@ div.Workspace {
     background-color: transparent !important;
 }
 
+.SqlLab {
+    .tab-content {
+        height: 100%;
+    }
+
+    #brace-editor {
+        height: calc(100% - 51px);
+    }
+
+    .ace_content {
+        height: 100%;
+    }
+
+    .SouthPane {
+        height: 100%;
+    }
+}
+
 .SqlEditor {
-    .Resizer {
-        -moz-box-sizing: border-box;
-        -webkit-box-sizing: border-box;
-        box-sizing: border-box;
+    display: flex;
+    flex-direction: row;
+    height: 100%;
+
+    .schemaPane {
+        flex-grow: 1;
+        transition: all .3s ease-in-out;
     }
 
-    .Resizer.horizontal {
-        height: 4px;
+    .schemaPane-enter-done, .schemaPane-exit {
+        transform: translateX(0);
+    }
+
+    .schemaPane-enter-active, .schemaPane-exit-active {
+        transform: translateX(-50%);
+    }
+
+    .schemaPane-enter, .schemaPane-exit-done {
+        transform: translateX(-100%);
+        max-width: 0;
+        overflow: hidden;
+    }
+
+    .queryPane {
+        flex-grow: 8;
+        position: relative;
+        margin-left: 15px;
+    }
+
+    .schemaPane-exit-done + .queryPane {
+        margin-left: 0;
+    }
+
+    .gutter {
         border-top: 1px solid #ccc;
         border-bottom: 1px solid #ccc;
+        width: 3%;
+        margin: 3px 47%;
+    }
+
+    .gutter.gutter-vertical {
         cursor: row-resize;
-        width: 4%;
-        margin-top: 4px;
-        margin-left: 47%;
     }
 }
 
@@ -298,9 +345,6 @@ a.Link {
 .tooltip-inner {
     max-width: 500px;
 }
-.SplitPane.horizontal {
-    padding-right: 4px;
-}
 .SouthPane {
     margin-top: 10px;
     position: absolute;
diff --git a/superset/assets/src/components/AsyncSelect.jsx b/superset/assets/src/components/AsyncSelect.jsx
index 1106402..da32fab 100644
--- a/superset/assets/src/components/AsyncSelect.jsx
+++ b/superset/assets/src/components/AsyncSelect.jsx
@@ -83,17 +83,15 @@ class AsyncSelect extends React.PureComponent {
 
   render() {
     return (
-      <div>
-        <Select
-          placeholder={this.props.placeholder}
-          options={this.state.options}
-          value={this.props.value}
-          isLoading={this.state.isLoading}
-          onChange={this.onChange}
-          valueRenderer={this.props.valueRenderer}
-          {...this.props}
-        />
-      </div>
+      <Select
+        placeholder={this.props.placeholder}
+        options={this.state.options}
+        value={this.props.value}
+        isLoading={this.state.isLoading}
+        onChange={this.onChange}
+        valueRenderer={this.props.valueRenderer}
+        {...this.props}
+      />
     );
   }
 }
diff --git a/superset/assets/src/components/TableSelector.css b/superset/assets/src/components/TableSelector.css
index b4636de..41b8d11 100644
--- a/superset/assets/src/components/TableSelector.css
+++ b/superset/assets/src/components/TableSelector.css
@@ -17,8 +17,22 @@
  * under the License.
  */
 .TableSelector .fa-refresh {
-  padding-top: 7px
+  padding-left: 9px;
 }
 .TableSelector .refresh-col {
-  padding-left: 0px;
+  display: flex;
+  align-items: center;
+  width: 30px;
+}
+.TableSelector .section {
+  padding-bottom: 5px;
+  display: flex;
+  flex-direction: row;
+}
+.TableSelector .select {
+  flex-grow: 1;
+}
+.TableSelector .divider {
+  border-bottom: 1px solid #f2f2f2;
+  margin: 10px 0;
 }
diff --git a/superset/assets/src/components/TableSelector.jsx b/superset/assets/src/components/TableSelector.jsx
index 9031d1a..6130fc6 100644
--- a/superset/assets/src/components/TableSelector.jsx
+++ b/superset/assets/src/components/TableSelector.jsx
@@ -20,7 +20,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import Select from 'react-virtualized-select';
 import createFilterOptions from 'react-select-fast-filter-options';
-import { ControlLabel, Col, Label, Row } from 'react-bootstrap';
+import { ControlLabel, Label } from 'react-bootstrap';
 import { t } from '@superset-ui/translation';
 import { SupersetClient } from '@superset-ui/connection';
 
@@ -38,7 +38,6 @@ const propTypes = {
   tableNameSticky: PropTypes.bool,
   tableName: PropTypes.string,
   database: PropTypes.object,
-  horizontal: PropTypes.bool,
   sqlLabMode: PropTypes.bool,
   onChange: PropTypes.func,
   clearable: PropTypes.bool,
@@ -52,7 +51,6 @@ const defaultProps = {
   onTableChange: () => {},
   onChange: () => {},
   tableNameSticky: true,
-  horizontal: false,
   sqlLabMode: true,
   clearable: true,
 };
@@ -199,10 +197,10 @@ export default class TableSelector extends React.PureComponent {
   }
   renderSelectRow(select, refreshBtn) {
     return (
-      <Row>
-        <Col md={11}>{select}</Col>
-        <Col md={1} className="refresh-col">{refreshBtn}</Col>
-      </Row>
+      <div className="section">
+        <span className="select">{select}</span>
+        <span className="refresh-col">{refreshBtn}</span>
+      </div>
     );
   }
   renderDatabaseSelect() {
@@ -232,29 +230,25 @@ export default class TableSelector extends React.PureComponent {
       />);
   }
   renderSchema() {
-    return (
-      <div className="m-t-5">
-        {this.renderSelectRow(
-          <Select
-            name="select-schema"
-            placeholder={t('Select a schema (%s)', this.state.schemaOptions.length)}
-            options={this.state.schemaOptions}
-            value={this.props.schema}
-            valueRenderer={o => (
-              <div>
-                <span className="text-muted">{t('Schema:')}</span> {o.label}
-              </div>
-            )}
-            isLoading={this.state.schemaLoading}
-            autosize={false}
-            onChange={this.changeSchema}
-          />,
-          <RefreshLabel
-            onClick={() => this.onDatabaseChange({ id: this.props.dbId }, true)}
-            tooltipContent={t('Force refresh schema list')}
-          />,
+    return this.renderSelectRow(
+      <Select
+        name="select-schema"
+        placeholder={t('Select a schema (%s)', this.state.schemaOptions.length)}
+        options={this.state.schemaOptions}
+        value={this.props.schema}
+        valueRenderer={o => (
+          <div>
+            <span className="text-muted">{t('Schema:')}</span> {o.label}
+          </div>
         )}
-      </div>
+        isLoading={this.state.schemaLoading}
+        autosize={false}
+        onChange={this.changeSchema}
+      />,
+      <RefreshLabel
+        onClick={() => this.onDatabaseChange({ id: this.props.dbId }, true)}
+        tooltipContent={t('Force refresh schema list')}
+      />,
     );
   }
   renderTable() {
@@ -290,20 +284,16 @@ export default class TableSelector extends React.PureComponent {
           value={this.state.tableName}
           loadOptions={this.getTableNamesBySubStr}
         />);
-    return (
-      <div className="m-t-5">
-        {this.renderSelectRow(
-          select,
-          <RefreshLabel
-            onClick={() => this.changeSchema({ value: this.props.schema }, true)}
-            tooltipContent={t('Force refresh table list')}
-          />)}
-      </div>);
+    return this.renderSelectRow(
+      select,
+      <RefreshLabel
+        onClick={() => this.changeSchema({ value: this.props.schema }, true)}
+        tooltipContent={t('Force refresh table list')}
+      />);
   }
   renderSeeTableLabel() {
     return (
-      <div>
-        <hr />
+      <div className="section">
         <ControlLabel>
           {t('See table schema')}{' '}
           <small>
@@ -319,18 +309,11 @@ export default class TableSelector extends React.PureComponent {
   render() {
     return (
       <div className="TableSelector">
-        {this.props.horizontal ?
-          <div>
-            <Col md={4}>{this.renderDatabaseSelect()}</Col>
-            <Col md={4}>{this.renderSchema()}</Col>
-            <Col md={4}>{this.renderTable()}</Col>
-          </div> :
-          <div>
-            <div>{this.renderDatabaseSelect()}</div>
-            <div className="m-t-5">{this.renderSchema()}</div>
-            {this.props.sqlLabMode && this.renderSeeTableLabel()}
-            <div className="m-t-5">{this.renderTable()}</div>
-          </div>}
+        {this.renderDatabaseSelect()}
+        {this.renderSchema()}
+        <div className="divider" />
+        {this.props.sqlLabMode && this.renderSeeTableLabel()}
+        {this.renderTable()}
       </div>
     );
   }


Mime
View raw message