refactor(explore): Enhance Dataset and Control panel Collapse components (#12218)

This commit is contained in:
Geido
2021-01-26 00:05:19 +01:00
committed by GitHub
parent 0fed1e04ef
commit 1b2611c211
10 changed files with 107 additions and 240 deletions

View File

@@ -1,117 +0,0 @@
/**
* 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.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Panel } from 'react-bootstrap';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import { styled } from '@superset-ui/core';
const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
children: PropTypes.node.isRequired,
startExpanded: PropTypes.bool,
hasErrors: PropTypes.bool,
};
const defaultProps = {
label: null,
description: null,
startExpanded: false,
hasErrors: false,
};
const StyledPanelTitle = styled(Panel.Title)`
& > div {
display: flex;
align-items: center;
justify-content: space-between;
}
`;
export default class ControlPanelSection extends React.Component {
constructor(props) {
super(props);
this.state = { expanded: this.props.startExpanded };
this.toggleExpand = this.toggleExpand.bind(this);
}
toggleExpand() {
this.setState(prevState => ({ expanded: !prevState.expanded }));
}
renderHeader() {
const { label, description, hasErrors } = this.props;
return (
label && (
<div>
<span>
<span
data-test="clickable-control-panel-section-title"
role="button"
tabIndex={0}
onClick={this.toggleExpand}
>
{label}
</span>{' '}
{description && (
<InfoTooltipWithTrigger label={label} tooltip={description} />
)}
{hasErrors && (
<InfoTooltipWithTrigger
label="validation-errors"
bsStyle="danger"
tooltip="This section contains validation errors"
/>
)}
</span>
<i
role="button"
aria-label="Toggle expand"
tabIndex={0}
className={`float-right fa-lg text-primary expander fa fa-angle-${
this.state.expanded ? 'up' : 'down'
}`}
onClick={this.toggleExpand.bind(this)}
/>
</div>
)
);
}
render() {
return (
<Panel
className="control-panel-section"
expanded={this.state.expanded}
onToggle={this.toggleExpand}
>
<Panel.Heading>
<StyledPanelTitle>{this.renderHeader()}</StyledPanelTitle>
</Panel.Heading>
<Panel.Collapse>
<Panel.Body>{this.props.children}</Panel.Body>
</Panel.Collapse>
</Panel>
);
}
}
ControlPanelSection.propTypes = propTypes;
ControlPanelSection.defaultProps = defaultProps;

View File

@@ -25,9 +25,10 @@ import { Alert } from 'react-bootstrap';
import { t, styled, getChartControlPanelRegistry } from '@superset-ui/core';
import Tabs from 'src/common/components/Tabs';
import { Collapse } from 'src/common/components';
import { PluginContext } from 'src/components/DynamicPlugins';
import Loading from 'src/components/Loading';
import ControlPanelSection from './ControlPanelSection';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import ControlRow from './ControlRow';
import Control from './Control';
import { sectionsToRender } from '../controlUtils';
@@ -75,8 +76,10 @@ const ControlPanelsTabs = styled(Tabs)`
.ant-tabs-content-holder {
overflow: visible;
}
.ant-tabs-tabpane {
height: 100%;
}
`;
class ControlPanelsContainer extends React.Component {
// trigger updates to the component when async plugins load
static contextType = PluginContext;
@@ -96,6 +99,13 @@ class ControlPanelsContainer extends React.Component {
);
}
sectionsToExpand(sections) {
return sections.reduce(
(acc, cur) => (cur.expanded ? [...acc, cur.label] : acc),
[],
);
}
removeAlert() {
this.props.actions.removeControlPanelAlert();
}
@@ -137,6 +147,7 @@ class ControlPanelsContainer extends React.Component {
renderControlPanelSection(section) {
const { controls } = this.props;
const { label, description } = section;
const hasErrors = section.controlSetRows.some(rows =>
rows.some(
@@ -146,14 +157,27 @@ class ControlPanelsContainer extends React.Component {
controls[s].validationErrors.length > 0,
),
);
const PanelHeader = () => (
<span>
<span>{label}</span>{' '}
{description && (
<InfoTooltipWithTrigger label={label} tooltip={description} />
)}
{hasErrors && (
<InfoTooltipWithTrigger
label="validation-errors"
bsStyle="danger"
tooltip="This section contains validation errors"
/>
)}
</span>
);
return (
<ControlPanelSection
<Collapse.Panel
className="control-panel-section"
header={PanelHeader()}
key={section.label}
label={section.label}
startExpanded={section.expanded}
hasErrors={hasErrors}
description={section.description}
>
{section.controlSetRows.map((controlSets, i) => {
const renderedControls = controlSets
@@ -188,7 +212,7 @@ class ControlPanelsContainer extends React.Component {
/>
);
})}
</ControlPanelSection>
</Collapse.Panel>
);
}
@@ -223,7 +247,13 @@ class ControlPanelsContainer extends React.Component {
displaySectionsToRender.push(section);
}
});
const showCustomizeTab = displaySectionsToRender.length > 0;
const expandedQuerySections = this.sectionsToExpand(querySectionsToRender);
const expandedCustomSections = this.sectionsToExpand(
displaySectionsToRender,
);
return (
<Styles>
{this.props.alert && (
@@ -245,11 +275,25 @@ class ControlPanelsContainer extends React.Component {
fullWidth={showCustomizeTab}
>
<Tabs.TabPane key="query" tab={t('Data')}>
{querySectionsToRender.map(this.renderControlPanelSection)}
<Collapse
bordered
defaultActiveKey={expandedQuerySections}
expandIconPosition="right"
ghost
>
{querySectionsToRender.map(this.renderControlPanelSection)}
</Collapse>
</Tabs.TabPane>
{showCustomizeTab && (
<Tabs.TabPane key="display" tab={t('Customize')}>
{displaySectionsToRender.map(this.renderControlPanelSection)}
<Collapse
bordered
defaultActiveKey={expandedCustomSections}
expandIconPosition="right"
ghost
>
{displaySectionsToRender.map(this.renderControlPanelSection)}
</Collapse>
</Tabs.TabPane>
)}
</ControlPanelsTabs>

View File

@@ -83,44 +83,9 @@ const DatasourceContainer = styled.div`
max-height: 100%;
.ant-collapse {
height: auto;
border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
padding-bottom: ${({ theme }) => theme.gridUnit * 2}px;
background-color: ${({ theme }) => theme.colors.grayscale.light4};
}
.ant-collapse > .ant-collapse-item > .ant-collapse-header {
padding-left: ${({ theme }) => theme.gridUnit * 2}px;
padding-bottom: 0px;
}
.ant-collapse-item {
background-color: ${({ theme }) => theme.colors.grayscale.light4};
.anticon.anticon-right.ant-collapse-arrow > svg {
transform: rotate(90deg) !important;
margin-right: ${({ theme }) => theme.gridUnit * -2}px;
}
}
.ant-collapse-item.ant-collapse-item-active {
.anticon.anticon-right.ant-collapse-arrow > svg {
transform: rotate(-90deg) !important;
}
.ant-collapse-header {
border: 0;
}
}
.header {
font-size: ${({ theme }) => theme.typography.sizes.l}px;
margin-left: ${({ theme }) => theme.gridUnit * -2}px;
}
.ant-collapse-borderless
> .ant-collapse-item
> .ant-collapse-content
> .ant-collapse-content-box {
padding: 0px;
}
.field-selections {
padding: ${({ theme }) =>
`${2 * theme.gridUnit}px ${2 * theme.gridUnit}px ${
4 * theme.gridUnit
}px`};
padding: ${({ theme }) => `0 0 ${4 * theme.gridUnit}px`};
overflow: auto;
}
.field-length {
@@ -247,9 +212,10 @@ export default function DataSourcePanel({
/>
<div className="field-selections">
<Collapse
bordered={false}
bordered
defaultActiveKey={['metrics', 'column']}
expandIconPosition="right"
ghost
>
<Collapse.Panel
header={<span className="header">{t('Metrics')}</span>}