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
cy.get('script[src*="mathjs"]').should('have.length', 1);
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();

View File

@ -21,6 +21,7 @@ import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {
ensureIsArray,
t,
styled,
getChartControlPanelRegistry,
@ -31,8 +32,10 @@ import {
ControlPanelSectionConfig,
ControlState,
CustomControlItem,
DatasourceMeta,
ExpandedControlItem,
InfoTooltipWithTrigger,
sections,
} from '@superset-ui/chart-controls';
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
static contextType = PluginContext;
constructor(props: ControlPanelsContainerProps) {
super(props);
this.state = {
expandedQuerySections: [],
expandedCustomizeSections: [],
querySections: [],
customizeSections: [],
};
this.renderControl = this.renderControl.bind(this);
this.renderControlPanelSection = this.renderControlPanelSection.bind(this);
}
sectionsToRender(): ExpandedControlPanelSectionConfig[] {
return getSectionsToRender(
this.props.form_data.viz_type,
this.props.datasource_type,
);
static getDerivedStateFromProps(
props: ControlPanelsContainerProps,
state: ControlPanelsContainerState,
): 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[]) {
return sections.reduce(
(acc, section) =>
section.expanded ? [...acc, String(section.label)] : acc,
[] as string[],
);
componentDidUpdate(prevProps: ControlPanelsContainerProps) {
if (
this.props.form_data.datasource !== prevProps.form_data.datasource ||
this.props.form_data.viz_type !== prevProps.form_data.viz_type
) {
// 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) {
@ -260,36 +363,7 @@ export class ControlPanelsContainer extends React.Component<ControlPanelsContain
return <Loading />;
}
const querySectionsToRender: ExpandedControlPanelSectionConfig[] = [];
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,
);
const showCustomizeTab = this.state.customizeSections.length > 0;
return (
<Styles>
<ControlPanelsTabs
@ -300,22 +374,34 @@ export class ControlPanelsContainer extends React.Component<ControlPanelsContain
<Tabs.TabPane key="query" tab={t('Data')}>
<Collapse
bordered
defaultActiveKey={expandedQuerySections}
activeKey={this.state.expandedQuerySections}
expandIconPosition="right"
onChange={selection => {
this.setState({
expandedQuerySections: ensureIsArray(selection),
});
}}
ghost
>
{querySectionsToRender.map(this.renderControlPanelSection)}
{this.state.querySections.map(this.renderControlPanelSection)}
</Collapse>
</Tabs.TabPane>
{showCustomizeTab && (
<Tabs.TabPane key="display" tab={t('Customize')}>
<Collapse
bordered
defaultActiveKey={expandedCustomSections}
activeKey={this.state.expandedCustomizeSections}
expandIconPosition="right"
onChange={selection => {
this.setState({
expandedCustomizeSections: ensureIsArray(selection),
});
}}
ghost
>
{displaySectionsToRender.map(this.renderControlPanelSection)}
{this.state.customizeSections.map(
this.renderControlPanelSection,
)}
</Collapse>
</Tabs.TabPane>
)}