You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by kg...@apache.org on 2022/02/14 15:15:44 UTC

[superset] branch master updated: feat(explore): Implement chart empty states (#18678)

This is an automated email from the ASF dual-hosted git repository.

kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 167e18e  feat(explore): Implement chart empty states (#18678)
167e18e is described below

commit 167e18e806799dede3aa56da98be11f4751f0272
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Mon Feb 14 16:14:20 2022 +0100

    feat(explore): Implement chart empty states (#18678)
    
    * feat(explore): Implement chart empty states
    
    * Fix test
    
    * Remove unused import
    
    * Fix tests
---
 .../cypress/integration/explore/chart.test.js      |  4 +++-
 .../explore/visualizations/line.test.ts            |  8 +++++--
 .../src/chart/components/SuperChart.tsx            |  8 ++++++-
 superset-frontend/src/chart/Chart.jsx              | 27 ++++++++++++++++------
 superset-frontend/src/chart/ChartRenderer.jsx      | 12 +++++++++-
 .../components/gridComponents/ChartHolder.test.tsx | 18 ++++++++-------
 6 files changed, 57 insertions(+), 20 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js b/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js
index acb8326..c9f4a1c 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js
+++ b/superset-frontend/cypress-base/cypress/integration/explore/chart.test.js
@@ -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',
+    );
   });
 });
diff --git a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
index 63473a1..5dda1ab 100644
--- a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/line.test.ts
@@ -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]')
diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx
index ac3f3ed..a8e5592 100644
--- a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx
@@ -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}
diff --git a/superset-frontend/src/chart/Chart.jsx b/superset-frontend/src/chart/Chart.jsx
index be74672..14657cc 100644
--- a/superset-frontend/src/chart/Chart.jsx
+++ b/superset-frontend/src/chart/Chart.jsx
@@ -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"
         />
       );
     }
diff --git a/superset-frontend/src/chart/ChartRenderer.jsx b/superset-frontend/src/chart/ChartRenderer.jsx
index 03e0b25..a5a20c6 100644
--- a/superset-frontend/src/chart/ChartRenderer.jsx
+++ b/superset-frontend/src/chart/ChartRenderer.jsx
@@ -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"
+          />
+        }
       />
     );
   }
diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
index 6268e96..4b2b910 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
@@ -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();
   });
 });