feat(explore): collapse time section if no ts columns (#14493)

* feat(explore): collapse time section if no ts columns

* fix viz change bug

* fix test
This commit is contained in:
Ville Brofeldt 2021-05-07 11:55:54 +03:00 committed by GitHub
parent 05c24056b5
commit 680c96ec54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 133 additions and 47 deletions

View File

@ -111,7 +111,7 @@ describe('VizType control', () => {
// should load mathjs for line chart // should load mathjs for line chart
cy.get('script[src*="mathjs"]').should('have.length', 1); cy.get('script[src*="mathjs"]').should('have.length', 1);
cy.get('script').then(nodes => { cy.get('script').then(nodes => {
expect(nodes.length).to.eq(numScripts); expect(nodes.length).to.greaterThan(numScripts);
}); });
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();

View File

@ -21,6 +21,7 @@ import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
ensureIsArray,
t, t,
styled, styled,
getChartControlPanelRegistry, getChartControlPanelRegistry,
@ -31,8 +32,10 @@ import {
ControlPanelSectionConfig, ControlPanelSectionConfig,
ControlState, ControlState,
CustomControlItem, CustomControlItem,
DatasourceMeta,
ExpandedControlItem, ExpandedControlItem,
InfoTooltipWithTrigger, InfoTooltipWithTrigger,
sections,
} from '@superset-ui/chart-controls'; } from '@superset-ui/chart-controls';
import Collapse from 'src/components/Collapse'; import Collapse from 'src/components/Collapse';
@ -102,29 +105,129 @@ const ControlPanelsTabs = styled(Tabs)`
} }
`; `;
export class ControlPanelsContainer extends React.Component<ControlPanelsContainerProps> { type ControlPanelsContainerState = {
expandedQuerySections: string[];
expandedCustomizeSections: string[];
querySections: ControlPanelSectionConfig[];
customizeSections: ControlPanelSectionConfig[];
};
const isTimeSection = (section: ControlPanelSectionConfig): boolean =>
!!section.label &&
(sections.legacyRegularTime.label === section.label ||
sections.legacyTimeseriesTime.label === section.label);
const hasTimeColumn = (datasource: DatasourceMeta): boolean =>
datasource?.columns?.some(c => c.is_dttm) ||
datasource.type === DatasourceType.Druid;
const sectionsToExpand = (
sections: ControlPanelSectionConfig[],
datasource: DatasourceMeta,
): string[] =>
// avoid expanding time section if datasource doesn't include time column
sections.reduce(
(acc, section) =>
section.expanded && (!isTimeSection(section) || hasTimeColumn(datasource))
? [...acc, String(section.label)]
: acc,
[] as string[],
);
function getState(
props: ControlPanelsContainerProps,
): ControlPanelsContainerState {
const {
exploreState: { datasource },
} = props;
const querySections: ControlPanelSectionConfig[] = [];
const customizeSections: ControlPanelSectionConfig[] = [];
getSectionsToRender(props.form_data.viz_type, props.datasource_type).forEach(
section => {
// if at least one control in the section is not `renderTrigger`
// or asks to be displayed at the Data tab
if (
section.tabOverride === 'data' ||
section.controlSetRows.some(rows =>
rows.some(
control =>
control &&
typeof control === 'object' &&
'config' in control &&
control.config &&
(!control.config.renderTrigger ||
control.config.tabOverride === 'data'),
),
)
) {
querySections.push(section);
} else {
customizeSections.push(section);
}
},
);
const expandedQuerySections: string[] = sectionsToExpand(
querySections,
datasource,
);
const expandedCustomizeSections: string[] = sectionsToExpand(
customizeSections,
datasource,
);
return {
expandedQuerySections,
expandedCustomizeSections,
querySections,
customizeSections,
};
}
export class ControlPanelsContainer extends React.Component<
ControlPanelsContainerProps,
ControlPanelsContainerState
> {
// trigger updates to the component when async plugins load // trigger updates to the component when async plugins load
static contextType = PluginContext; static contextType = PluginContext;
constructor(props: ControlPanelsContainerProps) { constructor(props: ControlPanelsContainerProps) {
super(props); super(props);
this.state = {
expandedQuerySections: [],
expandedCustomizeSections: [],
querySections: [],
customizeSections: [],
};
this.renderControl = this.renderControl.bind(this); this.renderControl = this.renderControl.bind(this);
this.renderControlPanelSection = this.renderControlPanelSection.bind(this); this.renderControlPanelSection = this.renderControlPanelSection.bind(this);
} }
sectionsToRender(): ExpandedControlPanelSectionConfig[] { static getDerivedStateFromProps(
return getSectionsToRender( props: ControlPanelsContainerProps,
this.props.form_data.viz_type, state: ControlPanelsContainerState,
this.props.datasource_type, ): ControlPanelsContainerState {
); // only update the sections, not the expanded/collapsed state
const newState = getState(props);
return {
...state,
customizeSections: newState.customizeSections,
querySections: newState.querySections,
};
} }
sectionsToExpand(sections: ControlPanelSectionConfig[]) { componentDidUpdate(prevProps: ControlPanelsContainerProps) {
return sections.reduce( if (
(acc, section) => this.props.form_data.datasource !== prevProps.form_data.datasource ||
section.expanded ? [...acc, String(section.label)] : acc, this.props.form_data.viz_type !== prevProps.form_data.viz_type
[] as string[], ) {
); // eslint-disable-next-line react/no-did-update-set-state
this.setState(getState(this.props));
}
}
componentDidMount() {
this.setState(getState(this.props));
} }
renderControl({ name, config }: CustomControlItem) { renderControl({ name, config }: CustomControlItem) {
@ -260,36 +363,7 @@ export class ControlPanelsContainer extends React.Component<ControlPanelsContain
return <Loading />; return <Loading />;
} }
const querySectionsToRender: ExpandedControlPanelSectionConfig[] = []; const showCustomizeTab = this.state.customizeSections.length > 0;
const displaySectionsToRender: ExpandedControlPanelSectionConfig[] = [];
this.sectionsToRender().forEach(section => {
// if at least one control in the section is not `renderTrigger`
// or asks to be displayed at the Data tab
if (
section.tabOverride === 'data' ||
section.controlSetRows.some(rows =>
rows.some(
control =>
control &&
typeof control === 'object' &&
'config' in control &&
control.config &&
(!control.config.renderTrigger ||
control.config.tabOverride === 'data'),
),
)
) {
querySectionsToRender.push(section);
} else {
displaySectionsToRender.push(section);
}
});
const showCustomizeTab = displaySectionsToRender.length > 0;
const expandedQuerySections = this.sectionsToExpand(querySectionsToRender);
const expandedCustomSections = this.sectionsToExpand(
displaySectionsToRender,
);
return ( return (
<Styles> <Styles>
<ControlPanelsTabs <ControlPanelsTabs
@ -300,22 +374,34 @@ export class ControlPanelsContainer extends React.Component<ControlPanelsContain
<Tabs.TabPane key="query" tab={t('Data')}> <Tabs.TabPane key="query" tab={t('Data')}>
<Collapse <Collapse
bordered bordered
defaultActiveKey={expandedQuerySections} activeKey={this.state.expandedQuerySections}
expandIconPosition="right" expandIconPosition="right"
onChange={selection => {
this.setState({
expandedQuerySections: ensureIsArray(selection),
});
}}
ghost ghost
> >
{querySectionsToRender.map(this.renderControlPanelSection)} {this.state.querySections.map(this.renderControlPanelSection)}
</Collapse> </Collapse>
</Tabs.TabPane> </Tabs.TabPane>
{showCustomizeTab && ( {showCustomizeTab && (
<Tabs.TabPane key="display" tab={t('Customize')}> <Tabs.TabPane key="display" tab={t('Customize')}>
<Collapse <Collapse
bordered bordered
defaultActiveKey={expandedCustomSections} activeKey={this.state.expandedCustomizeSections}
expandIconPosition="right" expandIconPosition="right"
onChange={selection => {
this.setState({
expandedCustomizeSections: ensureIsArray(selection),
});
}}
ghost ghost
> >
{displaySectionsToRender.map(this.renderControlPanelSection)} {this.state.customizeSections.map(
this.renderControlPanelSection,
)}
</Collapse> </Collapse>
</Tabs.TabPane> </Tabs.TabPane>
)} )}