diff --git a/superset-frontend/src/assets/images/chart.svg b/superset-frontend/src/assets/images/chart.svg
new file mode 100644
index 0000000000000..2267342bccd1f
--- /dev/null
+++ b/superset-frontend/src/assets/images/chart.svg
@@ -0,0 +1,22 @@
+
+
diff --git a/superset-frontend/src/assets/images/document.svg b/superset-frontend/src/assets/images/document.svg
new file mode 100644
index 0000000000000..e3d1bfe1beb28
--- /dev/null
+++ b/superset-frontend/src/assets/images/document.svg
@@ -0,0 +1,22 @@
+
+
diff --git a/superset-frontend/src/assets/images/filter.svg b/superset-frontend/src/assets/images/filter.svg
new file mode 100644
index 0000000000000..0e1f6b41efc3d
--- /dev/null
+++ b/superset-frontend/src/assets/images/filter.svg
@@ -0,0 +1,32 @@
+
+
diff --git a/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx b/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
new file mode 100644
index 0000000000000..7872c4ec9266a
--- /dev/null
+++ b/superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * 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
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React from 'react';
+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 '.';
+
+export default {
+ title: 'Empty state',
+ component: EmptyStateMedium,
+};
+
+export const SmallEmptyState = () => (
+ }
+ title="Small empty state"
+ description="This is an example of a small empty state"
+ />
+);
+
+export const MediumEmptyState = () => (
+ }
+ title="Medium empty state"
+ description="This is an example of a medium empty state"
+ />
+);
+
+export const MediumEmptyStateWithButton = () => (
+ }
+ title="Medium empty state"
+ description="This is an example of a medium empty state with a button"
+ buttonAction={() => {}}
+ buttonText="Click!"
+ />
+);
+
+export const BigEmptyState = () => (
+ }
+ title="Big empty state"
+ description="This is an example of a big empty state"
+ />
+);
+
+export const BigEmptyStateWithButton = () => (
+ }
+ title="Big empty state"
+ description="This is an example of a big empty state with a button"
+ buttonText="Click!"
+ buttonAction={() => {}}
+ />
+);
diff --git a/superset-frontend/src/components/EmptyState/index.tsx b/superset-frontend/src/components/EmptyState/index.tsx
new file mode 100644
index 0000000000000..99a6ce7bc7160
--- /dev/null
+++ b/superset-frontend/src/components/EmptyState/index.tsx
@@ -0,0 +1,205 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * 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
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import React, { ReactNode } from 'react';
+import { styled, css, SupersetTheme } from '@superset-ui/core';
+import { Empty } from 'src/common/components';
+import Button from 'src/components/Button';
+
+export enum EmptyStateSize {
+ Small,
+ Medium,
+ Big,
+}
+
+export interface EmptyStateSmallProps {
+ title: string | ReactNode;
+ description?: string | ReactNode;
+ image: string | ReactNode;
+}
+
+export interface EmptyStateProps extends EmptyStateSmallProps {
+ buttonText?: string;
+ buttonAction?: React.MouseEventHandler;
+}
+
+export interface ImageContainerProps {
+ image: string | ReactNode;
+ size: EmptyStateSize;
+}
+
+const EmptyStateContainer = styled.div`
+ ${({ theme }) => css`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+ padding: ${theme.gridUnit * 4}px;
+ text-align: center;
+
+ & .ant-empty-image svg {
+ width: auto;
+ }
+ `}
+`;
+
+const TextContainer = styled.div``;
+
+const Title = styled.p`
+ ${({ theme }) => css`
+ font-size: ${theme.typography.sizes.m}px;
+ color: ${theme.colors.grayscale.light1};
+ margin: ${theme.gridUnit * 2}px 0 0 0;
+ 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;
+ 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;
+ `}
+`;
+
+const ActionButton = styled(Button)`
+ ${({ theme }) => css`
+ margin-top: ${theme.gridUnit * 4}px;
+ `}
+`;
+
+const getImage = (image: string | ReactNode) =>
+ typeof image === 'string' ? `/static/assets/images/${image}` : image;
+
+const getImageHeight = (size: EmptyStateSize) => {
+ switch (size) {
+ case EmptyStateSize.Small:
+ return { height: '50px' };
+ case EmptyStateSize.Medium:
+ return { height: '80px' };
+ case EmptyStateSize.Big:
+ return { height: '150px' };
+ default:
+ return { height: '50px' };
+ }
+};
+
+const ImageContainer = ({ image, size }: ImageContainerProps) => (
+
+);
+
+export const EmptyStateBig = ({
+ title,
+ image,
+ description,
+ buttonAction,
+ buttonText,
+}: EmptyStateProps) => (
+
+
+
+ css`
+ max-width: ${theme.gridUnit * 150}px;
+ `
+ }
+ >
+ {title}
+ {description && {description}}
+ {buttonAction && buttonText && (
+
+ {buttonText}
+
+ )}
+
+
+);
+
+export const EmptyStateMedium = ({
+ title,
+ image,
+ description,
+ buttonAction,
+ buttonText,
+}: EmptyStateProps) => (
+
+
+
+ css`
+ max-width: ${theme.gridUnit * 100}px;
+ `
+ }
+ >
+ {title}
+ {description && {description}}
+ {buttonText && buttonAction && (
+
+ {buttonText}
+
+ )}
+
+
+);
+
+export const EmptyStateSmall = ({
+ title,
+ image,
+ description,
+}: EmptyStateSmallProps) => (
+
+
+
+ css`
+ max-width: ${theme.gridUnit * 75}px;
+ `
+ }
+ >
+ {title}
+ {description && {description}}
+
+
+);
diff --git a/superset-frontend/webpack.config.js b/superset-frontend/webpack.config.js
index 06f6825fa9fd8..4c1aeff535578 100644
--- a/superset-frontend/webpack.config.js
+++ b/superset-frontend/webpack.config.js
@@ -404,7 +404,18 @@ const config = {
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
issuer: /\.([jt])sx?$/,
- use: ['@svgr/webpack'],
+ use: [
+ {
+ loader: '@svgr/webpack',
+ options: {
+ svgoConfig: {
+ plugins: {
+ removeViewBox: false,
+ },
+ },
+ },
+ },
+ ],
},
{
test: /\.(jpg|gif)$/,