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
This commit is contained in:
Kamil Gabryjelski 2020-10-31 06:05:31 +01:00 committed by GitHub
parent 01ddbd0697
commit 55a3404b71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 188 additions and 140 deletions

View File

@ -47,8 +47,9 @@ describe('Dashboard edit mode', () => {
}); });
cy.get('[data-test="dashboard-builder-component-pane-tabs-navigation"]') cy.get('[data-test="dashboard-builder-component-pane-tabs-navigation"]')
.children() .within(() => {
.last() cy.get('.ant-tabs-tab').last();
})
.click(); .click();
// find box plot is available from list // find box plot is available from list

View File

@ -43,6 +43,12 @@ describe('AdhocFilters', () => {
cy.get('input[type=text]').focus().type('name{enter}'); 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 => { cy.get('script').then(nodes => {
// should load new script chunks for SQL editor // should load new script chunks for SQL editor
expect(nodes.length).to.greaterThan(numScripts); expect(nodes.length).to.greaterThan(numScripts);

View File

@ -72,11 +72,6 @@ describe('AdhocMetrics', () => {
.should('have.text', 'num') .should('have.text', 'num')
.click(); .click();
cy.get('[data-test=option-label]')
.should('have.text', 'SUM(num)')
.first()
.click();
// add custom SQL // add custom SQL
cy.get('#adhoc-metric-edit-tabs-tab-SQL').click(); cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();
cy.get('[data-test=metrics-edit-popover]').within(() => { cy.get('[data-test=metrics-edit-popover]').within(() => {
@ -103,9 +98,6 @@ describe('AdhocMetrics', () => {
cy.get('[data-test=metrics]') cy.get('[data-test=metrics]')
.find('[data-test="metric-option"]') .find('[data-test="metric-option"]')
.should('have.length', 2); .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('#metrics-edit-popover').within(() => {
cy.get('#adhoc-metric-edit-tabs-tab-SQL').click(); cy.get('#adhoc-metric-edit-tabs-tab-SQL').click();

View File

@ -70,7 +70,9 @@ describe('Datasource control', () => {
cy.get('[data-test="datasource-menu-trigger"]').click(); cy.get('[data-test="datasource-menu-trigger"]').click();
cy.get('[data-test="edit-dataset"]').click(); cy.get('[data-test="edit-dataset"]').click();
cy.get('.ant-modal-content').within(() => { 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}"]`) cy.get(`input[value="${newMetricName}"]`)
.closest('tr') .closest('tr')
@ -140,7 +142,7 @@ describe('Time range filter', () => {
}); });
cy.get('#filter-popover').within(() => { 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('div.PopoverSection :not(.dimmed)').within(() => {
cy.get('input[value="100 years ago"]'); cy.get('input[value="100 years ago"]');
cy.get('input[value="now"]'); cy.get('input[value="now"]');

View File

@ -17,12 +17,12 @@
* under the License. * under the License.
*/ */
import React from 'react'; import React from 'react';
import { Tabs } from 'react-bootstrap';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store'; import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock'; import fetchMock from 'fetch-mock';
import thunk from 'redux-thunk'; import thunk from 'redux-thunk';
import Tabs from 'src/common/components/Tabs';
import DatasourceEditor from 'src/datasource/DatasourceEditor'; import DatasourceEditor from 'src/datasource/DatasourceEditor';
import Field from 'src/CRUD/Field'; import Field from 'src/CRUD/Field';
import mockDatasource from '../../fixtures/mockDatasource'; import mockDatasource from '../../fixtures/mockDatasource';

View File

@ -20,9 +20,9 @@
import React from 'react'; import React from 'react';
import sinon from 'sinon'; import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { Tab, Tabs } from 'react-bootstrap';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import Tabs from 'src/common/components/Tabs';
import AdhocFilter, { import AdhocFilter, {
EXPRESSION_TYPES, EXPRESSION_TYPES,
CLAUSES, CLAUSES,
@ -82,7 +82,7 @@ describe('AdhocFilterEditPopover', () => {
it('renders simple tab content by default', () => { it('renders simple tab content by default', () => {
const { wrapper } = setup(); const { wrapper } = setup();
expect(wrapper.find(Tabs)).toExist(); 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(Button)).toHaveLength(2);
expect(wrapper.find(AdhocFilterEditPopoverSimpleTabContent)).toHaveLength( expect(wrapper.find(AdhocFilterEditPopoverSimpleTabContent)).toHaveLength(
1, 1,
@ -92,7 +92,7 @@ describe('AdhocFilterEditPopover', () => {
it('renders sql tab content when the adhoc filter expressionType is sql', () => { it('renders sql tab content when the adhoc filter expressionType is sql', () => {
const { wrapper } = setup({ adhocFilter: sqlAdhocFilter }); const { wrapper } = setup({ adhocFilter: sqlAdhocFilter });
expect(wrapper.find(Tabs)).toExist(); 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(Button)).toHaveLength(2);
expect(wrapper.find(AdhocFilterEditPopoverSqlTabContent)).toExist(); expect(wrapper.find(AdhocFilterEditPopoverSqlTabContent)).toExist();
}); });

View File

@ -18,11 +18,12 @@
*/ */
/* eslint-disable no-unused-expressions */ /* eslint-disable no-unused-expressions */
import React from 'react'; import React from 'react';
import { OverlayTrigger, Tab, Tabs, Radio } from 'react-bootstrap'; import { OverlayTrigger, Radio } from 'react-bootstrap';
import sinon from 'sinon'; import sinon from 'sinon';
import { styledMount as mount } from 'spec/helpers/theming'; import { styledMount as mount } from 'spec/helpers/theming';
import Popover from 'src/common/components/Popover'; import Popover from 'src/common/components/Popover';
import Tabs from 'src/common/components/Tabs';
import Label from 'src/components/Label'; import Label from 'src/components/Label';
import DateFilterControl from 'src/explore/components/controls/DateFilterControl'; import DateFilterControl from 'src/explore/components/controls/DateFilterControl';
import ControlHeader from 'src/explore/components/ControlHeader'; import ControlHeader from 'src/explore/components/ControlHeader';
@ -85,13 +86,13 @@ describe('DateFilterControl', () => {
const popoverContentWrapper = mount(popoverContent); const popoverContentWrapper = mount(popoverContent);
expect(popoverContentWrapper.find(Tabs)).toExist(); expect(popoverContentWrapper.find(Tabs)).toExist();
expect(popoverContentWrapper.find(Tab)).toHaveLength(2); expect(popoverContentWrapper.find(Tabs.TabPane)).toHaveLength(2);
}); });
it('renders default time options', () => { it('renders default time options', () => {
const popoverContent = wrapper.find(Popover).first().props().content; const popoverContent = wrapper.find(Popover).first().props().content;
const popoverContentWrapper = mount(popoverContent); 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)).toExist();
expect(defaultTab.find(Radio)).toHaveLength(6); expect(defaultTab.find(Radio)).toHaveLength(6);
@ -100,7 +101,7 @@ describe('DateFilterControl', () => {
it('renders tooltips over timeframe options', () => { it('renders tooltips over timeframe options', () => {
const popoverContent = wrapper.find(Popover).first().props().content; const popoverContent = wrapper.find(Popover).first().props().content;
const popoverContentWrapper = mount(popoverContent); const popoverContentWrapper = mount(popoverContent);
const defaultTab = popoverContentWrapper.find(Tab).first(); const defaultTab = popoverContentWrapper.find(Tabs.TabPane).first();
const radioTrigger = defaultTab.find(OverlayTrigger); const radioTrigger = defaultTab.find(OverlayTrigger);
expect(radioTrigger).toExist(); expect(radioTrigger).toExist();
@ -110,7 +111,7 @@ describe('DateFilterControl', () => {
it('renders the correct time range in tooltip', () => { it('renders the correct time range in tooltip', () => {
const popoverContent = wrapper.find(Popover).first().props().content; const popoverContent = wrapper.find(Popover).first().props().content;
const popoverContentWrapper = mount(popoverContent); const popoverContentWrapper = mount(popoverContent);
const defaultTab = popoverContentWrapper.find(Tab).first(); const defaultTab = popoverContentWrapper.find(Tabs.TabPane).first();
const triggers = defaultTab.find(OverlayTrigger); const triggers = defaultTab.find(OverlayTrigger);
const expectedLabels = { const expectedLabels = {

View File

@ -30,6 +30,10 @@ const notForwardedProps = ['fullWidth'];
const StyledTabs = styled(AntdTabs, { const StyledTabs = styled(AntdTabs, {
shouldForwardProp: prop => !notForwardedProps.includes(prop), shouldForwardProp: prop => !notForwardedProps.includes(prop),
})<TabsProps>` })<TabsProps>`
.ant-tabs-content-holder {
overflow: auto;
}
.ant-tabs-tab { .ant-tabs-tab {
flex: 1 1 auto; 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 default Tabs;
export { EditableTabs }; export { CardTabs, EditableTabs };

View File

@ -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';

View File

@ -19,7 +19,7 @@
/* eslint-env browser */ /* eslint-env browser */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import { Tabs, Tab } from 'react-bootstrap'; import Tabs from 'src/common/components/Tabs';
import { StickyContainer, Sticky } from 'react-sticky'; import { StickyContainer, Sticky } from 'react-sticky';
import { ParentSize } from '@vx/responsive'; import { ParentSize } from '@vx/responsive';
@ -48,23 +48,24 @@ class BuilderComponentPane extends React.PureComponent {
const { isSticky } = this.props; const { isSticky } = this.props;
return ( return (
<Tabs <Tabs
className="m-t-10 tabs-components"
id="tabs" id="tabs"
className="tabs-components"
style={{ marginTop: '10px' }}
data-test="dashboard-builder-component-pane-tabs-navigation" data-test="dashboard-builder-component-pane-tabs-navigation"
> >
<Tab eventKey={1} title={t('Components')}> <Tabs.TabPane key={1} tab={t('Components')}>
<NewTabs /> <NewTabs />
<NewRow /> <NewRow />
<NewColumn /> <NewColumn />
<NewHeader /> <NewHeader />
<NewMarkdown /> <NewMarkdown />
<NewDivider /> <NewDivider />
</Tab> </Tabs.TabPane>
<Tab eventKey={2} title={t('Charts')} className="tab-charts"> <Tabs.TabPane key={2} tab={t('Charts')} className="tab-charts">
<SliceAdder <SliceAdder
height={height + (isSticky ? SUPERSET_HEADER_HEIGHT : 0)} height={height + (isSticky ? SUPERSET_HEADER_HEIGHT : 0)}
/> />
</Tab> </Tabs.TabPane>
</Tabs> </Tabs>
); );
} }

View File

@ -18,10 +18,11 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 shortid from 'shortid';
import { styled, SupersetClient, t } from '@superset-ui/core'; import { styled, SupersetClient, t } from '@superset-ui/core';
import Tabs from 'src/common/components/Tabs';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import CertifiedIconWithTooltip from 'src/components/CertifiedIconWithTooltip'; import CertifiedIconWithTooltip from 'src/components/CertifiedIconWithTooltip';
import DatabaseSelector from 'src/components/DatabaseSelector'; import DatabaseSelector from 'src/components/DatabaseSelector';
@ -596,11 +597,9 @@ class DatasourceEditor extends React.PureComponent {
const { datasource } = this.state; const { datasource } = this.state;
const { spatials, all_cols: allCols } = datasource; const { spatials, all_cols: allCols } = datasource;
return ( return (
<Tab <Tabs.TabPane
title={ tab={<CollectionTabTitle collection={spatials} title={t('Spatial')} />}
<CollectionTabTitle collection={spatials} title={t('Spatial')} /> key={4}
}
eventKey={4}
> >
<CollectionTable <CollectionTable
tableColumns={['name', 'config']} 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> </Alert>
</div> </div>
<Tabs <Tabs
fullWidth={false}
id="table-tabs" id="table-tabs"
data-test="edit-dataset-tabs" data-test="edit-dataset-tabs"
onSelect={this.handleTabSelect} onChange={this.handleTabSelect}
defaultActiveKey={activeTabKey} defaultActiveKey={activeTabKey}
> >
<Tab eventKey={0} title={t('Source')}> <Tabs.TabPane key={0} tab={t('Source')}>
{activeTabKey === 0 && this.renderSourceFieldset()} {this.renderSourceFieldset()}
</Tab> </Tabs.TabPane>
<Tab <Tabs.TabPane
title={ tab={
<CollectionTabTitle <CollectionTabTitle
collection={datasource.metrics} collection={datasource.metrics}
title={t('Metrics')} title={t('Metrics')}
/> />
} }
eventKey={1} key={1}
> >
{activeTabKey === 1 && this.renderMetricCollection()} {this.renderMetricCollection()}
</Tab> </Tabs.TabPane>
<Tab <Tabs.TabPane
title={ tab={
<CollectionTabTitle <CollectionTabTitle
collection={this.state.databaseColumns} collection={this.state.databaseColumns}
title={t('Columns')} title={t('Columns')}
/> />
} }
eventKey={2} key={2}
> >
{activeTabKey === 2 && ( <div>
<div> <ColumnCollectionTable
<ColumnCollectionTable columns={this.state.databaseColumns}
columns={this.state.databaseColumns} onChange={databaseColumns =>
onChange={databaseColumns => this.setColumns({ databaseColumns })
this.setColumns({ databaseColumns }) }
} />
/> <Button
<Button buttonStyle="primary"
buttonStyle="primary" onClick={this.syncMetadata}
onClick={this.syncMetadata} className="sync-from-source"
className="sync-from-source" >
> {t('Sync columns from source')}
{t('Sync columns from source')} </Button>
</Button> {this.state.metadataLoading && <Loading />}
{this.state.metadataLoading && <Loading />} </div>
</div> </Tabs.TabPane>
)} <Tabs.TabPane
</Tab> tab={
<Tab
title={
<CollectionTabTitle <CollectionTabTitle
collection={this.state.calculatedColumns} collection={this.state.calculatedColumns}
title={t('Calculated Columns')} title={t('Calculated Columns')}
/> />
} }
eventKey={3} key={3}
> >
{activeTabKey === 3 && ( <ColumnCollectionTable
<ColumnCollectionTable columns={this.state.calculatedColumns}
columns={this.state.calculatedColumns} onChange={calculatedColumns =>
onChange={calculatedColumns => this.setColumns({ calculatedColumns })
this.setColumns({ calculatedColumns }) }
} editableColumnName
editableColumnName showExpression
showExpression allowAddItem
allowAddItem allowEditDataType
allowEditDataType itemGenerator={() => ({
itemGenerator={() => ({ column_name: '<new column>',
column_name: '<new column>', filterable: true,
filterable: true, groupby: true,
groupby: true, expression: '<enter SQL expression here>',
expression: '<enter SQL expression here>', __expanded: true,
__expanded: true, })}
})} />
/> </Tabs.TabPane>
)} <Tabs.TabPane key={4} tab={t('Settings')}>
</Tab> <div>
<Tab eventKey={4} title={t('Settings')}> <Col md={6}>
{activeTabKey === 4 && ( <FormContainer>{this.renderSettingsFieldset()}</FormContainer>
<div> </Col>
<Col md={6}> <Col md={6}>
<FormContainer>{this.renderSettingsFieldset()}</FormContainer> <FormContainer>{this.renderAdvancedFieldset()}</FormContainer>
</Col> </Col>
<Col md={6}> </div>
<FormContainer>{this.renderAdvancedFieldset()}</FormContainer> </Tabs.TabPane>
</Col>
</div>
)}
</Tab>
</Tabs> </Tabs>
</DatasourceContainer> </DatasourceContainer>
); );

View File

@ -18,10 +18,10 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Tab, Tabs } from 'react-bootstrap';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
import Tabs from 'src/common/components/Tabs';
import columnType from '../propTypes/columnType'; import columnType from '../propTypes/columnType';
import adhocMetricType from '../propTypes/adhocMetricType'; import adhocMetricType from '../propTypes/adhocMetricType';
import AdhocFilter, { EXPRESSION_TYPES } from '../AdhocFilter'; import AdhocFilter, { EXPRESSION_TYPES } from '../AdhocFilter';
@ -46,7 +46,7 @@ const propTypes = {
}; };
const startingWidth = 300; const startingWidth = 300;
const startingHeight = 190; const startingHeight = 240;
export default class AdhocFilterEditPopover extends React.Component { export default class AdhocFilterEditPopover extends React.Component {
constructor(props) { constructor(props) {
@ -144,10 +144,10 @@ export default class AdhocFilterEditPopover extends React.Component {
data-test="adhoc-filter-edit-tabs" data-test="adhoc-filter-edit-tabs"
style={{ height: this.state.height, width: this.state.width }} style={{ height: this.state.height, width: this.state.width }}
> >
<Tab <Tabs.TabPane
className="adhoc-filter-edit-tab" className="adhoc-filter-edit-tab"
eventKey={EXPRESSION_TYPES.SIMPLE} key={EXPRESSION_TYPES.SIMPLE}
title="Simple" tab="Simple"
> >
<AdhocFilterEditPopoverSimpleTabContent <AdhocFilterEditPopoverSimpleTabContent
adhocFilter={this.state.adhocFilter} adhocFilter={this.state.adhocFilter}
@ -157,11 +157,11 @@ export default class AdhocFilterEditPopover extends React.Component {
onHeightChange={this.adjustHeight} onHeightChange={this.adjustHeight}
partitionColumn={partitionColumn} partitionColumn={partitionColumn}
/> />
</Tab> </Tabs.TabPane>
<Tab <Tabs.TabPane
className="adhoc-filter-edit-tab" className="adhoc-filter-edit-tab"
eventKey={EXPRESSION_TYPES.SQL} key={EXPRESSION_TYPES.SQL}
title="Custom SQL" tab="Custom SQL"
> >
{!this.props.datasource || {!this.props.datasource ||
this.props.datasource.type !== 'druid' ? ( 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 Custom SQL Filters are not available on druid datasources
</div> </div>
)} )}
</Tab> </Tabs.TabPane>
</Tabs> </Tabs>
<div> <div>
<Button buttonSize="small" onClick={this.props.onClose} cta> <Button buttonSize="small" onClick={this.props.onClose} cta>

View File

@ -18,7 +18,8 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; 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 Button from 'src/components/Button';
import Select from 'src/components/Select'; import Select from 'src/components/Select';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
@ -50,7 +51,7 @@ const defaultProps = {
}; };
const startingWidth = 300; const startingWidth = 300;
const startingHeight = 180; const startingHeight = 240;
export default class AdhocMetricEditPopover extends React.Component { export default class AdhocMetricEditPopover extends React.Component {
constructor(props) { constructor(props) {
@ -222,13 +223,12 @@ export default class AdhocMetricEditPopover extends React.Component {
defaultActiveKey={adhocMetric.expressionType} defaultActiveKey={adhocMetric.expressionType}
className="adhoc-metric-edit-tabs" className="adhoc-metric-edit-tabs"
style={{ height: this.state.height, width: this.state.width }} style={{ height: this.state.height, width: this.state.width }}
onSelect={this.refreshAceEditor} onChange={this.refreshAceEditor}
animation={false}
> >
<Tab <Tabs.TabPane
className="adhoc-metric-edit-tab" className="adhoc-metric-edit-tab"
eventKey={EXPRESSION_TYPES.SIMPLE} key={EXPRESSION_TYPES.SIMPLE}
title="Simple" tab="Simple"
> >
<FormGroup> <FormGroup>
<FormLabel> <FormLabel>
@ -251,11 +251,11 @@ export default class AdhocMetricEditPopover extends React.Component {
autoFocus autoFocus
/> />
</FormGroup> </FormGroup>
</Tab> </Tabs.TabPane>
<Tab <Tabs.TabPane
className="adhoc-metric-edit-tab" className="adhoc-metric-edit-tab"
eventKey={EXPRESSION_TYPES.SQL} key={EXPRESSION_TYPES.SQL}
title="Custom SQL" tab="Custom SQL"
data-test="adhoc-metric-edit-tab#custom" data-test="adhoc-metric-edit-tab#custom"
> >
{this.props.datasourceType !== 'druid' ? ( {this.props.datasourceType !== 'druid' ? (
@ -282,7 +282,7 @@ export default class AdhocMetricEditPopover extends React.Component {
Custom SQL Metrics are not available on druid datasources Custom SQL Metrics are not available on druid datasources
</div> </div>
)} )}
</Tab> </Tabs.TabPane>
</Tabs> </Tabs>
<div> <div>
<Button <Button

View File

@ -21,9 +21,11 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-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 { t, styled } from '@superset-ui/core';
import Tabs from 'src/common/components/Tabs';
import ControlPanelSection from './ControlPanelSection'; import ControlPanelSection from './ControlPanelSection';
import ControlRow from './ControlRow'; import ControlRow from './ControlRow';
import Control from './Control'; import Control from './Control';
@ -44,7 +46,7 @@ const Styles = styled.div`
height: 100%; height: 100%;
max-height: 100%; max-height: 100%;
.remove-alert { .remove-alert {
cursor: 'pointer'; cursor: pointer;
} }
#controlSections { #controlSections {
display: flex; 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 { class ControlPanelsContainer extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -193,6 +204,7 @@ class ControlPanelsContainer extends React.Component {
} }
}); });
const showCustomizeTab = displaySectionsToRender.length > 0;
return ( return (
<Styles> <Styles>
{this.props.alert && ( {this.props.alert && (
@ -208,16 +220,20 @@ class ControlPanelsContainer extends React.Component {
/> />
</Alert> </Alert>
)} )}
<Tabs id="controlSections" data-test="control-tabs"> <ControlPanelsTabs
<Tab eventKey="query" title={t('Data')}> id="controlSections"
data-test="control-tabs"
fullWidth={showCustomizeTab}
>
<Tabs.TabPane key="query" tab={t('Data')}>
{querySectionsToRender.map(this.renderControlPanelSection)} {querySectionsToRender.map(this.renderControlPanelSection)}
</Tab> </Tabs.TabPane>
{displaySectionsToRender.length > 0 && ( {showCustomizeTab && (
<Tab eventKey="display" title={t('Customize')}> <Tabs.TabPane key="display" tab={t('Customize')}>
{displaySectionsToRender.map(this.renderControlPanelSection)} {displaySectionsToRender.map(this.renderControlPanelSection)}
</Tab> </Tabs.TabPane>
)} )}
</Tabs> </ControlPanelsTabs>
</Styles> </Styles>
); );
} }

View File

@ -26,8 +26,6 @@ import {
MenuItem, MenuItem,
OverlayTrigger, OverlayTrigger,
Radio, Radio,
Tab,
Tabs,
Tooltip, Tooltip,
} from 'react-bootstrap'; } from 'react-bootstrap';
import Popover from 'src/common/components/Popover'; import Popover from 'src/common/components/Popover';
@ -37,6 +35,7 @@ import 'react-datetime/css/react-datetime.css';
import moment from 'moment'; import moment from 'moment';
import { t, styled, withTheme } from '@superset-ui/core'; import { t, styled, withTheme } from '@superset-ui/core';
import Tabs from 'src/common/components/Tabs';
import { import {
buildTimeRangeString, buildTimeRangeString,
formatTimeRange, formatTimeRange,
@ -435,15 +434,17 @@ class DateFilterControl extends React.Component {
}} }}
> >
<Tabs <Tabs
defaultActiveKey={this.state.tab === TABS.DEFAULTS ? 1 : 2} defaultActiveKey={this.state.tab === TABS.DEFAULTS ? '1' : '2'}
id="type" id="type"
className="time-filter-tabs" className="time-filter-tabs"
onSelect={this.changeTab} onSelect={this.changeTab}
> >
<Tab eventKey={1} title="Defaults"> <Tabs.TabPane key="1" tab="Defaults" forceRender>
<FormGroup>{timeFrames}</FormGroup> <div style={{ marginLeft: '8px' }}>
</Tab> <FormGroup>{timeFrames}</FormGroup>
<Tab eventKey={2} title="Custom"> </div>
</Tabs.TabPane>
<Tabs.TabPane key="2" tab="Custom">
<FormGroup> <FormGroup>
<PopoverSection <PopoverSection
title="Relative to today" title="Relative to today"
@ -573,7 +574,7 @@ class DateFilterControl extends React.Component {
</div> </div>
</PopoverSection> </PopoverSection>
</FormGroup> </FormGroup>
</Tab> </Tabs.TabPane>
</Tabs> </Tabs>
<div className="clearfix"> <div className="clearfix">
<Button <Button