feat(explore): Implement chart empty states (#18678)

* feat(explore): Implement chart empty states

* Fix test

* Remove unused import

* Fix tests
This commit is contained in:
Kamil Gabryjelski 2022-02-14 16:14:20 +01:00 committed by GitHub
parent c1205b5279
commit 167e18e806
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 57 additions and 20 deletions

View File

@ -43,6 +43,8 @@ describe('No Results', () => {
cy.visitChartByParams(JSON.stringify(formData));
cy.wait('@getJson').its('response.statusCode').should('eq', 200);
cy.get('div.chart-container').contains('No Results');
cy.get('div.chart-container').contains(
'No results were returned for this query',
);
});
});

View File

@ -29,13 +29,17 @@ describe('Visualization > Line', () => {
it('should show validator error when no metric', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
cy.visitChartByParams(JSON.stringify(formData));
cy.get('.ant-alert-warning').contains(`Metrics: cannot be empty`);
cy.get('.panel-body').contains(
`Add required control values to preview chart`,
);
});
it('should not show validator error when metric added', () => {
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
cy.visitChartByParams(JSON.stringify(formData));
cy.get('.ant-alert-warning').contains(`Metrics: cannot be empty`);
cy.get('.panel-body').contains(
`Add required control values to preview chart`,
);
cy.get('.text-danger').contains('Metrics');
cy.get('[data-test=metrics]')

View File

@ -77,6 +77,11 @@ export type Props = Omit<SuperChartCoreProps, 'chartProps'> &
* because it will clash with auto-sizing.
*/
Wrapper?: React.ComponentType<WrapperProps>;
/**
* Component to display when query returns no results.
* If not defined, NoResultsComponent is used
*/
noResults?: ReactNode;
};
type PropsWithDefault = Props & Readonly<typeof defaultProps>;
@ -148,6 +153,7 @@ export default class SuperChart extends React.PureComponent<Props, {}> {
Wrapper,
queriesData,
enableNoResults,
noResults,
...rest
} = this.props as PropsWithDefault;
@ -167,7 +173,7 @@ export default class SuperChart extends React.PureComponent<Props, {}> {
({ data }) => !data || (Array.isArray(data) && data.length === 0),
));
if (noResultQueries) {
chart = (
chart = noResults || (
<NoResultsComponent
id={id}
className={className}

View File

@ -18,17 +18,17 @@
*/
import PropTypes from 'prop-types';
import React from 'react';
import Alert from 'src/components/Alert';
import { styled, logging, t } from '@superset-ui/core';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants';
import Button from 'src/components/Button';
import Loading from 'src/components/Loading';
import ErrorBoundary from '../components/ErrorBoundary';
import { EmptyStateBig } from 'src/components/EmptyState';
import ErrorBoundary from 'src/components/ErrorBoundary';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
import ChartRenderer from './ChartRenderer';
import { ChartErrorMessage } from './ChartErrorMessage';
import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger/LogUtils';
const propTypes = {
annotationData: PropTypes.object,
@ -96,6 +96,10 @@ const Styles = styled.div`
opacity: 0.75;
font-size: ${({ theme }) => theme.typography.sizes.s}px;
}
.slice_container {
height: ${p => p.height}px;
}
`;
const RefreshOverlayWrapper = styled.div`
@ -248,11 +252,20 @@ class Chart extends React.PureComponent {
}
if (errorMessage) {
const description = isFeatureEnabled(
FeatureFlag.ENABLE_EXPLORE_DRAG_AND_DROP,
)
? t(
'Drag and drop values into highlighted field(s) on the left control panel and run query',
)
: t(
'Select values in highlighted field(s) on the left control panel and run query',
);
return (
<Alert
data-test="alert-warning"
message={errorMessage}
type="warning"
<EmptyStateBig
title={t('Add required control values to preview chart')}
description={description}
image="chart.svg"
/>
);
}

View File

@ -19,8 +19,9 @@
import { snakeCase, isEqual } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import { SuperChart, logging, Behavior } from '@superset-ui/core';
import { SuperChart, logging, Behavior, t } from '@superset-ui/core';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
import { EmptyStateBig } from 'src/components/EmptyState';
const propTypes = {
annotationData: PropTypes.object,
@ -231,6 +232,15 @@ class ChartRenderer extends React.Component {
queriesData={queriesResponse}
onRenderSuccess={this.handleRenderSuccess}
onRenderFailure={this.handleRenderFailure}
noResults={
<EmptyStateBig
title={t('No results were returned for this query')}
description={t(
'Make sure that the controls are configured properly and the datasource contains data for the selected time range',
)}
image="chart.svg"
/>
}
/>
);
}

View File

@ -18,7 +18,6 @@
*/
import React from 'react';
import { waitFor } from '@testing-library/react';
import { sliceId as chartId } from 'spec/fixtures/mockChartQueries';
import { nativeFiltersInfo } from 'src/dashboard/fixtures/mockNativeFilters';
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
@ -80,14 +79,17 @@ describe('ChartHolder', () => {
</Provider>,
);
it('should render full size', async () => {
it('should render empty state', async () => {
renderWrapper();
const chart = (
screen.getByTestId('slice-container').firstChild as HTMLElement
).style;
await waitFor(() => expect(chart?.width).toBe('992px'));
expect(chart?.height).toBe('714px');
expect(
screen.getByText('No results were returned for this query'),
).toBeVisible();
expect(
screen.getByText(
'Make sure that the controls are configured properly and the datasource contains data for the selected time range',
),
).toBeVisible();
expect(screen.getByAltText('empty')).toBeVisible();
});
});