refactor: Replace react-bootstrap tabs with Antd tabs (#11090)

* Replace tabs in profile

* Replace tabs in SouthPane

* Replace tabs in TabbedSqlEditors

* Add typing for dropdown

* Add license

* Remove isSelected

* Fixes

* Add data-test

* Fix test

* Remove unnecessary style

* Remove unnecessary style

* Tests fix

* Tests fix

* Update superset-frontend/src/common/components/Dropdown.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/common/components/Dropdown.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/common/components/Dropdown.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/common/components/Dropdown.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/common/components/Tabs.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/common/components/Dropdown.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/common/components/Dropdown.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Remove inModal prop

* Remove inModal from storybook

* Move inline style to styled component

Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
Kamil Gabryjelski
2020-10-02 22:07:52 +02:00
committed by GitHub
parent 53cd05d74a
commit 4fd993c4e0
10 changed files with 369 additions and 179 deletions

View File

@@ -19,7 +19,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import shortid from 'shortid';
import { Alert, Tab, Tabs } from 'react-bootstrap';
import { Alert } from 'react-bootstrap';
import Tabs from 'src/common/components/Tabs';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { t } from '@superset-ui/core';
@@ -140,9 +141,8 @@ export class SouthPane extends React.PureComponent {
);
}
const dataPreviewTabs = props.dataPreviewQueries.map(query => (
<Tab
title={t('Preview: `%s`', decodeURIComponent(query.tableName))}
eventKey={query.id}
<Tabs.TabPane
tab={t('Preview: `%s`', decodeURIComponent(query.tableName))}
key={query.id}
>
<ResultSet
@@ -154,29 +154,27 @@ export class SouthPane extends React.PureComponent {
height={innerTabContentHeight}
displayLimit={this.props.displayLimit}
/>
</Tab>
</Tabs.TabPane>
));
return (
<div className="SouthPane" ref={this.southPaneRef}>
<Tabs
bsStyle="tabs"
animation={false}
defaultActiveKey={this.props.activeSouthPaneTab}
className="SouthPaneTabs"
id={shortid.generate()}
activeKey={this.props.activeSouthPaneTab}
onSelect={this.switchTab}
fullWidth={false}
>
<Tab title={t('Results')} eventKey="Results">
<Tabs.TabPane tab={t('Results')} key="Results">
{results}
</Tab>
<Tab title={t('Query History')} eventKey="History">
</Tabs.TabPane>
<Tabs.TabPane tab={t('Query History')} key="History">
<QueryHistory
queries={props.editorQueries}
actions={props.actions}
displayLimit={props.displayLimit}
/>
</Tab>
</Tabs.TabPane>
{dataPreviewTabs}
</Tabs>
</div>

View File

@@ -18,18 +18,19 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import { MenuItem, DropdownButton, Tab, Tabs } from 'react-bootstrap';
import { EditableTabs } from 'src/common/components/Tabs';
import { Dropdown } from 'src/common/components/Dropdown';
import { Menu } from 'src/common/components';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import URI from 'urijs';
import { t } from '@superset-ui/core';
import { styled, t } from '@superset-ui/core';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import { areArraysShallowEqual } from 'src/reduxUtils';
import * as Actions from '../actions/sqlLab';
import SqlEditor from './SqlEditor';
import { areArraysShallowEqual } from '../../reduxUtils';
import TabStatusIcon from './TabStatusIcon';
import Icon from '../../components/Icon';
const propTypes = {
actions: PropTypes.object.isRequired,
@@ -57,6 +58,10 @@ const defaultProps = {
let queryCount = 1;
const TabTitle = styled.span`
margin-right: ${({ theme }) => theme.gridUnit * 2}px;
`;
class TabbedSqlEditors extends React.PureComponent {
constructor(props) {
super(props);
@@ -74,6 +79,8 @@ class TabbedSqlEditors extends React.PureComponent {
this,
);
this.duplicateQueryEditor = this.duplicateQueryEditor.bind(this);
this.handleSelect = this.handleSelect.bind(this);
this.handleEdit = this.handleEdit.bind(this);
}
componentDidMount() {
@@ -253,17 +260,23 @@ class TabbedSqlEditors extends React.PureComponent {
}
handleSelect(key) {
if (key === 'add_tab') {
const qeid = this.props.tabHistory[this.props.tabHistory.length - 1];
if (key !== qeid) {
const queryEditor = this.props.queryEditors.find(qe => qe.id === key);
this.props.actions.switchQueryEditor(
queryEditor,
this.props.displayLimit,
);
}
}
handleEdit(key, action) {
if (action === 'remove') {
const qe = this.props.queryEditors.find(qe => qe.id === key);
this.removeQueryEditor(qe);
}
if (action === 'add') {
this.newQueryEditor();
} else {
const qeid = this.props.tabHistory[this.props.tabHistory.length - 1];
if (key !== qeid) {
const queryEditor = this.props.queryEditors.find(qe => qe.id === key);
this.props.actions.switchQueryEditor(
queryEditor,
this.props.displayLimit,
);
}
}
}
@@ -286,10 +299,7 @@ class TabbedSqlEditors extends React.PureComponent {
}
render() {
const editors = this.props.queryEditors.map((qe, i) => {
const isSelected =
this.activeQueryEditor() && this.activeQueryEditor().id === qe.id;
const editors = this.props.queryEditors.map(qe => {
let latestQuery;
if (qe.latestQueryId) {
latestQuery = this.props.queries[qe.latestQueryId];
@@ -300,123 +310,97 @@ class TabbedSqlEditors extends React.PureComponent {
}
const state = latestQuery ? latestQuery.state : '';
const title = (
<>
{qe.title} <TabStatusIcon tabState={state} />{' '}
<Icon
role="button"
tabIndex={0}
cursor="pointer"
name="cancel-x"
const menu = (
<Menu>
<Menu.Item
className="close-btn"
key="1"
onClick={() => this.removeQueryEditor(qe)}
/>
</>
data-test="close-tab-menu-option"
>
<div className="icon-container">
<i className="fa fa-close" />
</div>
{t('Close tab')}
</Menu.Item>
<Menu.Item key="2" onClick={() => this.renameTab(qe)}>
<div className="icon-container">
<i className="fa fa-i-cursor" />
</div>
{t('Rename tab')}
</Menu.Item>
<Menu.Item key="3" onClick={this.toggleLeftBar}>
<div className="icon-container">
<i className="fa fa-cogs" />
</div>
{this.state.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')}
</Menu.Item>
<Menu.Item
key="4"
onClick={() => this.removeAllOtherQueryEditors(qe)}
>
<div className="icon-container">
<i className="fa fa-times-circle-o" />
</div>
{t('Close all other tabs')}
</Menu.Item>
<Menu.Item key="5" onClick={() => this.duplicateQueryEditor(qe)}>
<div className="icon-container">
<i className="fa fa-files-o" />
</div>
{t('Duplicate tab')}
</Menu.Item>
</Menu>
);
const tabTitle = (
const tabHeader = (
<>
{isSelected && (
<DropdownButton
data-test="dropdown-toggle-button"
bsSize="small"
id={`ddbtn-tab-${i}`}
title={' '}
noCaret
>
<MenuItem
className="close-btn"
eventKey="1"
onClick={() => this.removeQueryEditor(qe)}
data-test="close-tab-menu-option"
>
<div className="icon-container">
<i className="fa fa-close" />
</div>
{t('Close tab')}
</MenuItem>
<MenuItem eventKey="2" onClick={() => this.renameTab(qe)}>
<div className="icon-container">
<i className="fa fa-i-cursor" />
</div>
{t('Rename tab')}
</MenuItem>
<MenuItem eventKey="3" onClick={this.toggleLeftBar}>
<div className="icon-container">
<i className="fa fa-cogs" />
</div>
{this.state.hideLeftBar
? t('Expand tool bar')
: t('Hide tool bar')}
</MenuItem>
<MenuItem
eventKey="4"
onClick={() => this.removeAllOtherQueryEditors(qe)}
>
<div className="icon-container">
<i className="fa fa-times-circle-o" />
</div>
{t('Close all other tabs')}
</MenuItem>
<MenuItem
eventKey="5"
onClick={() => this.duplicateQueryEditor(qe)}
>
<div className="icon-container">
<i className="fa fa-files-o" />
</div>
{t('Duplicate tab')}
</MenuItem>
</DropdownButton>
)}
<span className="ddbtn-tab">{title}</span>
<div data-test="dropdown-toggle-button">
<Dropdown overlay={menu} trigger={['click']} />
</div>
<TabTitle>{qe.title}</TabTitle> <TabStatusIcon tabState={state} />{' '}
</>
);
return (
<Tab key={qe.id} title={tabTitle} eventKey={qe.id}>
{isSelected && (
<SqlEditor
tables={this.props.tables.filter(
xt => xt.queryEditorId === qe.id,
)}
queryEditor={qe}
editorQueries={this.state.queriesArray}
dataPreviewQueries={this.state.dataPreviewQueries}
latestQuery={latestQuery}
database={database}
actions={this.props.actions}
hideLeftBar={this.state.hideLeftBar}
defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow}
displayLimit={this.props.displayLimit}
saveQueryWarning={this.props.saveQueryWarning}
scheduleQueryWarning={this.props.scheduleQueryWarning}
/>
)}
</Tab>
<EditableTabs.TabPane
key={qe.id}
tab={tabHeader}
// for tests - key prop isn't handled by enzyme well bcs it's a react keyword
data-key={qe.id}
>
<SqlEditor
tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)}
queryEditor={qe}
editorQueries={this.state.queriesArray}
dataPreviewQueries={this.state.dataPreviewQueries}
latestQuery={latestQuery}
database={database}
actions={this.props.actions}
hideLeftBar={this.state.hideLeftBar}
defaultQueryLimit={this.props.defaultQueryLimit}
maxRow={this.props.maxRow}
displayLimit={this.props.displayLimit}
saveQueryWarning={this.props.saveQueryWarning}
scheduleQueryWarning={this.props.scheduleQueryWarning}
/>
</EditableTabs.TabPane>
);
});
return (
<Tabs
bsStyle="tabs"
animation={false}
<EditableTabs
activeKey={this.props.tabHistory[this.props.tabHistory.length - 1]}
onSelect={this.handleSelect.bind(this)}
id="a11y-query-editor-tabs"
className="SqlEditorTabs"
data-test="sql-editor-tabs"
onChange={this.handleSelect}
fullWidth={false}
hideAdd={this.props.offline}
onEdit={this.handleEdit}
addIcon={<i data-test="add-tab-icon" className="fa fa-plus-circle" />}
>
{editors}
<Tab
title={
<div>
<i data-test="add-tab-icon" className="fa fa-plus-circle" />
&nbsp;
</div>
}
className="addEditorTab"
eventKey="add_tab"
disabled={this.props.offline}
/>
</Tabs>
</EditableTabs>
);
}
}