You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2020/10/31 05:06:04 UTC

[incubator-superset] branch master updated: refactor: Replace react-bootstrap tabs with Antd tabs (#11118)

This is an automated email from the ASF dual-hosted git repository.

rusackas 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 55a3404  refactor: Replace react-bootstrap tabs with Antd tabs (#11118)
55a3404 is described below

commit 55a3404b71232a1cfd6d978f666945cde37c99e2
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Sat Oct 31 06:05:31 2020 +0100

    refactor: Replace react-bootstrap tabs with Antd tabs (#11118)
    
    * Replace tabs in BuilderComponentPane
    
    * Replace tabs in ControlPanelsContainer
    
    * Replace tabs in AdhocMetricEditPopover
    
    * Replace Tabs in DatasourceEditor
    
    * Replace tabs in AdhocFilterEditPopover
    
    * Replace tabs in DateFilterControl
    
    * Bug fix
    
    * Change Tab styles
    
    * Fix tests
    
    * Fix cypress tests
    
    * Lint fix
    
    * Fix tests
    
    * Change Tabs style in ControlPanelsContainer
    
    * Change tabs content height
    
    * Lint fix
    
    * Add data test
    
    * Fix e2e test
    
    * Move Tabs file to separate dir
    
    * Fix after rebase
    
    * Fix e2e tests
    
    * Fix after rebase
---
 .../integration/dashboard/edit_mode.test.js        |   5 +-
 .../integration/explore/AdhocFilters.test.ts       |   6 +
 .../integration/explore/AdhocMetrics.test.ts       |   8 --
 .../cypress/integration/explore/control.test.ts    |   6 +-
 .../datasource/DatasourceEditor_spec.jsx           |   2 +-
 .../components/AdhocFilterEditPopover_spec.jsx     |   6 +-
 .../explore/components/DateFilterControl_spec.jsx  |  11 +-
 .../src/common/components/{ => Tabs}/Tabs.tsx      |  16 ++-
 .../src/common/components/Tabs/index.ts            |  20 +++
 .../dashboard/components/BuilderComponentPane.jsx  |  13 +-
 .../src/datasource/DatasourceEditor.jsx            | 140 ++++++++++-----------
 .../explore/components/AdhocFilterEditPopover.jsx  |  20 +--
 .../explore/components/AdhocMetricEditPopover.jsx  |  24 ++--
 .../explore/components/ControlPanelsContainer.jsx  |  34 +++--
 .../components/controls/DateFilterControl.jsx      |  17 +--
 15 files changed, 188 insertions(+), 140 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
index c628837..4832c44 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/edit_mode.test.js
@@ -47,8 +47,9 @@ describe('Dashboard edit mode', () => {
       });
 
     cy.get('[data-test="dashboard-builder-component-pane-tabs-navigation"]')
-      .children()
-      .last()
+      .within(() => {
+        cy.get('.ant-tabs-tab').last();
+      })
       .click();
 
     // find box plot is available from list
diff --git a/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts
index 9b7c5ae..d9057d3 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/AdhocFilters.test.ts
@@ -43,6 +43,12 @@ describe('AdhocFilters', () => {
       cy.get('input[type=text]').focus().type('name{enter}');
     });
 
+    // antd tabs do lazy loading, so we need to click on tab with ace editor
+    cy.get('#filter-edit-popover').within(() => {
+      cy.get('.ant-tabs-tab').contains('Custom SQL').click();
+      cy.get('.ant-tabs-tab').contains('Simple').click();
+    });
+
     cy.get('script').then(nodes => {
       // should load new script chunks for SQL editor
       expect(nodes.length).to.greaterThan(numScripts);
diff --git a/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts
index e839fc2..7c085c3 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/AdhocMetrics.test.ts
@@ -72,11 +72,6 @@ describe('AdhocMetrics', () => {
       .should('have.text', 'num')
       .click();
 
-    cy.get('[data-test=option-label]')
-      .should('have.text', 'SUM(num)')
-      .first()
-      .click();
-
     // add custom SQL
     cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
     cy.get('[data-test=metrics-edit-popover]').within(() => {
@@ -103,9 +98,6 @@ describe('AdhocMetrics', () => {
     cy.get('[data-test=metrics]')
       .find('[data-test="metric-option"]')
       .should('have.length', 2);
-    cy.get('[data-test=metrics]').within(() => {
-      cy.contains('[data-test="metric-option"]', 'SUM(sum_girls)').click();
-    });
 
     cy.get('#metrics-edit-popover').within(() => {
       cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
diff --git a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
index 4cf22df..e4bc157f 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts
@@ -70,7 +70,9 @@ describe('Datasource control', () => {
     cy.get('[data-test="datasource-menu-trigger"]').click();
     cy.get('[data-test="edit-dataset"]').click();
     cy.get('.ant-modal-content').within(() => {
-      cy.get('a[role="tab"]').contains('Metrics').click();
+      cy.get('[data-test="collection-tab-Metrics"]')
+        .contains('Metrics')
+        .click();
     });
     cy.get(`input[value="${newMetricName}"]`)
       .closest('tr')
@@ -140,7 +142,7 @@ describe('Time range filter', () => {
     });
 
     cy.get('#filter-popover').within(() => {
-      cy.get('div.tab-pane.active').within(() => {
+      cy.get('div.ant-tabs-tabpane-active').within(() => {
         cy.get('div.PopoverSection :not(.dimmed)').within(() => {
           cy.get('input[value="100 years ago"]');
           cy.get('input[value="now"]');
diff --git a/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx b/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx
index ce0d3ea..7fb2b2a 100644
--- a/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx
+++ b/superset-frontend/spec/javascripts/datasource/DatasourceEditor_spec.jsx
@@ -17,12 +17,12 @@
  * under the License.
  */
 import React from 'react';
-import { Tabs } from 'react-bootstrap';
 import { shallow } from 'enzyme';
 import configureStore from 'redux-mock-store';
 import fetchMock from 'fetch-mock';
 import thunk from 'redux-thunk';
 
+import Tabs from 'src/common/components/Tabs';
 import DatasourceEditor from 'src/datasource/DatasourceEditor';
 import Field from 'src/CRUD/Field';
 import mockDatasource from '../../fixtures/mockDatasource';
diff --git a/superset-frontend/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx b/superset-frontend/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx
index c8e91c6..c28ac9a 100644
--- a/superset-frontend/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx
+++ b/superset-frontend/spec/javascripts/explore/components/AdhocFilterEditPopover_spec.jsx
@@ -20,9 +20,9 @@
 import React from 'react';
 import sinon from 'sinon';
 import { shallow } from 'enzyme';
-import { Tab, Tabs } from 'react-bootstrap';
 import Button from 'src/components/Button';
 
+import Tabs from 'src/common/components/Tabs';
 import AdhocFilter, {
   EXPRESSION_TYPES,
   CLAUSES,
@@ -82,7 +82,7 @@ describe('AdhocFilterEditPopover', () => {
   it('renders simple tab content by default', () => {
     const { wrapper } = setup();
     expect(wrapper.find(Tabs)).toExist();
-    expect(wrapper.find(Tab)).toHaveLength(2);
+    expect(wrapper.find(Tabs.TabPane)).toHaveLength(2);
     expect(wrapper.find(Button)).toHaveLength(2);
     expect(wrapper.find(AdhocFilterEditPopoverSimpleTabContent)).toHaveLength(
       1,
@@ -92,7 +92,7 @@ describe('AdhocFilterEditPopover', () => {
   it('renders sql tab content when the adhoc filter expressionType is sql', () => {
     const { wrapper } = setup({ adhocFilter: sqlAdhocFilter });
     expect(wrapper.find(Tabs)).toExist();
-    expect(wrapper.find(Tab)).toHaveLength(2);
+    expect(wrapper.find(Tabs.TabPane)).toHaveLength(2);
     expect(wrapper.find(Button)).toHaveLength(2);
     expect(wrapper.find(AdhocFilterEditPopoverSqlTabContent)).toExist();
   });
diff --git a/superset-frontend/spec/javascripts/explore/components/DateFilterControl_spec.jsx b/superset-frontend/spec/javascripts/explore/components/DateFilterControl_spec.jsx
index fd1984d..49ac950 100644
--- a/superset-frontend/spec/javascripts/explore/components/DateFilterControl_spec.jsx
+++ b/superset-frontend/spec/javascripts/explore/components/DateFilterControl_spec.jsx
@@ -18,11 +18,12 @@
  */
 /* eslint-disable no-unused-expressions */
 import React from 'react';
-import { OverlayTrigger, Tab, Tabs, Radio } from 'react-bootstrap';
+import { OverlayTrigger, Radio } from 'react-bootstrap';
 import sinon from 'sinon';
 import { styledMount as mount } from 'spec/helpers/theming';
 
 import Popover from 'src/common/components/Popover';
+import Tabs from 'src/common/components/Tabs';
 import Label from 'src/components/Label';
 import DateFilterControl from 'src/explore/components/controls/DateFilterControl';
 import ControlHeader from 'src/explore/components/ControlHeader';
@@ -85,13 +86,13 @@ describe('DateFilterControl', () => {
     const popoverContentWrapper = mount(popoverContent);
 
     expect(popoverContentWrapper.find(Tabs)).toExist();
-    expect(popoverContentWrapper.find(Tab)).toHaveLength(2);
+    expect(popoverContentWrapper.find(Tabs.TabPane)).toHaveLength(2);
   });
 
   it('renders default time options', () => {
     const popoverContent = wrapper.find(Popover).first().props().content;
     const popoverContentWrapper = mount(popoverContent);
-    const defaultTab = popoverContentWrapper.find(Tab).first();
+    const defaultTab = popoverContentWrapper.find(Tabs.TabPane).first();
 
     expect(defaultTab.find(Radio)).toExist();
     expect(defaultTab.find(Radio)).toHaveLength(6);
@@ -100,7 +101,7 @@ describe('DateFilterControl', () => {
   it('renders tooltips over timeframe options', () => {
     const popoverContent = wrapper.find(Popover).first().props().content;
     const popoverContentWrapper = mount(popoverContent);
-    const defaultTab = popoverContentWrapper.find(Tab).first();
+    const defaultTab = popoverContentWrapper.find(Tabs.TabPane).first();
     const radioTrigger = defaultTab.find(OverlayTrigger);
 
     expect(radioTrigger).toExist();
@@ -110,7 +111,7 @@ describe('DateFilterControl', () => {
   it('renders the correct time range in tooltip', () => {
     const popoverContent = wrapper.find(Popover).first().props().content;
     const popoverContentWrapper = mount(popoverContent);
-    const defaultTab = popoverContentWrapper.find(Tab).first();
+    const defaultTab = popoverContentWrapper.find(Tabs.TabPane).first();
     const triggers = defaultTab.find(OverlayTrigger);
 
     const expectedLabels = {
diff --git a/superset-frontend/src/common/components/Tabs.tsx b/superset-frontend/src/common/components/Tabs/Tabs.tsx
similarity index 91%
rename from superset-frontend/src/common/components/Tabs.tsx
rename to superset-frontend/src/common/components/Tabs/Tabs.tsx
index 7627354..027f286 100644
--- a/superset-frontend/src/common/components/Tabs.tsx
+++ b/superset-frontend/src/common/components/Tabs/Tabs.tsx
@@ -30,6 +30,10 @@ const notForwardedProps = ['fullWidth'];
 const StyledTabs = styled(AntdTabs, {
   shouldForwardProp: prop => !notForwardedProps.includes(prop),
 })<TabsProps>`
+  .ant-tabs-content-holder {
+    overflow: auto;
+  }
+
   .ant-tabs-tab {
     flex: 1 1 auto;
 
@@ -120,5 +124,15 @@ EditableTabs.TabPane.defaultProps = {
   ),
 };
 
+const StyledCardTabs = styled(EditableTabs)``;
+
+const CardTabs = Object.assign(StyledCardTabs, {
+  TabPane: StyledTabPane,
+});
+
+CardTabs.defaultProps = {
+  type: 'card',
+};
+
 export default Tabs;
-export { EditableTabs };
+export { CardTabs, EditableTabs };
diff --git a/superset-frontend/src/common/components/Tabs/index.ts b/superset-frontend/src/common/components/Tabs/index.ts
new file mode 100644
index 0000000..32bc0c3
--- /dev/null
+++ b/superset-frontend/src/common/components/Tabs/index.ts
@@ -0,0 +1,20 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export * from './Tabs';
+export { default } from './Tabs';
diff --git a/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx b/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx
index df18290..a693f87 100644
--- a/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx
+++ b/superset-frontend/src/dashboard/components/BuilderComponentPane.jsx
@@ -19,7 +19,7 @@
 /* eslint-env browser */
 import PropTypes from 'prop-types';
 import React from 'react';
-import { Tabs, Tab } from 'react-bootstrap';
+import Tabs from 'src/common/components/Tabs';
 import { StickyContainer, Sticky } from 'react-sticky';
 import { ParentSize } from '@vx/responsive';
 
@@ -48,23 +48,24 @@ class BuilderComponentPane extends React.PureComponent {
     const { isSticky } = this.props;
     return (
       <Tabs
-        className="m-t-10 tabs-components"
         id="tabs"
+        className="tabs-components"
+        style={{ marginTop: '10px' }}
         data-test="dashboard-builder-component-pane-tabs-navigation"
       >
-        <Tab eventKey={1} title={t('Components')}>
+        <Tabs.TabPane key={1} tab={t('Components')}>
           <NewTabs />
           <NewRow />
           <NewColumn />
           <NewHeader />
           <NewMarkdown />
           <NewDivider />
-        </Tab>
-        <Tab eventKey={2} title={t('Charts')} className="tab-charts">
+        </Tabs.TabPane>
+        <Tabs.TabPane key={2} tab={t('Charts')} className="tab-charts">
           <SliceAdder
             height={height + (isSticky ? SUPERSET_HEADER_HEIGHT : 0)}
           />
-        </Tab>
+        </Tabs.TabPane>
       </Tabs>
     );
   }
diff --git a/superset-frontend/src/datasource/DatasourceEditor.jsx b/superset-frontend/src/datasource/DatasourceEditor.jsx
index 30fb17a..5530662 100644
--- a/superset-frontend/src/datasource/DatasourceEditor.jsx
+++ b/superset-frontend/src/datasource/DatasourceEditor.jsx
@@ -18,10 +18,11 @@
  */
 import React from 'react';
 import PropTypes from 'prop-types';
-import { Alert, Badge, Col, Radio, Tabs, Tab, Well } from 'react-bootstrap';
+import { Alert, Badge, Col, Radio, Well } from 'react-bootstrap';
 import shortid from 'shortid';
 import { styled, SupersetClient, t } from '@superset-ui/core';
 
+import Tabs from 'src/common/components/Tabs';
 import Button from 'src/components/Button';
 import CertifiedIconWithTooltip from 'src/components/CertifiedIconWithTooltip';
 import DatabaseSelector from 'src/components/DatabaseSelector';
@@ -596,11 +597,9 @@ class DatasourceEditor extends React.PureComponent {
     const { datasource } = this.state;
     const { spatials, all_cols: allCols } = datasource;
     return (
-      <Tab
-        title={
-          <CollectionTabTitle collection={spatials} title={t('Spatial')} />
-        }
-        eventKey={4}
+      <Tabs.TabPane
+        tab={<CollectionTabTitle collection={spatials} title={t('Spatial')} />}
+        key={4}
       >
         <CollectionTable
           tableColumns={['name', 'config']}
@@ -621,7 +620,7 @@ class DatasourceEditor extends React.PureComponent {
             ),
           }}
         />
-      </Tab>
+      </Tabs.TabPane>
     );
   }
 
@@ -905,94 +904,89 @@ class DatasourceEditor extends React.PureComponent {
           </Alert>
         </div>
         <Tabs
+          fullWidth={false}
           id="table-tabs"
           data-test="edit-dataset-tabs"
-          onSelect={this.handleTabSelect}
+          onChange={this.handleTabSelect}
           defaultActiveKey={activeTabKey}
         >
-          <Tab eventKey={0} title={t('Source')}>
-            {activeTabKey === 0 && this.renderSourceFieldset()}
-          </Tab>
-          <Tab
-            title={
+          <Tabs.TabPane key={0} tab={t('Source')}>
+            {this.renderSourceFieldset()}
+          </Tabs.TabPane>
+          <Tabs.TabPane
+            tab={
               <CollectionTabTitle
                 collection={datasource.metrics}
                 title={t('Metrics')}
               />
             }
-            eventKey={1}
+            key={1}
           >
-            {activeTabKey === 1 && this.renderMetricCollection()}
-          </Tab>
-          <Tab
-            title={
+            {this.renderMetricCollection()}
+          </Tabs.TabPane>
+          <Tabs.TabPane
+            tab={
               <CollectionTabTitle
                 collection={this.state.databaseColumns}
                 title={t('Columns')}
               />
             }
-            eventKey={2}
+            key={2}
           >
-            {activeTabKey === 2 && (
-              <div>
-                <ColumnCollectionTable
-                  columns={this.state.databaseColumns}
-                  onChange={databaseColumns =>
-                    this.setColumns({ databaseColumns })
-                  }
-                />
-                <Button
-                  buttonStyle="primary"
-                  onClick={this.syncMetadata}
-                  className="sync-from-source"
-                >
-                  {t('Sync columns from source')}
-                </Button>
-                {this.state.metadataLoading && <Loading />}
-              </div>
-            )}
-          </Tab>
-          <Tab
-            title={
+            <div>
+              <ColumnCollectionTable
+                columns={this.state.databaseColumns}
+                onChange={databaseColumns =>
+                  this.setColumns({ databaseColumns })
+                }
+              />
+              <Button
+                buttonStyle="primary"
+                onClick={this.syncMetadata}
+                className="sync-from-source"
+              >
+                {t('Sync columns from source')}
+              </Button>
+              {this.state.metadataLoading && <Loading />}
+            </div>
+          </Tabs.TabPane>
+          <Tabs.TabPane
+            tab={
               <CollectionTabTitle
                 collection={this.state.calculatedColumns}
                 title={t('Calculated Columns')}
               />
             }
-            eventKey={3}
+            key={3}
           >
-            {activeTabKey === 3 && (
-              <ColumnCollectionTable
-                columns={this.state.calculatedColumns}
-                onChange={calculatedColumns =>
-                  this.setColumns({ calculatedColumns })
-                }
-                editableColumnName
-                showExpression
-                allowAddItem
-                allowEditDataType
-                itemGenerator={() => ({
-                  column_name: '<new column>',
-                  filterable: true,
-                  groupby: true,
-                  expression: '<enter SQL expression here>',
-                  __expanded: true,
-                })}
-              />
-            )}
-          </Tab>
-          <Tab eventKey={4} title={t('Settings')}>
-            {activeTabKey === 4 && (
-              <div>
-                <Col md={6}>
-                  <FormContainer>{this.renderSettingsFieldset()}</FormContainer>
-                </Col>
-                <Col md={6}>
-                  <FormContainer>{this.renderAdvancedFieldset()}</FormContainer>
-                </Col>
-              </div>
-            )}
-          </Tab>
+            <ColumnCollectionTable
+              columns={this.state.calculatedColumns}
+              onChange={calculatedColumns =>
+                this.setColumns({ calculatedColumns })
+              }
+              editableColumnName
+              showExpression
+              allowAddItem
+              allowEditDataType
+              itemGenerator={() => ({
+                column_name: '<new column>',
+                filterable: true,
+                groupby: true,
+                expression: '<enter SQL expression here>',
+                __expanded: true,
+              })}
+            />
+          </Tabs.TabPane>
+          <Tabs.TabPane key={4} tab={t('Settings')}>
+            <div>
+              <Col md={6}>
+                <FormContainer>{this.renderSettingsFieldset()}</FormContainer>
+              </Col>
+              <Col md={6}>
+                <FormContainer>{this.renderAdvancedFieldset()}</FormContainer>
+              </Col>
+            </div>
+          </Tabs.TabPane>
         </Tabs>
       </DatasourceContainer>
     );
diff --git a/superset-frontend/src/explore/components/AdhocFilterEditPopover.jsx b/superset-frontend/src/explore/components/AdhocFilterEditPopover.jsx
index 34f885c..11b5b69 100644
--- a/superset-frontend/src/explore/components/AdhocFilterEditPopover.jsx
+++ b/superset-frontend/src/explore/components/AdhocFilterEditPopover.jsx
@@ -18,10 +18,10 @@
  */
 import React from 'react';
 import PropTypes from 'prop-types';
-import { Tab, Tabs } from 'react-bootstrap';
 import Button from 'src/components/Button';
 import { t } from '@superset-ui/core';
 
+import Tabs from 'src/common/components/Tabs';
 import columnType from '../propTypes/columnType';
 import adhocMetricType from '../propTypes/adhocMetricType';
 import AdhocFilter, { EXPRESSION_TYPES } from '../AdhocFilter';
@@ -46,7 +46,7 @@ const propTypes = {
 };
 
 const startingWidth = 300;
-const startingHeight = 190;
+const startingHeight = 240;
 
 export default class AdhocFilterEditPopover extends React.Component {
   constructor(props) {
@@ -144,10 +144,10 @@ export default class AdhocFilterEditPopover extends React.Component {
           data-test="adhoc-filter-edit-tabs"
           style={{ height: this.state.height, width: this.state.width }}
         >
-          <Tab
+          <Tabs.TabPane
             className="adhoc-filter-edit-tab"
-            eventKey={EXPRESSION_TYPES.SIMPLE}
-            title="Simple"
+            key={EXPRESSION_TYPES.SIMPLE}
+            tab="Simple"
           >
             <AdhocFilterEditPopoverSimpleTabContent
               adhocFilter={this.state.adhocFilter}
@@ -157,11 +157,11 @@ export default class AdhocFilterEditPopover extends React.Component {
               onHeightChange={this.adjustHeight}
               partitionColumn={partitionColumn}
             />
-          </Tab>
-          <Tab
+          </Tabs.TabPane>
+          <Tabs.TabPane
             className="adhoc-filter-edit-tab"
-            eventKey={EXPRESSION_TYPES.SQL}
-            title="Custom SQL"
+            key={EXPRESSION_TYPES.SQL}
+            tab="Custom SQL"
           >
             {!this.props.datasource ||
             this.props.datasource.type !== 'druid' ? (
@@ -176,7 +176,7 @@ export default class AdhocFilterEditPopover extends React.Component {
                 Custom SQL Filters are not available on druid datasources
               </div>
             )}
-          </Tab>
+          </Tabs.TabPane>
         </Tabs>
         <div>
           <Button buttonSize="small" onClick={this.props.onClose} cta>
diff --git a/superset-frontend/src/explore/components/AdhocMetricEditPopover.jsx b/superset-frontend/src/explore/components/AdhocMetricEditPopover.jsx
index 4ee85a3..d53d9df 100644
--- a/superset-frontend/src/explore/components/AdhocMetricEditPopover.jsx
+++ b/superset-frontend/src/explore/components/AdhocMetricEditPopover.jsx
@@ -18,7 +18,8 @@
  */
 import React from 'react';
 import PropTypes from 'prop-types';
-import { FormGroup, Tab, Tabs } from 'react-bootstrap';
+import { FormGroup } from 'react-bootstrap';
+import Tabs from 'src/common/components/Tabs';
 import Button from 'src/components/Button';
 import Select from 'src/components/Select';
 import { t } from '@superset-ui/core';
@@ -50,7 +51,7 @@ const defaultProps = {
 };
 
 const startingWidth = 300;
-const startingHeight = 180;
+const startingHeight = 240;
 
 export default class AdhocMetricEditPopover extends React.Component {
   constructor(props) {
@@ -222,13 +223,12 @@ export default class AdhocMetricEditPopover extends React.Component {
           defaultActiveKey={adhocMetric.expressionType}
           className="adhoc-metric-edit-tabs"
           style={{ height: this.state.height, width: this.state.width }}
-          onSelect={this.refreshAceEditor}
-          animation={false}
+          onChange={this.refreshAceEditor}
         >
-          <Tab
+          <Tabs.TabPane
             className="adhoc-metric-edit-tab"
-            eventKey={EXPRESSION_TYPES.SIMPLE}
-            title="Simple"
+            key={EXPRESSION_TYPES.SIMPLE}
+            tab="Simple"
           >
             <FormGroup>
               <FormLabel>
@@ -251,11 +251,11 @@ export default class AdhocMetricEditPopover extends React.Component {
                 autoFocus
               />
             </FormGroup>
-          </Tab>
-          <Tab
+          </Tabs.TabPane>
+          <Tabs.TabPane
             className="adhoc-metric-edit-tab"
-            eventKey={EXPRESSION_TYPES.SQL}
-            title="Custom SQL"
+            key={EXPRESSION_TYPES.SQL}
+            tab="Custom SQL"
             data-test="adhoc-metric-edit-tab#custom"
           >
             {this.props.datasourceType !== 'druid' ? (
@@ -282,7 +282,7 @@ export default class AdhocMetricEditPopover extends React.Component {
                 Custom SQL Metrics are not available on druid datasources
               </div>
             )}
-          </Tab>
+          </Tabs.TabPane>
         </Tabs>
         <div>
           <Button
diff --git a/superset-frontend/src/explore/components/ControlPanelsContainer.jsx b/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
index e834c6d..eeb3ce4 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.jsx
@@ -21,9 +21,11 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { bindActionCreators } from 'redux';
 import { connect } from 'react-redux';
-import { Alert, Tab, Tabs } from 'react-bootstrap';
+import { Alert } from 'react-bootstrap';
+import { css } from '@emotion/core';
 import { t, styled } from '@superset-ui/core';
 
+import Tabs from 'src/common/components/Tabs';
 import ControlPanelSection from './ControlPanelSection';
 import ControlRow from './ControlRow';
 import Control from './Control';
@@ -44,7 +46,7 @@ const Styles = styled.div`
   height: 100%;
   max-height: 100%;
   .remove-alert {
-    cursor: 'pointer';
+    cursor: pointer;
   }
   #controlSections {
     display: flex;
@@ -61,6 +63,15 @@ const Styles = styled.div`
   }
 `;
 
+const ControlPanelsTabs = styled(Tabs)`
+  ${({ fullWidth }) =>
+    css`
+      .ant-tabs-nav-list {
+        width: ${fullWidth ? '100%' : '50%'};
+      }
+    `}
+`;
+
 class ControlPanelsContainer extends React.Component {
   constructor(props) {
     super(props);
@@ -193,6 +204,7 @@ class ControlPanelsContainer extends React.Component {
       }
     });
 
+    const showCustomizeTab = displaySectionsToRender.length > 0;
     return (
       <Styles>
         {this.props.alert && (
@@ -208,16 +220,20 @@ class ControlPanelsContainer extends React.Component {
             />
           </Alert>
         )}
-        <Tabs id="controlSections" data-test="control-tabs">
-          <Tab eventKey="query" title={t('Data')}>
+        <ControlPanelsTabs
+          id="controlSections"
+          data-test="control-tabs"
+          fullWidth={showCustomizeTab}
+        >
+          <Tabs.TabPane key="query" tab={t('Data')}>
             {querySectionsToRender.map(this.renderControlPanelSection)}
-          </Tab>
-          {displaySectionsToRender.length > 0 && (
-            <Tab eventKey="display" title={t('Customize')}>
+          </Tabs.TabPane>
+          {showCustomizeTab && (
+            <Tabs.TabPane key="display" tab={t('Customize')}>
               {displaySectionsToRender.map(this.renderControlPanelSection)}
-            </Tab>
+            </Tabs.TabPane>
           )}
-        </Tabs>
+        </ControlPanelsTabs>
       </Styles>
     );
   }
diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl.jsx b/superset-frontend/src/explore/components/controls/DateFilterControl.jsx
index f7d498c..81053d0 100644
--- a/superset-frontend/src/explore/components/controls/DateFilterControl.jsx
+++ b/superset-frontend/src/explore/components/controls/DateFilterControl.jsx
@@ -26,8 +26,6 @@ import {
   MenuItem,
   OverlayTrigger,
   Radio,
-  Tab,
-  Tabs,
   Tooltip,
 } from 'react-bootstrap';
 import Popover from 'src/common/components/Popover';
@@ -37,6 +35,7 @@ import 'react-datetime/css/react-datetime.css';
 import moment from 'moment';
 import { t, styled, withTheme } from '@superset-ui/core';
 
+import Tabs from 'src/common/components/Tabs';
 import {
   buildTimeRangeString,
   formatTimeRange,
@@ -435,15 +434,17 @@ class DateFilterControl extends React.Component {
         }}
       >
         <Tabs
-          defaultActiveKey={this.state.tab === TABS.DEFAULTS ? 1 : 2}
+          defaultActiveKey={this.state.tab === TABS.DEFAULTS ? '1' : '2'}
           id="type"
           className="time-filter-tabs"
           onSelect={this.changeTab}
         >
-          <Tab eventKey={1} title="Defaults">
-            <FormGroup>{timeFrames}</FormGroup>
-          </Tab>
-          <Tab eventKey={2} title="Custom">
+          <Tabs.TabPane key="1" tab="Defaults" forceRender>
+            <div style={{ marginLeft: '8px' }}>
+              <FormGroup>{timeFrames}</FormGroup>
+            </div>
+          </Tabs.TabPane>
+          <Tabs.TabPane key="2" tab="Custom">
             <FormGroup>
               <PopoverSection
                 title="Relative to today"
@@ -573,7 +574,7 @@ class DateFilterControl extends React.Component {
                 </div>
               </PopoverSection>
             </FormGroup>
-          </Tab>
+          </Tabs.TabPane>
         </Tabs>
         <div className="clearfix">
           <Button