Skip to content

Commit

Permalink
feat: Implement EmptyState components (#18676)
Browse files Browse the repository at this point in the history
* feat: Implement EmptyState components

* Fix title in storybook

* Add licenses

* Add max widths
  • Loading branch information
kgabryje authored Feb 11, 2022
1 parent e44163e commit a7d505d
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 1 deletion.
22 changes: 22 additions & 0 deletions superset-frontend/src/assets/images/chart.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions superset-frontend/src/assets/images/document.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions superset-frontend/src/assets/images/filter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions superset-frontend/src/components/EmptyState/EmptyState.stories.tsx
Original file line number Diff line number Diff line change
@@ -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 = () => (
<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"
/>
);

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 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={() => {}}
/>
);
205 changes: 205 additions & 0 deletions superset-frontend/src/components/EmptyState/index.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLElement>;
}

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) => (
<Empty
description={false}
image={getImage(image)}
imageStyle={getImageHeight(size)}
/>
);

export const EmptyStateBig = ({
title,
image,
description,
buttonAction,
buttonText,
}: EmptyStateProps) => (
<EmptyStateContainer>
<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}>
{buttonText}
</ActionButton>
)}
</TextContainer>
</EmptyStateContainer>
);

export const EmptyStateMedium = ({
title,
image,
description,
buttonAction,
buttonText,
}: EmptyStateProps) => (
<EmptyStateContainer>
<ImageContainer image={image} size={EmptyStateSize.Medium} />
<TextContainer
css={(theme: SupersetTheme) =>
css`
max-width: ${theme.gridUnit * 100}px;
`
}
>
<Title>{title}</Title>
{description && <Description>{description}</Description>}
{buttonText && buttonAction && (
<ActionButton buttonStyle="primary" onClick={buttonAction}>
{buttonText}
</ActionButton>
)}
</TextContainer>
</EmptyStateContainer>
);

export const EmptyStateSmall = ({
title,
image,
description,
}: EmptyStateSmallProps) => (
<EmptyStateContainer>
<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>
</EmptyStateContainer>
);
13 changes: 12 additions & 1 deletion superset-frontend/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)$/,
Expand Down

0 comments on commit a7d505d

Please sign in to comment.