This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin 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 fcd166149c chore: Empty state refactor (#31860)
fcd166149c is described below
commit fcd166149cfb896a2a389a5c9075b24eb2cf1ade
Author: Maxime Beauchemin <[email protected]>
AuthorDate: Wed Jan 22 13:20:38 2025 -0800
chore: Empty state refactor (#31860)
---
superset-frontend/package-lock.json | 118 ++--------
.../src/SqlLab/components/QueryHistory/index.tsx | 5 +-
.../src/SqlLab/components/SouthPane/Results.tsx | 4 +-
.../src/SqlLab/components/SqlEditor/index.tsx | 5 +-
.../SqlLab/components/SqlEditorLeftBar/index.tsx | 6 +-
.../SqlLab/components/TabbedSqlEditors/index.tsx | 5 +-
superset-frontend/src/assets/images/chart.svg | Bin 3130 -> 3223 bytes
superset-frontend/src/assets/images/document.svg | Bin 1038 -> 1072 bytes
.../src/assets/images/empty-charts.svg | Bin 3823 -> 1461 bytes
.../src/assets/images/empty-dashboard.svg | Bin 1583 -> 1631 bytes
.../src/assets/images/empty-dataset.svg | Bin 4219 -> 4303 bytes
.../src/assets/images/empty-queries.svg | Bin 5778 -> 5832 bytes
.../src/assets/images/empty-query.svg | Bin 1702 -> 1702 bytes
.../src/assets/images/empty-table.svg | Bin 2554 -> 2541 bytes
superset-frontend/src/assets/images/empty.svg | Bin 1915 -> 1944 bytes
.../src/assets/images/empty_sql_chart.svg | Bin 5972 -> 5957 bytes
.../src/assets/images/filter-results.svg | Bin 1904 -> 1902 bytes
superset-frontend/src/assets/images/filter.svg | Bin 1728 -> 1606 bytes
.../src/assets/images/star-circle.svg | Bin 2023 -> 2051 bytes
superset-frontend/src/assets/images/union.svg | Bin 1871 -> 1899 bytes
superset-frontend/src/assets/images/vector.svg | Bin 2665 -> 2669 bytes
superset-frontend/src/components/Chart/Chart.tsx | 8 +-
.../src/components/Chart/ChartRenderer.jsx | 7 +-
.../Chart/DrillDetail/DrillDetailPane.tsx | 4 +-
.../DatabaseSelector/DatabaseSelector.test.tsx | 4 +-
.../components/EmptyState/EmptyState.stories.tsx | 99 ++++----
.../src/components/EmptyState/index.tsx | 251 ++++++++-------------
.../src/components/ListView/ListView.tsx | 8 +-
superset-frontend/src/components/Table/index.tsx | 59 ++---
.../DashboardBuilder/DashboardBuilder.tsx | 5 +-
.../src/dashboard/components/DashboardGrid.jsx | 11 +-
.../components/gridComponents/ChartHolder.test.tsx | 2 +-
.../dashboard/components/gridComponents/Tab.jsx | 4 +-
.../components/gridComponents/Tab.test.tsx | 4 +-
.../nativeFilters/FilterBar/Vertical.tsx | 5 +-
.../DataTablesPane/components/SamplesPane.tsx | 4 +-
.../DataTablesPane/components/useResultsPane.tsx | 6 +-
.../AnnotationLayerControl/AnnotationLayer.jsx | 5 +-
.../DndColumnSelectControl/ColumnSelectPopover.tsx | 11 +-
.../MetricControl/AdhocMetricEditPopover/index.jsx | 8 +-
.../src/features/allEntities/AllEntitiesTable.tsx | 5 +-
.../AddDataset/DatasetPanel/MessageContent.tsx | 7 +-
.../AddDataset/EditDataset/UsageTab/index.tsx | 7 +-
.../datasets/AddDataset/LeftPanel/index.tsx | 21 +-
.../src/features/home/ActivityTable.test.tsx | 6 +-
.../src/features/home/ChartTable.test.tsx | 4 +-
.../src/features/home/EmptyState.test.tsx | 14 +-
superset-frontend/src/features/home/EmptyState.tsx | 206 ++++++-----------
48 files changed, 363 insertions(+), 555 deletions(-)
diff --git a/superset-frontend/package-lock.json
b/superset-frontend/package-lock.json
index a7ac01fa41..1b06e003e4 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -1714,15 +1714,18 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.24.8",
- "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
- "integrity":
"sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==",
+ "version": "7.25.9",
+ "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity":
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.24.7",
+ "version": "7.25.9",
+ "resolved":
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity":
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -3496,13 +3499,13 @@
"license": "MIT"
},
"node_modules/@babel/types": {
- "version": "7.25.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz",
- "integrity":
"sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==",
+ "version": "7.26.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz",
+ "integrity":
"sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==",
+ "license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.24.8",
- "@babel/helper-validator-identifier": "^7.24.7",
- "to-fast-properties": "^2.0.0"
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
@@ -50919,13 +50922,6 @@
"dev": true,
"license": "BSD-3-Clause"
},
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-object-path": {
"version": "0.3.0",
"dev": true,
@@ -56297,26 +56293,6 @@
"node": ">=6.9.0"
}
},
- "packages/superset-ui-demo/node_modules/@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity":
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
-
"packages/superset-ui-demo/node_modules/@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity":
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
"packages/superset-ui-demo/node_modules/@babel/helper-validator-option": {
"version": "7.25.9",
"resolved":
"https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
@@ -58309,26 +58285,6 @@
"react-dom": "^16.13.1"
}
},
-
"plugins/plugin-chart-pivot-table/node_modules/@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity":
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
-
"plugins/plugin-chart-pivot-table/node_modules/@babel/helper-validator-identifier":
{
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity":
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.9.0"
- }
- },
"plugins/plugin-chart-pivot-table/node_modules/@babel/types": {
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
@@ -59449,12 +59405,14 @@
}
},
"@babel/helper-string-parser": {
- "version": "7.24.8",
- "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
- "integrity":
"sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ=="
+ "version": "7.25.9",
+ "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity":
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
},
"@babel/helper-validator-identifier": {
- "version": "7.24.7"
+ "version": "7.25.9",
+ "resolved":
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity":
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
},
"@babel/helper-validator-option": {
"version": "7.24.7",
@@ -60507,13 +60465,12 @@
}
},
"@babel/types": {
- "version": "7.25.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz",
- "integrity":
"sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==",
+ "version": "7.26.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz",
+ "integrity":
"sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==",
"requires": {
- "@babel/helper-string-parser": "^7.24.8",
- "@babel/helper-validator-identifier": "^7.24.7",
- "to-fast-properties": "^2.0.0"
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
}
},
"@base2/pretty-print-object": {
@@ -66764,18 +66721,6 @@
"integrity":
"sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
"dev": true
},
- "@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity":
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "dev": true
- },
- "@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity":
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "dev": true
- },
"@babel/helper-validator-option": {
"version": "7.25.9",
"resolved":
"https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
@@ -68904,18 +68849,6 @@
"jest": "^29.7.0"
},
"dependencies": {
- "@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity":
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "dev": true
- },
- "@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved":
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity":
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "dev": true
- },
"@babel/types": {
"version": "7.26.3",
"resolved":
"https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
@@ -93957,9 +93890,6 @@
"version": "1.0.5",
"dev": true
},
- "to-fast-properties": {
- "version": "2.0.0"
- },
"to-object-path": {
"version": "0.3.0",
"dev": true,
diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
index 30103dafa5..6916e1e19b 100644
--- a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx
@@ -20,7 +20,7 @@ import { useEffect, useMemo, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { useInView } from 'react-intersection-observer';
import { omit } from 'lodash';
-import { EmptyStateMedium } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import {
t,
styled,
@@ -143,8 +143,9 @@ const QueryHistory = ({
</>
) : (
<StyledEmptyStateWrapper>
- <EmptyStateMedium
+ <EmptyState
title={t('Run a query to display query history')}
+ size="medium"
image="document.svg"
/>
</StyledEmptyStateWrapper>
diff --git a/superset-frontend/src/SqlLab/components/SouthPane/Results.tsx
b/superset-frontend/src/SqlLab/components/SouthPane/Results.tsx
index 3dfec40bdc..6a2a60831b 100644
--- a/superset-frontend/src/SqlLab/components/SouthPane/Results.tsx
+++ b/superset-frontend/src/SqlLab/components/SouthPane/Results.tsx
@@ -19,7 +19,7 @@
import { FC } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import Alert from 'src/components/Alert';
-import { EmptyStateMedium } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { FeatureFlag, styled, t, isFeatureEnabled } from '@superset-ui/core';
import { SqlLabRootState } from 'src/SqlLab/types';
@@ -67,7 +67,7 @@ const Results: FC<Props> = ({
) {
return (
<StyledEmptyStateWrapper>
- <EmptyStateMedium
+ <EmptyState
title={t('Run a query to display results')}
image="document.svg"
/>
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
index 4d1aded5be..09a2fa94e4 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
@@ -100,7 +100,7 @@ import {
LocalStorageKeys,
setItem,
} from 'src/utils/localStorageHelpers';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import Alert from 'src/components/Alert';
import getBootstrapData from 'src/utils/getBootstrapData';
import useLogAction from 'src/logger/useLogAction';
@@ -968,8 +968,9 @@ const SqlEditor: FC<Props> = ({
<Skeleton active />
</div>
) : showEmptyState && !hasSqlStatement ? (
- <EmptyStateBig
+ <EmptyState
image="vector.svg"
+ size="large"
title={t('Select a database to write a query')}
description={t(
'Choose one of the available databases from the panel on the
left.',
diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
index c91036a725..1e0012aed2 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
@@ -41,7 +41,7 @@ import { TableSelectorMultiple } from
'src/components/TableSelector';
import { IconTooltip } from 'src/components/IconTooltip';
import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
import type { DatabaseObject } from 'src/components/DatabaseSelector';
-import { emptyStateComponent } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import {
getItem,
LocalStorageKeys,
@@ -113,7 +113,7 @@ const SqlEditorLeftBar = ({
'schema',
]);
- const [emptyResultsWithSearch, setEmptyResultsWithSearch] = useState(false);
+ const [_emptyResultsWithSearch, setEmptyResultsWithSearch] = useState(false);
const [userSelectedDb, setUserSelected] = useState<DatabaseObject | null>(
null,
);
@@ -249,7 +249,7 @@ const SqlEditorLeftBar = ({
<LeftBarStyles data-test="sql-editor-left-bar">
<TableSelectorMultiple
onEmptyResults={onEmptyResults}
- emptyState={emptyStateComponent(emptyResultsWithSearch)}
+ emptyState={<EmptyState />}
database={userSelectedDb}
getDbList={handleDbList}
handleError={handleError}
diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx
b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx
index 3105aa94bd..a9360ad655 100644
--- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx
+++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx
@@ -27,7 +27,7 @@ import { Logger } from 'src/logger/LogUtils';
import { Tooltip } from 'src/components/Tooltip';
import { detectOS } from 'src/utils/common';
import * as Actions from 'src/SqlLab/actions/sqlLab';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import getBootstrapData from 'src/utils/getBootstrapData';
import { locationContext } from 'src/pages/SqlLab/LocationContext';
import SqlEditor from '../SqlEditor';
@@ -259,8 +259,9 @@ class TabbedSqlEditors extends
PureComponent<TabbedSqlEditorsProps> {
tab={emptyTab}
closable={false}
>
- <EmptyStateBig
+ <EmptyState
image="empty_sql_chart.svg"
+ size="large"
description={t('Add a new tab to create SQL Query')}
/>
</EditableTabs.TabPane>
diff --git a/superset-frontend/src/assets/images/chart.svg
b/superset-frontend/src/assets/images/chart.svg
index 2267342bcc..34e45631e0 100644
Binary files a/superset-frontend/src/assets/images/chart.svg and
b/superset-frontend/src/assets/images/chart.svg differ
diff --git a/superset-frontend/src/assets/images/document.svg
b/superset-frontend/src/assets/images/document.svg
index e3d1bfe1be..552fc8d220 100644
Binary files a/superset-frontend/src/assets/images/document.svg and
b/superset-frontend/src/assets/images/document.svg differ
diff --git a/superset-frontend/src/assets/images/empty-charts.svg
b/superset-frontend/src/assets/images/empty-charts.svg
index b4cdd99086..8c7b205d2a 100644
Binary files a/superset-frontend/src/assets/images/empty-charts.svg and
b/superset-frontend/src/assets/images/empty-charts.svg differ
diff --git a/superset-frontend/src/assets/images/empty-dashboard.svg
b/superset-frontend/src/assets/images/empty-dashboard.svg
index c76eca0c30..2acf05c801 100644
Binary files a/superset-frontend/src/assets/images/empty-dashboard.svg and
b/superset-frontend/src/assets/images/empty-dashboard.svg differ
diff --git a/superset-frontend/src/assets/images/empty-dataset.svg
b/superset-frontend/src/assets/images/empty-dataset.svg
index 5ce1752545..9a0299d3b4 100644
Binary files a/superset-frontend/src/assets/images/empty-dataset.svg and
b/superset-frontend/src/assets/images/empty-dataset.svg differ
diff --git a/superset-frontend/src/assets/images/empty-queries.svg
b/superset-frontend/src/assets/images/empty-queries.svg
index 2239c0ae8e..1d2d8b00f2 100644
Binary files a/superset-frontend/src/assets/images/empty-queries.svg and
b/superset-frontend/src/assets/images/empty-queries.svg differ
diff --git a/superset-frontend/src/assets/images/empty-query.svg
b/superset-frontend/src/assets/images/empty-query.svg
index be72857d8e..a19fc73eeb 100644
Binary files a/superset-frontend/src/assets/images/empty-query.svg and
b/superset-frontend/src/assets/images/empty-query.svg differ
diff --git a/superset-frontend/src/assets/images/empty-table.svg
b/superset-frontend/src/assets/images/empty-table.svg
index c1062502f3..9079b07114 100644
Binary files a/superset-frontend/src/assets/images/empty-table.svg and
b/superset-frontend/src/assets/images/empty-table.svg differ
diff --git a/superset-frontend/src/assets/images/empty.svg
b/superset-frontend/src/assets/images/empty.svg
index e2c78339ce..b503324f67 100644
Binary files a/superset-frontend/src/assets/images/empty.svg and
b/superset-frontend/src/assets/images/empty.svg differ
diff --git a/superset-frontend/src/assets/images/empty_sql_chart.svg
b/superset-frontend/src/assets/images/empty_sql_chart.svg
index 6ab969575c..b729b471a6 100644
Binary files a/superset-frontend/src/assets/images/empty_sql_chart.svg and
b/superset-frontend/src/assets/images/empty_sql_chart.svg differ
diff --git a/superset-frontend/src/assets/images/filter-results.svg
b/superset-frontend/src/assets/images/filter-results.svg
index 770a54b34f..7244f5538b 100644
Binary files a/superset-frontend/src/assets/images/filter-results.svg and
b/superset-frontend/src/assets/images/filter-results.svg differ
diff --git a/superset-frontend/src/assets/images/filter.svg
b/superset-frontend/src/assets/images/filter.svg
index 0e1f6b41ef..2e8c5e0f09 100644
Binary files a/superset-frontend/src/assets/images/filter.svg and
b/superset-frontend/src/assets/images/filter.svg differ
diff --git a/superset-frontend/src/assets/images/star-circle.svg
b/superset-frontend/src/assets/images/star-circle.svg
index a46a1dd0fb..d9e6c77e2c 100644
Binary files a/superset-frontend/src/assets/images/star-circle.svg and
b/superset-frontend/src/assets/images/star-circle.svg differ
diff --git a/superset-frontend/src/assets/images/union.svg
b/superset-frontend/src/assets/images/union.svg
index 6ac0e0f016..9ccf437b22 100644
Binary files a/superset-frontend/src/assets/images/union.svg and
b/superset-frontend/src/assets/images/union.svg differ
diff --git a/superset-frontend/src/assets/images/vector.svg
b/superset-frontend/src/assets/images/vector.svg
index 0bf9c39c6c..d2f946e810 100644
Binary files a/superset-frontend/src/assets/images/vector.svg and
b/superset-frontend/src/assets/images/vector.svg differ
diff --git a/superset-frontend/src/components/Chart/Chart.tsx
b/superset-frontend/src/components/Chart/Chart.tsx
index 3526a26b3f..fd7b5fcaab 100644
--- a/superset-frontend/src/components/Chart/Chart.tsx
+++ b/superset-frontend/src/components/Chart/Chart.tsx
@@ -31,7 +31,7 @@ import {
} from '@superset-ui/core';
import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants';
import Loading from 'src/components/Loading';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import ErrorBoundary from 'src/components/ErrorBoundary';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
import { URL_PARAMS } from 'src/constants';
@@ -337,7 +337,8 @@ class Chart extends PureComponent<ChartProps, {}> {
if (errorMessage && ensureIsArray(queriesResponse).length === 0) {
return (
- <EmptyStateBig
+ <EmptyState
+ size="large"
title={t('Add required control values to preview chart')}
description={getChartRequiredFieldsMissingMessage(true)}
image="chart.svg"
@@ -352,7 +353,8 @@ class Chart extends PureComponent<ChartProps, {}> {
ensureIsArray(queriesResponse).length === 0
) {
return (
- <EmptyStateBig
+ <EmptyState
+ size="large"
title={t('Your chart is ready to go!')}
description={
<span>
diff --git a/superset-frontend/src/components/Chart/ChartRenderer.jsx
b/superset-frontend/src/components/Chart/ChartRenderer.jsx
index d93e30a107..dd456b0db4 100644
--- a/superset-frontend/src/components/Chart/ChartRenderer.jsx
+++ b/superset-frontend/src/components/Chart/ChartRenderer.jsx
@@ -30,7 +30,7 @@ import {
VizType,
} from '@superset-ui/core';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
-import { EmptyStateBig, EmptyStateSmall } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { ChartSource } from 'src/types/ChartSource';
import ChartContextMenu from './ChartContextMenu/ChartContextMenu';
@@ -308,7 +308,8 @@ class ChartRenderer extends Component {
const noResultImage = 'chart.svg';
if (width > BIG_NO_RESULT_MIN_WIDTH && height > BIG_NO_RESULT_MIN_HEIGHT) {
noResultsComponent = (
- <EmptyStateBig
+ <EmptyState
+ size="large"
title={noResultTitle}
description={noResultDescription}
image={noResultImage}
@@ -316,7 +317,7 @@ class ChartRenderer extends Component {
);
} else {
noResultsComponent = (
- <EmptyStateSmall title={noResultTitle} image={noResultImage} />
+ <EmptyState size="small" title={noResultTitle} image={noResultImage} />
);
}
diff --git
a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
index 9cf0102b5e..bcab733f48 100644
--- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
+++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailPane.tsx
@@ -41,7 +41,7 @@ import Loading from 'src/components/Loading';
import BooleanCell from 'src/components/Table/cell-renderers/BooleanCell';
import NullCell from 'src/components/Table/cell-renderers/NullCell';
import TimeCell from 'src/components/Table/cell-renderers/TimeCell';
-import { EmptyStateMedium } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { getDatasourceSamples } from 'src/components/Chart/chartAction';
import Table, { ColumnsType, TableSize } from 'src/components/Table';
import HeaderWithRadioGroup from
'src/components/Table/header-renderers/HeaderWithRadioGroup';
@@ -285,7 +285,7 @@ export default function DrillDetailPane({
} else if (resultsPage?.total === 0) {
// Render empty state if no results are returned for page
const title = t('No rows were returned for this dataset');
- tableContent = <EmptyStateMedium image="document.svg" title={title} />;
+ tableContent = <EmptyState image="document.svg" title={title} />;
} else {
// Render table if at least one page has successfully loaded
tableContent = (
diff --git
a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
index 44d0bff0e0..ee17f2d801 100644
---
a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
+++
b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
@@ -28,7 +28,7 @@ import {
import userEvent from '@testing-library/user-event';
import { api } from 'src/hooks/apiResources/queryApi';
import DatabaseSelector, { DatabaseSelectorProps } from '.';
-import { EmptyStateSmall } from '../EmptyState';
+import { EmptyState } from '../EmptyState';
const createProps = (): DatabaseSelectorProps => ({
db: {
@@ -307,7 +307,7 @@ test('should show empty state if there are no options',
async () => {
<DatabaseSelector
{...props}
db={undefined}
- emptyState={<EmptyStateSmall title="empty" image="" />}
+ emptyState={<EmptyState size="small" title="empty" />}
/>,
{ useRedux: true, store },
);
diff --git a/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
b/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
index 9d6f56c52d..dbc3a0af9c 100644
--- a/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
+++ b/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
@@ -9,64 +9,59 @@
*
* 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
+ * 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 { Meta, StoryFn } from '@storybook/react';
+import { Row, Col } from 'antd';
+import { EmptyState, imageMap } from '.';
-import EmptyImage from 'src/assets/images/empty.svg';
-import ChartImage from 'src/assets/images/chart.svg';
-import FilterImage from 'src/assets/images/filter.svg';
-import { EmptyStateBig, EmptyStateMedium, EmptyStateSmall } from '.';
+const emptyStates = [
+ { title: null, description: null, image: undefined },
+ ...Object.keys(imageMap).map(key => ({
+ image: key,
+ title: `Empty State with image ${key}`,
+ description: 'This is the default empty state.',
+ })),
+];
export default {
- title: 'Empty state',
- component: EmptyStateMedium,
-};
-
-export const SmallEmptyState = () => (
- <EmptyStateSmall
- image={<FilterImage />}
- title="Small empty state"
- description="This is an example of a small empty state"
- />
-);
-
-export const MediumEmptyState = () => (
- <EmptyStateMedium
- image={<ChartImage />}
- title="Medium empty state"
- description="This is an example of a medium empty state"
- />
-);
+ title: 'Empty State Gallery',
+ component: EmptyState,
+ argTypes: {
+ size: {
+ control: { type: 'select' },
+ options: ['small', 'medium', 'large'],
+ defaultValue: 'medium',
+ description: 'Size of the Empty State components',
+ },
+ },
+} as Meta;
-export const MediumEmptyStateWithButton = () => (
- <EmptyStateMedium
- image={<EmptyImage />}
- title="Medium empty state"
- description="This is an example of a medium empty state with a button"
- buttonAction={() => {}}
- buttonText="Click!"
- />
+export const Gallery: StoryFn<{ size: 'small' | 'medium' | 'large' }> = ({
+ size,
+}) => (
+ <Row gutter={[16, 16]}>
+ {emptyStates.map((state, index) => (
+ <Col key={index} xs={24} sm={12} md={8} lg={6}>
+ <EmptyState
+ size={size}
+ title={state.title}
+ description={state.description}
+ image={state.image}
+ >
+ Childrens render here.
+ </EmptyState>
+ </Col>
+ ))}
+ </Row>
);
-export const BigEmptyState = () => (
- <EmptyStateBig
- image={<EmptyImage />}
- title="Big empty state"
- description="This is an example of a big empty state"
- />
-);
-
-export const BigEmptyStateWithButton = () => (
- <EmptyStateBig
- image={<ChartImage />}
- title="Big empty state"
- description="This is an example of a big empty state with a button"
- buttonText="Click!"
- buttonAction={() => {}}
- />
-);
+Gallery.args = {
+ size: 'medium',
+};
diff --git a/superset-frontend/src/components/EmptyState/index.tsx
b/superset-frontend/src/components/EmptyState/index.tsx
index edc13e622f..a083b6cdcf 100644
--- a/superset-frontend/src/components/EmptyState/index.tsx
+++ b/superset-frontend/src/components/EmptyState/index.tsx
@@ -16,38 +16,57 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-import {
- ReactNode,
- SyntheticEvent,
- MouseEventHandler as ReactMouseEventHandler,
-} from 'react';
+import { ReactNode, SyntheticEvent } from 'react';
import { styled, css, SupersetTheme, t } from '@superset-ui/core';
import Button from 'src/components/Button';
+
+// Importing svg images
+import FilterResultsImage from 'src/assets/images/filter-results.svg';
+import ChartImage from 'src/assets/images/chart.svg';
+import FilterImage from 'src/assets/images/filter.svg';
+import EmptyChartsImage from 'src/assets/images/empty-charts.svg';
+import EmptyDashboardImage from 'src/assets/images/empty-dashboard.svg';
+import UnionImage from 'src/assets/images/union.svg';
+import EmptyQueriesImage from 'src/assets/images/empty-queries.svg';
+import StarCircleImage from 'src/assets/images/star-circle.svg';
+import VectorImage from 'src/assets/images/vector.svg';
+import DocumentImage from 'src/assets/images/document.svg';
+import DatasetImage from 'src/assets/images/empty-dataset.svg';
+import EmptySqlChartImage from 'src/assets/images/empty_sql_chart.svg';
+import EmptyQueryImage from 'src/assets/images/empty-query.svg';
+import EmptyTableImage from 'src/assets/images/empty-table.svg';
+import EmptyImage from 'src/assets/images/empty.svg';
import { Empty } from './Empty';
-export enum EmptyStateSize {
- Small,
- Medium,
- Big,
-}
+export const imageMap = {
+ 'chart.svg': <ChartImage />,
+ 'document.svg': <DocumentImage />,
+ 'empty-charts.svg': <EmptyChartsImage />,
+ 'empty-dashboard.svg': <EmptyDashboardImage />,
+ 'empty-dataset.svg': <DatasetImage />,
+ 'empty-queries.svg': <EmptyQueriesImage />,
+ 'empty-query.svg': <EmptyQueryImage />,
+ 'empty-table.svg': <EmptyTableImage />,
+ 'empty.svg': <EmptyImage />,
+ 'empty_sql_chart.svg': <EmptySqlChartImage />,
+ 'filter-results.svg': <FilterResultsImage />,
+ 'filter.svg': <FilterImage />,
+ 'star-circle.svg': <StarCircleImage />,
+ 'union.svg': <UnionImage />,
+ 'vector.svg': <VectorImage />,
+};
-export interface EmptyStateSmallProps {
+type EmptyStateSize = 'small' | 'medium' | 'large';
+
+export type EmptyStateProps = {
title?: ReactNode;
description?: ReactNode;
- image?: ReactNode;
-}
-
-export interface EmptyStateProps extends EmptyStateSmallProps {
+ image?: ReactNode | string;
buttonText?: ReactNode;
- buttonAction?: ReactMouseEventHandler<HTMLElement>;
- className?: string;
-}
-
-export interface ImageContainerProps {
- image: ReactNode;
- size: EmptyStateSize;
-}
+ buttonAction?: (event: SyntheticEvent) => void;
+ size?: EmptyStateSize;
+ children?: ReactNode;
+};
const EmptyStateContainer = styled.div`
${({ theme }) => css`
@@ -55,6 +74,7 @@ const EmptyStateContainer = styled.div`
flex-direction: column;
width: 100%;
height: 100%;
+ color: ${theme.colors.grayscale.light2};
align-items: center;
justify-content: center;
padding: ${theme.gridUnit * 4}px;
@@ -75,43 +95,24 @@ const EmptyStateContainer = styled.div`
`}
`;
-const TextContainer = styled.div``;
-
-const Title = styled.p`
- ${({ theme }) => css`
- font-size: ${theme.typography.sizes.m}px;
+const Title = styled.p<{ size: EmptyStateSize }>`
+ ${({ theme, size }) => css`
+ font-size: ${size === 'large'
+ ? theme.typography.sizes.l
+ : theme.typography.sizes.m}px;
color: ${theme.colors.grayscale.light1};
- margin: ${theme.gridUnit * 2}px 0 0 0;
+ margin-top: ${size === 'large' ? theme.gridUnit * 4 : theme.gridUnit *
2}px;
font-weight: ${theme.typography.weights.bold};
`}
`;
-const BigTitle = styled(Title)`
- ${({ theme }) => css`
- font-size: ${theme.typography.sizes.l}px;
- color: ${theme.colors.grayscale.light1};
- margin-top: ${theme.gridUnit * 4}px;
- `}
-`;
-
-const Description = styled.p`
- ${({ theme }) => css`
- font-size: ${theme.typography.sizes.s}px;
+const Description = styled.p<{ size: EmptyStateSize }>`
+ ${({ theme, size }) => css`
+ font-size: ${size === 'large'
+ ? theme.typography.sizes.m
+ : theme.typography.sizes.s}px;
color: ${theme.colors.grayscale.light1};
- margin: ${theme.gridUnit * 2}px 0 0 0;
- `}
-`;
-
-const BigDescription = styled(Description)`
- ${({ theme }) => css`
- font-size: ${theme.typography.sizes.m}px;
- `}
-`;
-
-const SmallDescription = styled(Description)`
- ${({ theme }) => css`
- margin-top: ${theme.gridUnit}px;
- line-height: 1.2;
+ margin-top: ${theme.gridUnit * 2}px;
`}
`;
@@ -122,81 +123,68 @@ const ActionButton = styled(Button)`
`}
`;
-const getImage = (image: string | ReactNode) =>
- typeof image === 'string' ? `/static/assets/images/${image}` : image;
-
const getImageHeight = (size: EmptyStateSize) => {
switch (size) {
- case EmptyStateSize.Small:
+ case 'small':
return { height: '50px' };
- case EmptyStateSize.Medium:
+ case 'medium':
return { height: '80px' };
- case EmptyStateSize.Big:
+ case 'large':
return { height: '150px' };
default:
- return { height: '50px' };
+ return { height: '80px' };
}
};
-const ImageContainer = ({ image, size }: ImageContainerProps) => (
- <Empty
- description={false}
- image={getImage(image)}
- imageStyle={getImageHeight(size)}
- />
-);
+const ImageContainer = ({
+ image,
+ size,
+}: {
+ image?: ReactNode | string;
+ size: EmptyStateSize;
+}) => {
+ if (!image) return null;
+ const mappedImage = typeof image === 'string' ? imageMap[image] : image;
+ return (
+ <div role="img" aria-label="empty">
+ <Empty
+ description={false}
+ image={mappedImage}
+ imageStyle={getImageHeight(size)}
+ />
+ </div>
+ );
+};
const handleMouseDown = (e: SyntheticEvent) => {
e.preventDefault();
e.stopPropagation();
};
-export const EmptyStateBig = ({
- title,
- image,
- description,
- buttonAction,
+export const EmptyState: React.FC<EmptyStateProps> = ({
+ title = t('No results'),
+ description = t('There is currently no information to display.'),
+ image = 'empty.svg',
buttonText,
- className,
-}: EmptyStateProps) => (
- <EmptyStateContainer className={className}>
- {image && <ImageContainer image={image} size={EmptyStateSize.Big} />}
- <TextContainer
- css={(theme: SupersetTheme) => css`
- max-width: ${theme.gridUnit * 150}px;
- `}
- >
- <BigTitle>{title}</BigTitle>
- {description && <BigDescription>{description}</BigDescription>}
- {buttonAction && buttonText && (
- <ActionButton
- buttonStyle="primary"
- onClick={buttonAction}
- onMouseDown={handleMouseDown}
- >
- {buttonText}
- </ActionButton>
- )}
- </TextContainer>
- </EmptyStateContainer>
-);
-
-export const EmptyStateMedium = ({
- title,
- image,
- description,
buttonAction,
- buttonText,
-}: EmptyStateProps) => (
+ size = 'medium',
+ children,
+}) => (
<EmptyStateContainer>
- {image && <ImageContainer image={image} size={EmptyStateSize.Medium} />}
- <TextContainer
+ {image && <ImageContainer image={image} size={size} />}
+ <div
css={(theme: SupersetTheme) => css`
- max-width: ${theme.gridUnit * 100}px;
+ max-width: ${size === 'large'
+ ? theme.gridUnit * 150
+ : theme.gridUnit * 100}px;
`}
>
- <Title>{title}</Title>
- {description && <Description>{description}</Description>}
+ {title && <Title size={size}>{title}</Title>}
+ {description && (
+ <Description size={size} className="ant-empty-description">
+ {description}
+ </Description>
+ )}
{buttonText && buttonAction && (
<ActionButton
buttonStyle="primary"
@@ -206,48 +194,7 @@ export const EmptyStateMedium = ({
{buttonText}
</ActionButton>
)}
- </TextContainer>
- </EmptyStateContainer>
-);
-
-export const EmptyStateSmall = ({
- title,
- image,
- description,
-}: EmptyStateSmallProps) => (
- <EmptyStateContainer>
- {image && <ImageContainer image={image} size={EmptyStateSize.Small} />}
- <TextContainer
- css={(theme: SupersetTheme) => css`
- max-width: ${theme.gridUnit * 75}px;
- `}
- >
- <Title>{title}</Title>
- {description && <SmallDescription>{description}</SmallDescription>}
- </TextContainer>
+ {children}
+ </div>
</EmptyStateContainer>
);
-
-const TRANSLATIONS = {
- NO_DATABASES_MATCH_TITLE: t('No databases match your search'),
- NO_DATABASES_AVAILABLE_TITLE: t('There are no databases available'),
- MANAGE_YOUR_DATABASES_TEXT: t('Manage your databases'),
- HERE_TEXT: t('here'),
-};
-
-export const emptyStateComponent = (emptyResultsWithSearch: boolean) => (
- <EmptyStateSmall
- image="empty.svg"
- title={
- emptyResultsWithSearch
- ? TRANSLATIONS.NO_DATABASES_MATCH_TITLE
- : TRANSLATIONS.NO_DATABASES_AVAILABLE_TITLE
- }
- description={
- <p>
- {TRANSLATIONS.MANAGE_YOUR_DATABASES_TEXT}{' '}
- <a href="/databaseview/list">{TRANSLATIONS.HERE_TEXT}</a>
- </p>
- }
- />
-);
diff --git a/superset-frontend/src/components/ListView/ListView.tsx
b/superset-frontend/src/components/ListView/ListView.tsx
index 30bcb99aa3..cfa060f11d 100644
--- a/superset-frontend/src/components/ListView/ListView.tsx
+++ b/superset-frontend/src/components/ListView/ListView.tsx
@@ -37,7 +37,7 @@ import {
ViewModeType,
} from './types';
import { ListViewError, useListViewState } from './utils';
-import { EmptyStateBig, EmptyStateProps } from '../EmptyState';
+import { EmptyState, EmptyStateProps } from '../EmptyState';
const ListViewStyles = styled.div`
text-align: center;
@@ -447,17 +447,19 @@ function ListView<T extends object = any>({
{!loading && rows.length === 0 && (
<EmptyWrapper className={viewMode}>
{query.filters ? (
- <EmptyStateBig
+ <EmptyState
title={t('No results match your filter criteria')}
description={t('Try different criteria to display results.')}
+ size="large"
image="filter-results.svg"
buttonAction={() => handleClearFilterControls()}
buttonText={t('clear all filters')}
/>
) : (
- <EmptyStateBig
+ <EmptyState
{...emptyState}
title={emptyState?.title || t('No Data')}
+ size="large"
image={emptyState?.image || 'filter-results.svg'}
/>
)}
diff --git a/superset-frontend/src/components/Table/index.tsx
b/superset-frontend/src/components/Table/index.tsx
index 1495befa5a..11a803893b 100644
--- a/superset-frontend/src/components/Table/index.tsx
+++ b/superset-frontend/src/components/Table/index.tsx
@@ -16,13 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useState, useEffect, useRef, ReactElement, Key } from 'react';
+import { useState, useEffect, useRef, Key } from 'react';
import AntTable, {
ColumnsType,
TableProps as AntTableProps,
} from 'antd/lib/table';
-import ConfigProvider from 'antd/lib/config-provider';
import { PaginationProps } from 'antd/lib/pagination';
import { t, useTheme, logging, styled } from '@superset-ui/core';
import Loading from 'src/components/Loading';
@@ -116,10 +115,6 @@ export interface TableProps<RecordType> {
* Set table to display no data even if data has been provided
*/
hideData?: boolean;
- /**
- * emptyComponent
- */
- emptyComponent?: ReactElement;
/**
* Enables setting the text displayed in various components and tooltips
within the Table UI.
*/
@@ -256,7 +251,6 @@ export function Table<RecordType extends object>(
defaultPageSize = 15,
pageSizeOptions = ['5', '15', '25', '50', '100'],
hideData = false,
- emptyComponent,
locale,
height,
virtualize = false,
@@ -288,9 +282,6 @@ export function Table<RecordType extends object>(
onChange: onSelectChange,
};
- const renderEmpty = () =>
- emptyComponent ?? <div>{mergedLocale.emptyText}</div>;
-
// Log use of experimental features
useEffect(() => {
if (reorderable === true) {
@@ -403,31 +394,29 @@ export function Table<RecordType extends object>(
};
return (
- <ConfigProvider renderEmpty={renderEmpty}>
- <div ref={wrapperRef}>
- {!virtualize && (
- <StyledTable
- {...sharedProps}
- rowSelection={selectionTypeValue ? rowSelection : undefined}
- sticky={sticky}
- />
- )}
- {virtualize && (
- <StyledVirtualTable
- {...sharedProps}
- scroll={{
- y: 300,
- x: '100vw',
- // To avoid jest failure by scrollTo
- ...(process.env.WEBPACK_MODE === 'test' && {
- scrollToFirstRowOnChange: false,
- }),
- }}
- allowHTML={allowHTML}
- />
- )}
- </div>
- </ConfigProvider>
+ <div ref={wrapperRef}>
+ {!virtualize && (
+ <StyledTable
+ {...sharedProps}
+ rowSelection={selectionTypeValue ? rowSelection : undefined}
+ sticky={sticky}
+ />
+ )}
+ {virtualize && (
+ <StyledVirtualTable
+ {...sharedProps}
+ scroll={{
+ y: 300,
+ x: '100vw',
+ // To avoid jest failure by scrollTo
+ ...(process.env.WEBPACK_MODE === 'test' && {
+ scrollToFirstRowOnChange: false,
+ }),
+ }}
+ allowHTML={allowHTML}
+ />
+ )}
+ </div>
);
}
diff --git
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index 29dc12a7c8..cdd59b459a 100644
---
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -65,7 +65,7 @@ import {
} from 'src/dashboard/util/constants';
import FilterBar from 'src/dashboard/components/nativeFilters/FilterBar';
import Loading from 'src/components/Loading';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { useUiConfig } from 'src/components/UiConfigContext';
import ResizableSidebar from 'src/components/ResizableSidebar';
import {
@@ -667,8 +667,9 @@ const DashboardBuilder = () => {
{!editMode &&
!topLevelTabs &&
dashboardLayout[DASHBOARD_GRID_ID]?.children?.length === 0 && (
- <EmptyStateBig
+ <EmptyState
title={t('There are no charts added to this dashboard')}
+ size="large"
description={
canEdit &&
t(
diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
index 6c6d5f853a..a5f0e52408 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
@@ -20,7 +20,7 @@ import { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { addAlpha, css, styled, t } from '@superset-ui/core';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { componentShape } from '../util/propShapes';
import DashboardComponent from '../containers/DashboardComponent';
import { Droppable } from './dnd/DragDroppable';
@@ -205,11 +205,12 @@ class DashboardGrid extends PureComponent {
shouldDisplayEmptyState && gridComponent.type === TAB_TYPE;
const dashboardEmptyState = editMode && (
- <EmptyStateBig
+ <EmptyState
title={t('Drag and drop components and charts to the dashboard')}
description={t(
'You can create a new chart or use existing ones from the panel on
the right',
)}
+ size="large"
buttonText={
<>
<i className="fa fa-plus" />
@@ -228,8 +229,9 @@ class DashboardGrid extends PureComponent {
);
const topLevelTabEmptyState = editMode ? (
- <EmptyStateBig
+ <EmptyState
title={t('Drag and drop components to this tab')}
+ size="large"
description={t(
`You can create a new chart or use existing ones from the panel on
the right`,
)}
@@ -249,8 +251,9 @@ class DashboardGrid extends PureComponent {
image="chart.svg"
/>
) : (
- <EmptyStateBig
+ <EmptyState
title={t('There are no components added to this tab')}
+ size="large"
description={
canEdit && t('You can add the components in the edit mode.')
}
diff --git
a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
index 990647b2c3..2a3d0ada5d 100644
---
a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
+++
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
@@ -117,7 +117,7 @@ describe('ChartHolder', () => {
'Make sure that the controls are configured properly and the
datasource contains data for the selected time range',
),
).not.toBeInTheDocument(); // description should display only in Explore
view
- expect(screen.getByAltText('empty')).toBeVisible();
+ expect(screen.getByRole('img', { name: 'empty' })).toBeVisible();
});
it('should render anchor link when not editing', async () => {
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index 2c4695f8c7..36a7731641 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -22,7 +22,7 @@ import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { styled, t } from '@superset-ui/core';
-import { EmptyStateMedium } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import EditableTitle from 'src/components/EditableTitle';
import { setEditMode } from 'src/dashboard/actions/dashboardState';
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
@@ -197,7 +197,7 @@ const Tab = props => {
</Droppable>
)}
{shouldDisplayEmptyState && (
- <EmptyStateMedium
+ <EmptyState
title={
editMode
? t('Drag and drop components to this tab')
diff --git
a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx
b/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx
index 39adea09f9..c007b535d4 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx
@@ -288,7 +288,7 @@ test('Render tab content with no children', () => {
expect(
screen.getByText('There are no components added to this tab'),
).toBeVisible();
- expect(screen.getByAltText('empty')).toBeVisible();
+ expect(screen.getByRole('img', { name: 'empty' })).toBeVisible();
expect(screen.queryByText('edit mode')).not.toBeInTheDocument();
});
@@ -397,7 +397,7 @@ test('Render tab content with no children, editMode: true,
canEdit: true', () =>
expect(
screen.getByText('Drag and drop components to this tab'),
).toBeVisible();
- expect(screen.getByAltText('empty')).toBeVisible();
+ expect(screen.getByRole('img', { name: 'empty' })).toBeVisible();
expect(
screen.getByRole('link', { name: 'create a new chart' }),
).toBeVisible();
diff --git
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
index b76b65c133..d6960ad370 100644
---
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
+++
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx
@@ -33,7 +33,7 @@ import cx from 'classnames';
import { FeatureFlag, isFeatureEnabled, styled, t } from '@superset-ui/core';
import Icons from 'src/components/Icons';
import Loading from 'src/components/Loading';
-import { EmptyStateSmall } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { getFilterBarTestId } from './utils';
import { VerticalBarProps } from './types';
import Header from './Header';
@@ -168,7 +168,8 @@ const VerticalFilterBar: FC<VerticalBarProps> = ({
() =>
filterValues.length === 0 ? (
<FilterBarEmptyStateContainer>
- <EmptyStateSmall
+ <EmptyState
+ size="small"
title={t('No global filters are currently added')}
image="filter.svg"
description={
diff --git
a/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
b/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
index ca0a1d6af4..4e83fa63a5 100644
---
a/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
+++
b/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
@@ -19,7 +19,7 @@
import { useState, useEffect, useMemo } from 'react';
import { ensureIsArray, GenericDataType, styled, t } from '@superset-ui/core';
import Loading from 'src/components/Loading';
-import { EmptyStateMedium } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import TableView, { EmptyWrapperType } from 'src/components/TableView';
import {
useFilteredTableData,
@@ -124,7 +124,7 @@ export const SamplesPane = ({
if (data.length === 0) {
const title = t('No samples were returned for this dataset');
- return <EmptyStateMedium image="document.svg" title={title} />;
+ return <EmptyState image="document.svg" title={title} />;
}
return (
diff --git
a/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx
b/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx
index fe262fe720..e96963d108 100644
---
a/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx
+++
b/superset-frontend/src/explore/components/DataTablesPane/components/useResultsPane.tsx
@@ -26,7 +26,7 @@ import {
getClientErrorObject,
} from '@superset-ui/core';
import Loading from 'src/components/Loading';
-import { EmptyStateMedium } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { getChartDataRequest } from 'src/components/Chart/chartAction';
import { ResultsPaneProps, QueryResultInterface } from '../types';
import { SingleQueryResultPane } from './SingleQueryResultPane';
@@ -110,7 +110,7 @@ export const useResultsPane = ({
if (errorMessage) {
const title = t('Run a query to display results');
return Array(queryCount).fill(
- <EmptyStateMedium image="document.svg" title={title} />,
+ <EmptyState image="document.svg" title={title} />,
);
}
@@ -136,7 +136,7 @@ export const useResultsPane = ({
if (resultResp.length === 0) {
const title = t('No results were returned for this query');
return Array(queryCount).fill(
- <EmptyStateMedium image="document.svg" title={title} />,
+ <EmptyState image="document.svg" title={title} />,
);
}
return resultResp
diff --git
a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
index 304ec3409d..905fd04f9c 100644
---
a/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
+++
b/superset-frontend/src/explore/components/controls/AnnotationLayerControl/AnnotationLayer.jsx
@@ -38,7 +38,7 @@ import TextControl from
'src/explore/components/controls/TextControl';
import CheckboxControl from 'src/explore/components/controls/CheckboxControl';
import PopoverSection from 'src/components/PopoverSection';
import ControlHeader from 'src/explore/components/ControlHeader';
-import { EmptyStateSmall } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import {
ANNOTATION_SOURCE_TYPES,
ANNOTATION_TYPES,
@@ -111,8 +111,9 @@ const NotFoundContentWrapper = styled.div`
const NotFoundContent = () => (
<NotFoundContentWrapper>
- <EmptyStateSmall
+ <EmptyState
title={t('No annotation layers')}
+ size="small"
description={
<span>
{t('Add an annotation layer')}{' '}
diff --git
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
index 711cad392d..6e0a256965 100644
---
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
+++
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx
@@ -43,7 +43,7 @@ import { Select } from 'src/components';
import { Form, FormItem } from 'src/components/Form';
import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
import { SQLEditor } from 'src/components/AsyncAceEditor';
-import { EmptyStateSmall } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { getColumnKeywords } from 'src/explore/controlUtils/getColumnKeywords';
import { StyledColumnOption } from 'src/explore/components/optionRenderers';
import {
@@ -334,8 +334,9 @@ const ColumnSelectPopover = ({
/>
</FormItem>
) : datasourceType === DatasourceType.Table ? (
- <EmptyStateSmall
+ <EmptyState
image="empty.svg"
+ size="small"
title={
isTemporal
? t('No temporal columns found')
@@ -352,8 +353,9 @@ const ColumnSelectPopover = ({
}
/>
) : (
- <EmptyStateSmall
+ <EmptyState
image="empty.svg"
+ size="small"
title={
isTemporal
? t('No temporal columns found')
@@ -393,8 +395,9 @@ const ColumnSelectPopover = ({
disabled={disabledTabs.has('simple')}
>
{isTemporal && simpleColumns.length === 0 ? (
- <EmptyStateSmall
+ <EmptyState
image="empty.svg"
+ size="small"
title={t('No temporal columns found')}
description={
datasourceType === DatasourceType.Table ? (
diff --git
a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
index c527aa5825..188487b338 100644
---
a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
+++
b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx
@@ -30,7 +30,7 @@ import Tabs from 'src/components/Tabs';
import Button from 'src/components/Button';
import { Select } from 'src/components';
import { Tooltip } from 'src/components/Tooltip';
-import { EmptyStateSmall } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { Form, FormItem } from 'src/components/Form';
import { SQLEditor } from 'src/components/AsyncAceEditor';
import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
@@ -389,16 +389,18 @@ export default class AdhocMetricEditPopover extends
PureComponent {
/>
</FormItem>
) : datasource.type === DatasourceType.Table ? (
- <EmptyStateSmall
+ <EmptyState
image="empty.svg"
+ size="small"
title={t('No saved metrics found')}
description={t(
'Add metrics to dataset in "Edit datasource" modal',
)}
/>
) : (
- <EmptyStateSmall
+ <EmptyState
image="empty.svg"
+ size="small"
title={t('No saved metrics found')}
description={
<>
diff --git a/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
b/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
index 8ecbf33872..ae51762d78 100644
--- a/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
+++ b/superset-frontend/src/features/allEntities/AllEntitiesTable.tsx
@@ -23,7 +23,7 @@ import { TagsList } from 'src/components/Tags';
import FacePile from 'src/components/FacePile';
import Tag from 'src/types/TagType';
import Owner from 'src/types/Owner';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { NumberParam, useQueryParam } from 'use-query-params';
const MAX_TAGS_TO_SHOW = 3;
@@ -160,8 +160,9 @@ export default function AllEntitiesTable({
{renderTable('query')}
</>
) : (
- <EmptyStateBig
+ <EmptyState
image="dashboard.svg"
+ size="large"
title={t('No entities have this tag currently assigned')}
buttonAction={() => setShowTagModal(true)}
buttonText={t('Add tag to entities')}
diff --git
a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
index 9f57935947..5aab720272 100644
---
a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
+++
b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/MessageContent.tsx
@@ -18,7 +18,7 @@
*/
import { t, styled } from '@superset-ui/core';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { Link } from 'react-router-dom';
const StyledContainer = styled.div`
@@ -31,7 +31,7 @@ const StyledContainer = styled.div`
height: 100%;
`;
-const StyledEmptyStateBig = styled(EmptyStateBig)`
+const StyledEmptyState = styled(EmptyState)`
max-width: 50%;
p {
@@ -91,8 +91,9 @@ export const MessageContent = (props: MessageContentProps) =>
{
}
return (
<StyledContainer>
- <StyledEmptyStateBig
+ <StyledEmptyState
image={currentImage}
+ size="large"
title={currentTitle}
description={currentDescription}
/>
diff --git
a/superset-frontend/src/features/datasets/AddDataset/EditDataset/UsageTab/index.tsx
b/superset-frontend/src/features/datasets/AddDataset/EditDataset/UsageTab/index.tsx
index 0e41182096..289a67f5aa 100644
---
a/superset-frontend/src/features/datasets/AddDataset/EditDataset/UsageTab/index.tsx
+++
b/superset-frontend/src/features/datasets/AddDataset/EditDataset/UsageTab/index.tsx
@@ -32,7 +32,7 @@ import Table, {
TableSize,
OnChangeFunction,
} from 'src/components/Table';
-import { EmptyStateBig } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import ChartImage from 'src/assets/images/chart.svg';
import Icons from 'src/components/Icons';
import { useToasts } from 'src/components/MessageToasts/withToasts';
@@ -147,7 +147,7 @@ const emptyStateButtonText = (
</>
);
-const StyledEmptyStateBig = styled(EmptyStateBig)`
+const StyledEmptyState = styled(EmptyState)`
margin: ${({ theme }) => 13 * theme.gridUnit}px 0;
`;
@@ -250,8 +250,9 @@ const DatasetUsage = ({ datasetId }: DatasetUsageProps) => {
onChange={onChange}
/>
{!data.length && !loading ? (
- <StyledEmptyStateBig
+ <StyledEmptyState
image={<ChartImage />}
+ size="large"
title={t('No charts')}
description={t('This dataset is not used to power any charts.')}
buttonText={emptyStateButtonText}
diff --git
a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
index c14109aede..adb5223422 100644
--- a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/index.tsx
@@ -20,7 +20,7 @@ import { useEffect, SetStateAction, Dispatch, useCallback }
from 'react';
import { styled, t } from '@superset-ui/core';
import TableSelector, { TableOption } from 'src/components/TableSelector';
import { DatabaseObject } from 'src/components/DatabaseSelector';
-import { emptyStateComponent } from 'src/components/EmptyState';
+import { EmptyState } from 'src/components/EmptyState';
import { useToasts } from 'src/components/MessageToasts/withToasts';
import { LocalStorageKeys, getItem } from 'src/utils/localStorageHelpers';
import {
@@ -178,13 +178,30 @@ export default function LeftPanel({
),
[datasetNames],
);
+ const getDatabaseEmptyState = (emptyResultsWithSearch: boolean) => (
+ <EmptyState
+ image="empty.svg"
+ title={
+ emptyResultsWithSearch
+ ? t('No databases match your search')
+ : t('No databases available')
+ }
+ description={
+ <span>
+ {t('Manage your databases')}{' '}
+ <a href="/databaseview/list">{t('here')}</a>
+ </span>
+ }
+ size="small"
+ />
+ );
return (
<LeftPanelStyle>
<TableSelector
database={dataset?.db}
handleError={addDangerToast}
- emptyState={emptyStateComponent(false)}
+ emptyState={getDatabaseEmptyState(false)}
onDbChange={setDatabase}
onCatalogChange={setCatalog}
onSchemaChange={setSchema}
diff --git a/superset-frontend/src/features/home/ActivityTable.test.tsx
b/superset-frontend/src/features/home/ActivityTable.test.tsx
index 54fe3c7542..9b5ee127b7 100644
--- a/superset-frontend/src/features/home/ActivityTable.test.tsx
+++ b/superset-frontend/src/features/home/ActivityTable.test.tsx
@@ -137,9 +137,5 @@ test('calls the getEdited batch call when edited tab is
clicked', async () => {
});
test('show empty state if there is no data', () => {
renderActivityTable(emptyActivityProps);
- expect(
- screen.getByText(
- /recently created charts, dashboards, and saved queries will appear
here/i,
- ),
- ).toBeInTheDocument();
+ expect(screen.getByText(/nothing here yet/i)).toBeInTheDocument();
});
diff --git a/superset-frontend/src/features/home/ChartTable.test.tsx
b/superset-frontend/src/features/home/ChartTable.test.tsx
index f03a705245..0e19f8c059 100644
--- a/superset-frontend/src/features/home/ChartTable.test.tsx
+++ b/superset-frontend/src/features/home/ChartTable.test.tsx
@@ -87,9 +87,7 @@ const renderChartTable = (props: any) =>
test('renders with EmptyState if no data present', async () => {
await renderChartTable(mockedProps);
expect(screen.getAllByRole('tab')).toHaveLength(3);
- expect(
- screen.getByText(/other charts will appear here/i),
- ).toBeInTheDocument();
+ expect(screen.getByText(/nothing here yet/i)).toBeInTheDocument();
});
test('fetches chart favorites and renders chart cards', async () => {
diff --git a/superset-frontend/src/features/home/EmptyState.test.tsx
b/superset-frontend/src/features/home/EmptyState.test.tsx
index fe633bc788..0be86559dc 100644
--- a/superset-frontend/src/features/home/EmptyState.test.tsx
+++ b/superset-frontend/src/features/home/EmptyState.test.tsx
@@ -70,15 +70,7 @@ describe('EmptyState', () => {
// Select the first description node
const textContainer = wrapper.find('.ant-empty-description').at(0);
- expect(textContainer.text()).toEqual(
- variant.tab === TableTab.Favorite
- ? "You don't have any favorites yet!"
- : `No ${
- variant.tableName === WelcomeTable.SavedQueries
- ? 'saved queries'
- : variant.tableName.toLowerCase()
- } yet`,
- );
+ expect(textContainer.text()).toEqual('Nothing here yet');
expect(wrapper.find('button')).toHaveLength(1);
});
});
@@ -95,9 +87,7 @@ describe('EmptyState', () => {
expect(wrapper.find('.ant-empty-image').children()).toHaveLength(1);
// Check the correct text is displayed
- expect(textContainer.text()).toContain(
- `Recently ${recent.tab?.toLowerCase()} charts, dashboards, and saved
queries will appear here`,
- );
+ expect(textContainer.text()).toContain(`Nothing here yet`);
});
});
});
diff --git a/superset-frontend/src/features/home/EmptyState.tsx
b/superset-frontend/src/features/home/EmptyState.tsx
index cbcede55ec..7a2ba4d62b 100644
--- a/superset-frontend/src/features/home/EmptyState.tsx
+++ b/superset-frontend/src/features/home/EmptyState.tsx
@@ -16,172 +16,96 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Link } from 'react-router-dom';
import Button from 'src/components/Button';
-import { Empty } from 'src/components/EmptyState/Empty';
+import { EmptyState as EmptyStateComponent } from 'src/components/EmptyState';
import { TableTab } from 'src/views/CRUD/types';
import { styled, t } from '@superset-ui/core';
import { WelcomeTable } from './types';
-const welcomeTableLabels: Record<WelcomeTable, string> = {
- [WelcomeTable.Charts]: t('charts'),
- [WelcomeTable.Dashboards]: t('dashboards'),
- [WelcomeTable.Recents]: t('recents'),
- [WelcomeTable.SavedQueries]: t('saved queries'),
-};
-
-const welcomeTableEmpty: Record<WelcomeTable, string> = {
- [WelcomeTable.Charts]: t('No charts yet'),
- [WelcomeTable.Dashboards]: t('No dashboards yet'),
- [WelcomeTable.Recents]: t('No recents yet'),
- [WelcomeTable.SavedQueries]: t('No saved queries yet'),
-};
-
-const welcomeTableWillAppear: Record<WelcomeTable, (other: string) => string> =
- {
- [WelcomeTable.Charts]: (other: string) =>
- t('%(other)s charts will appear here', { other }),
- [WelcomeTable.Dashboards]: (other: string) =>
- t('%(other)s dashboards will appear here', { other }),
- [WelcomeTable.Recents]: (other: string) =>
- t('%(other)s recents will appear here', { other }),
- [WelcomeTable.SavedQueries]: (other: string) =>
- t('%(other)s saved queries will appear here', { other }),
- };
-
-export interface EmptyStateProps {
- tableName: WelcomeTable;
- tab?: string;
- otherTabTitle?: string;
-}
const EmptyContainer = styled.div`
min-height: 200px;
display: flex;
+ color: ${({ theme }) => theme.colors.grayscale.light2};
flex-direction: column;
justify-content: space-around;
`;
-const ButtonContainer = styled.div`
- Button {
- svg {
- color: ${({ theme }) => theme.colors.grayscale.light5};
- }
- }
-`;
-type Redirects = Record<
- WelcomeTable.Charts | WelcomeTable.Dashboards | WelcomeTable.SavedQueries,
- string
->;
+const ICONS = {
+ [WelcomeTable.Charts]: 'empty-charts.svg',
+ [WelcomeTable.Dashboards]: 'empty-dashboard.svg',
+ [WelcomeTable.Recents]: 'union.svg',
+ [WelcomeTable.SavedQueries]: 'empty-queries.svg',
+} as const;
-export default function EmptyState({
- tableName,
- tab,
- otherTabTitle,
-}: EmptyStateProps) {
- const mineRedirects: Redirects = {
+const REDIRECTS = {
+ create: {
[WelcomeTable.Charts]: '/chart/add',
[WelcomeTable.Dashboards]: '/dashboard/new',
[WelcomeTable.SavedQueries]: '/sqllab?new=true',
- };
- const favRedirects: Redirects = {
+ },
+ viewAll: {
[WelcomeTable.Charts]: '/chart/list',
[WelcomeTable.Dashboards]: '/dashboard/list/',
[WelcomeTable.SavedQueries]: '/savedqueryview/list/',
- };
- const tableIcon: Record<WelcomeTable, string> = {
- [WelcomeTable.Charts]: 'empty-charts.svg',
- [WelcomeTable.Dashboards]: 'empty-dashboard.svg',
- [WelcomeTable.Recents]: 'union.svg',
- [WelcomeTable.SavedQueries]: 'empty-queries.svg',
- };
- const mine = <span>{welcomeTableEmpty[tableName]}</span>;
- const recent = (
- <span className="no-recents">
- {(() => {
- if (tab === TableTab.Viewed) {
- return t(
- `Recently viewed charts, dashboards, and saved queries will appear
here`,
- );
- }
- if (tab === TableTab.Created) {
- return t(
- 'Recently created charts, dashboards, and saved queries will
appear here',
- );
- }
- if (tab === TableTab.Other) {
- const other = otherTabTitle || t('Other');
- return welcomeTableWillAppear[tableName](other);
- }
- if (tab === TableTab.Edited) {
- return t(
- `Recently edited charts, dashboards, and saved queries will appear
here`,
- );
- }
- return null;
- })()}
- </span>
- );
+ },
+} as const;
+
+export interface EmptyStateProps {
+ tableName: WelcomeTable;
+ tab?: string;
+ otherTabTitle?: string;
+}
+
+export default function EmptyState({
+ tableName,
+ tab,
+ otherTabTitle,
+}: EmptyStateProps) {
+ const getActionButton = () => {
+ if (tableName === WelcomeTable.Recents) {
+ return null;
+ }
+
+ const isFavorite = tab === TableTab.Favorite;
+ const buttonText =
+ tableName === WelcomeTable.SavedQueries
+ ? isFavorite
+ ? t('SQL Lab queries')
+ : t('SQL query')
+ : isFavorite
+ ? t(tableName.toLowerCase())
+ : tableName.slice(0, -1);
+
+ const url = isFavorite
+ ? REDIRECTS.viewAll[tableName]
+ : REDIRECTS.create[tableName];
- // Mine and Recent Activity(all tabs) tab empty state
- if (
- tab === TableTab.Mine ||
- tableName === WelcomeTable.Recents ||
- tab === TableTab.Other
- ) {
return (
- <EmptyContainer>
- <Empty
- image={`/static/assets/images/${tableIcon[tableName]}`}
- description={
- tableName === WelcomeTable.Recents || tab === TableTab.Other
- ? recent
- : mine
- }
- >
- {tableName !== WelcomeTable.Recents && (
- <ButtonContainer>
- <Link to={mineRedirects[tableName]}>
- <Button buttonStyle="primary">
- <i className="fa fa-plus" />
- {tableName === WelcomeTable.SavedQueries
- ? t('SQL query')
- : tableName
- .split('')
- .slice(0, tableName.length - 1)
- .join('')}
- </Button>
- </Link>
- </ButtonContainer>
- )}
- </Empty>
- </EmptyContainer>
+ <Button
+ buttonStyle="default"
+ onClick={() => {
+ window.location.href = url;
+ }}
+ >
+ {isFavorite
+ ? t('See all %(tableName)s', { tableName: buttonText })
+ : buttonText}
+ </Button>
);
- }
- // Favorite tab empty state
+ };
+
+ const image =
+ tab === TableTab.Favorite ? 'star-circle.svg' : ICONS[tableName];
+
return (
<EmptyContainer>
- <Empty
- image="/static/assets/images/star-circle.svg"
- description={
- <span className="no-favorites">
- {t("You don't have any favorites yet!")}
- </span>
- }
+ <EmptyStateComponent
+ image={image}
+ size="large"
+ description={t('Nothing here yet')}
>
- <Button
- buttonStyle="primary"
- onClick={() => {
- window.location.href = favRedirects[tableName];
- }}
- >
- {t('See all %(tableName)s', {
- tableName:
- tableName === WelcomeTable.SavedQueries
- ? t('SQL Lab queries')
- : welcomeTableLabels[tableName],
- })}
- </Button>
- </Empty>
+ {getActionButton()}
+ </EmptyStateComponent>
</EmptyContainer>
);
}