refactor: Use Antd Dropdown instead of react-bootstrap in DatasourceControl (#11395)

* Create Tooltip component

* Refactor DatasourceControl

* Bug fix

* Lint fix

* E2E test fix

* Move menu item keys to constants

* Remove LESS file

* Test fix

* Test fix

* Lint fix
This commit is contained in:
Kamil Gabryjelski 2020-10-23 18:08:35 +02:00 committed by GitHub
parent 6f69212f28
commit ad88a06d61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 98 deletions

View File

@ -34,13 +34,13 @@ describe('Datasource control', () => {
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@postJson' });
cy.get('#datasource_menu').click(); cy.get('[data-test="datasource-menu-trigger"]').click();
cy.get('script').then(nodes => { cy.get('script').then(nodes => {
numScripts = nodes.length; numScripts = nodes.length;
}); });
cy.get('a').contains('Edit Dataset').click(); cy.get('[data-test="edit-dataset"]').click();
// should load additional scripts for the modal // should load additional scripts for the modal
cy.get('script').then(nodes => { cy.get('script').then(nodes => {
@ -65,8 +65,8 @@ describe('Datasource control', () => {
.focus() .focus()
.type(newMetricName, { force: true }); .type(newMetricName, { force: true });
// delete metric // delete metric
cy.get('#datasource_menu').click(); cy.get('[data-test="datasource-menu-trigger"]').click();
cy.get('a').contains('Edit Dataset').click(); cy.get('[data-test="edit-dataset"]').click();
cy.get('.modal-content').within(() => { cy.get('.modal-content').within(() => {
cy.get('a[role="tab"]').contains('Metrics').click(); cy.get('a[role="tab"]').contains('Metrics').click();
}); });

View File

@ -20,7 +20,7 @@ import React from 'react';
import sinon from 'sinon'; import sinon from 'sinon';
import configureStore from 'redux-mock-store'; import configureStore from 'redux-mock-store';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { MenuItem } from 'react-bootstrap'; import { Menu } from 'src/common/components';
import DatasourceModal from 'src/datasource/DatasourceModal'; import DatasourceModal from 'src/datasource/DatasourceModal';
import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal'; import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal';
import DatasourceControl from 'src/explore/components/controls/DatasourceControl'; import DatasourceControl from 'src/explore/components/controls/DatasourceControl';
@ -72,17 +72,23 @@ describe('DatasourceControl', () => {
it('show or hide Edit Datasource option', () => { it('show or hide Edit Datasource option', () => {
let wrapper = setup(); let wrapper = setup();
expect(wrapper.find('#datasource_menu')).toExist(); expect(wrapper.find('[data-test="datasource-menu"]')).toExist();
expect(wrapper.find('#datasource_menu').dive().find(MenuItem)).toHaveLength( let menuWrapper = shallow(
3, <div>
{wrapper.find('[data-test="datasource-menu"]').prop('overlay')}
</div>,
); );
expect(menuWrapper.find(Menu.Item)).toHaveLength(3);
wrapper = setup({ wrapper = setup({
isEditable: false, isEditable: false,
}); });
expect(wrapper.find('#datasource_menu')).toExist(); expect(wrapper.find('[data-test="datasource-menu"]')).toExist();
expect(wrapper.find('#datasource_menu').dive().find(MenuItem)).toHaveLength( menuWrapper = shallow(
2, <div>
{wrapper.find('[data-test="datasource-menu"]').prop('overlay')}
</div>,
); );
expect(menuWrapper.find(Menu.Item)).toHaveLength(2);
}); });
}); });

View File

@ -16,33 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
@import '../../../../stylesheets/less/variables.less'; import React from 'react';
import { Tooltip as BaseTooltip } from 'src/common/components';
import { TooltipProps } from 'antd/lib/tooltip';
#datasource_menu { const Tooltip = (props: TooltipProps) => (
border-radius: @border-radius-normal; <BaseTooltip overlayStyle={{ fontSize: '12px' }} {...props} />
padding: 0px; );
border: none;
}
#datasource_menu .caret { export default Tooltip;
position: relative;
padding-right: 8px;
margin-left: 4px;
color: @lightest;
top: -8px;
}
#datasource_menu .caret {
display: none;
}
#datasource_menu:hover {
background-color: transparent;
}
.DatasourceControl svg {
vertical-align: middle;
color: @brand-primary;
cursor: pointer;
}
.DatasourceControl .angle {
color: @brand-primary;
}

View File

@ -19,12 +19,13 @@
import React from 'react'; import React from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { withKnobs, boolean, select } from '@storybook/addon-knobs'; import { withKnobs, boolean, select } from '@storybook/addon-knobs';
import Button from 'src/components/Button';
import Modal from './Modal'; import Modal from './Modal';
import Tabs, { EditableTabs } from './Tabs'; import Tabs, { EditableTabs } from './Tabs';
import AntdPopover from './Popover'; import AntdPopover from './Popover';
import AntdTooltip from './Tooltip';
import { Menu } from '.'; import { Menu } from '.';
import { Dropdown } from './Dropdown'; import { Dropdown } from './Dropdown';
import Button from '../../components/Button';
export default { export default {
title: 'Common Components', title: 'Common Components',
@ -146,3 +147,31 @@ export const Popover = () => (
<Button>TRIGGER</Button> <Button>TRIGGER</Button>
</AntdPopover> </AntdPopover>
); );
export const Tooltip = () => (
<AntdTooltip
title="This is a Tooltip"
trigger={select('Trigger', ['click', 'hover', 'focus'], 'click')}
placement={select(
'Placement',
[
'topLeft',
'top',
'topRight',
'leftTop',
'left',
'leftBottom',
'rightTop',
'right',
'rightBottom',
'bottomLeft',
'bottom',
'bottomRight',
],
'topLeft',
)}
arrowPointAtCenter={boolean('Arrow point at center', false)}
>
<Button>A button with tooltip</Button>
</AntdTooltip>
);

View File

@ -18,28 +18,18 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { Col, Collapse, Row, Well } from 'react-bootstrap';
Col,
Collapse,
DropdownButton,
MenuItem,
OverlayTrigger,
Row,
Tooltip,
Well,
} from 'react-bootstrap';
import { t, styled } from '@superset-ui/core'; import { t, styled } from '@superset-ui/core';
import { ColumnOption, MetricOption } from '@superset-ui/chart-controls'; import { ColumnOption, MetricOption } from '@superset-ui/chart-controls';
import TooltipWrapper from 'src/components/TooltipWrapper'; import { Dropdown, Menu } from 'src/common/components';
import Tooltip from 'src/common/components/Tooltip';
import Icon from 'src/components/Icon'; import Icon from 'src/components/Icon';
import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal'; import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal';
import DatasourceModal from 'src/datasource/DatasourceModal'; import DatasourceModal from 'src/datasource/DatasourceModal';
import Label from 'src/components/Label'; import Label from 'src/components/Label';
import ControlHeader from '../ControlHeader'; import ControlHeader from '../ControlHeader';
import './DatasourceControl.less';
const propTypes = { const propTypes = {
actions: PropTypes.object.isRequired, actions: PropTypes.object.isRequired,
@ -58,19 +48,30 @@ const defaultProps = {
}; };
const Styles = styled.div` const Styles = styled.div`
#datasource_menu { .ant-dropdown-trigger {
margin-left: ${({ theme }) => theme.gridUnit}px; margin-left: ${({ theme }) => theme.gridUnit}px;
box-shadow: none; box-shadow: none;
&:active { &:active {
box-shadow: none; box-shadow: none;
} }
} }
.btn-group .open .dropdown-toggle { .btn-group .open .dropdown-toggle {
box-shadow: none; box-shadow: none;
&.button-default { &.button-default {
background: none; background: none;
} }
} }
i.angle {
color: ${({ theme }) => theme.colors.primary.base};
}
svg.datasource-modal-trigger {
color: ${({ theme }) => theme.colors.primary.base};
vertical-align: middle;
cursor: pointer;
}
`; `;
/** /**
@ -84,6 +85,10 @@ const ColumnsCol = styled(Col)`
} }
`; `;
const CHANGE_DATASET = 'change_dataset';
const EXPLORE_IN_SQL_LAB = 'explore_in_sql_lab';
const EDIT_DATASET = 'edit_dataset';
class DatasourceControl extends React.PureComponent { class DatasourceControl extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
@ -98,6 +103,7 @@ class DatasourceControl extends React.PureComponent {
this.toggleEditDatasourceModal = this.toggleEditDatasourceModal.bind(this); this.toggleEditDatasourceModal = this.toggleEditDatasourceModal.bind(this);
this.toggleShowDatasource = this.toggleShowDatasource.bind(this); this.toggleShowDatasource = this.toggleShowDatasource.bind(this);
this.renderDatasource = this.renderDatasource.bind(this); this.renderDatasource = this.renderDatasource.bind(this);
this.handleMenuItemClick = this.handleMenuItemClick.bind(this);
} }
onDatasourceSave(datasource) { onDatasourceSave(datasource) {
@ -125,6 +131,15 @@ class DatasourceControl extends React.PureComponent {
})); }));
} }
handleMenuItemClick({ key }) {
if (key === CHANGE_DATASET) {
this.toggleChangeDatasourceModal();
}
if (key === EDIT_DATASET) {
this.toggleEditDatasourceModal();
}
}
renderDatasource() { renderDatasource() {
const { datasource } = this.props; const { datasource } = this.props;
const { showDatasource } = this.state; const { showDatasource } = this.state;
@ -176,18 +191,32 @@ class DatasourceControl extends React.PureComponent {
showDatasource, showDatasource,
} = this.state; } = this.state;
const { datasource, onChange, value } = this.props; const { datasource, onChange, value } = this.props;
const datasourceMenu = (
<Menu onClick={this.handleMenuItemClick}>
<Menu.Item key={CHANGE_DATASET}>{t('Change Dataset')}</Menu.Item>
<Menu.Item key={EXPLORE_IN_SQL_LAB}>
<a
href={`/superset/sqllab?datasourceKey=${value}`}
target="_blank"
rel="noopener noreferrer"
>
{t('Explore in SQL Lab')}
</a>
</Menu.Item>
{this.props.isEditable && (
<Menu.Item key={EDIT_DATASET} data-test="edit-dataset">
{t('Edit Dataset')}
</Menu.Item>
)}
</Menu>
);
return ( return (
<Styles className="DatasourceControl"> <Styles className="DatasourceControl">
<ControlHeader {...this.props} /> <ControlHeader {...this.props} />
<div> <div>
<OverlayTrigger <Tooltip title={t('Expand/collapse dataset configuration')}>
placement="top"
overlay={
<Tooltip id="toggle-dataset-tooltip">
{t('Expand/collapse dataset configuration')}
</Tooltip>
}
>
<Label <Label
style={{ textTransform: 'none' }} style={{ textTransform: 'none' }}
onClick={this.toggleShowDatasource} onClick={this.toggleShowDatasource}
@ -199,43 +228,20 @@ class DatasourceControl extends React.PureComponent {
}`} }`}
/> />
</Label> </Label>
</OverlayTrigger> </Tooltip>
<TooltipWrapper <Dropdown
label="change-datasource" overlay={datasourceMenu}
tooltip={t('More dataset related options')} trigger={['click']}
trigger={['hover']} data-test="datasource-menu"
> >
<DropdownButton <Tooltip title={t('More dataset related options')}>
title={<Icon name="more-horiz" />} <Icon
className="" className="datasource-modal-trigger"
bsSize="sm" data-test="datasource-menu-trigger"
id="datasource_menu" name="more-horiz"
data-test="datasource-menu" />
> </Tooltip>
<MenuItem eventKey="3" onClick={this.toggleChangeDatasourceModal}> </Dropdown>
{t('Change Dataset')}
</MenuItem>
{datasource.type === 'table' && (
<MenuItem
eventKey="3"
href={`/superset/sqllab?datasourceKey=${value}`}
target="_blank"
rel="noopener noreferrer"
>
{t('Explore in SQL Lab')}
</MenuItem>
)}
{this.props.isEditable && (
<MenuItem
data-test="edit-dataset"
eventKey="3"
onClick={this.toggleEditDatasourceModal}
>
{t('Edit Dataset')}
</MenuItem>
)}
</DropdownButton>
</TooltipWrapper>
</div> </div>
<Collapse in={this.state.showDatasource}> <Collapse in={this.state.showDatasource}>
{this.renderDatasource()} {this.renderDatasource()}