Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: empty: adds empty state component #180

Merged
merged 2 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
505 changes: 502 additions & 3 deletions src/__snapshots__/storybook.test.js.snap

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions src/components/Empty/Empty.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
import { Stories } from '@storybook/addon-docs';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Empty, EmptyMode } from './';
import { Icon, IconName } from '../Icon';

export default {
title: 'Empty',
parameters: {
docs: {
page: (): JSX.Element => (
<main>
<article>
<section>
<h1>Empty</h1>
<p>An Empty state placeholder.</p>
</section>
<section>
<Stories includePrimary title="" />
</section>
</article>
</main>
),
},
},
argTypes: {
mode: {
options: [
EmptyMode.data,
EmptyMode.error,
EmptyMode.messages,
EmptyMode.search,
EmptyMode.tasks,
],
control: { type: 'select' },
},
},
} as ComponentMeta<typeof Empty>;

const Empty_Story: ComponentStory<typeof Empty> = (args) => <Empty {...args} />;

export const No_Data = Empty_Story.bind({});
export const Error_State = Empty_Story.bind({});
export const Empty_Messages = Empty_Story.bind({});
export const No_Search_Results = Empty_Story.bind({});
export const Tasks_Complete = Empty_Story.bind({});
export const Custom_Image = Empty_Story.bind({});

const emptyArgs: Object = {
description: 'More detail on how might the user be able to get around this',
mode: EmptyMode.data,
style: {},
title: 'Short Message Here',
};

No_Data.args = {
...emptyArgs,
};

Error_State.args = {
...emptyArgs,
mode: EmptyMode.error,
};

Empty_Messages.args = {
...emptyArgs,
mode: EmptyMode.messages,
};

No_Search_Results.args = {
...emptyArgs,
mode: EmptyMode.search,
};

Tasks_Complete.args = {
...emptyArgs,
mode: EmptyMode.tasks,
};

Custom_Image.args = {
...emptyArgs,
image: (
<Icon color="#8ED0FA" path={IconName.mdiWrenchOutline} size="200px" />
jhoward-eightfold marked this conversation as resolved.
Show resolved Hide resolved
),
imageStyle: {},
mode: '',
};
16 changes: 16 additions & 0 deletions src/components/Empty/Empty.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import Enzyme, { mount } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import { Empty } from './';

Enzyme.configure({ adapter: new Adapter() });

describe('Empty', () => {
/*
* Functionality Tests
*/
test('empty renders', () => {
const wrapper = mount(<Empty description="Test" />);
expect(wrapper.containsMatchingElement(<Empty />)).toEqual(true);
});
jhoward-eightfold marked this conversation as resolved.
Show resolved Hide resolved
});
88 changes: 88 additions & 0 deletions src/components/Empty/Empty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { FC, Ref } from 'react';
import { mergeClasses } from '../../shared/utilities';
import { EmptyMode, EmptyProps } from './Empty.types';
import { DefaultEmptyDataImg } from './SVG/DefaultEmptyDataImg';
import { DefaultEmptyMessagesImg } from './SVG/DefaultEmptyMessagesImg';
import { DefaultEmptySearchImg } from './SVG/DefaultEmptySearchImg';
import { DefaultServerErrorImg } from './SVG/DefaultServerErrorImg';
import { DefaultTasksCompleteImg } from './SVG/DefaultTasksCompleteImg';
import { useCanvasDirection } from '../../hooks/useCanvasDirection';

import styles from './empty.module.scss';

const defaultEmptyDataImg = <DefaultEmptyDataImg />;
const defaultEmptyMessagesImg = <DefaultEmptyMessagesImg />;
const defaultEmptySearchImg = <DefaultEmptySearchImg />;
const defaultServerErrorImg = <DefaultServerErrorImg />;
const defaultTasksCompleteImg = <DefaultTasksCompleteImg />;

export const Empty: FC<EmptyProps> = React.forwardRef(
(
{
children,
classNames,
description,
image,
imageStyle,
mode = EmptyMode.data,
title,
...rest
},
ref: Ref<HTMLDivElement>
) => {
const htmlDir: string = useCanvasDirection();

const getDefaultImage = (mode: EmptyMode): JSX.Element => {
switch (mode) {
case EmptyMode.data:
return defaultEmptyDataImg;
case EmptyMode.error:
return defaultServerErrorImg;
case EmptyMode.messages:
return defaultEmptyMessagesImg;
case EmptyMode.search:
return defaultEmptySearchImg;
case EmptyMode.tasks:
return defaultTasksCompleteImg;
default:
return defaultEmptyDataImg;
}
};

let imageNode: React.ReactNode = null;

if (image) {
if (typeof image === 'string') {
imageNode = <img alt={description} src={image} />;
} else {
imageNode = image;
}
} else {
imageNode = getDefaultImage(mode);
}

return (
<div
{...rest}
ref={ref}
className={mergeClasses([
styles.empty,
{ [styles.emptyDefault]: image === getDefaultImage(mode) },
{ [styles.emptyRtl]: htmlDir === 'rtl' },
classNames,
])}
>
<div className={styles.emptyImage} style={imageStyle}>
{imageNode}
</div>
{title && <div className={styles.emptyTitle}>{title}</div>}
{description && (
<div className={styles.emptyDescription}>{description}</div>
)}
{children && (
<div className={styles.emptyFooter}>{children}</div>
)}
</div>
);
}
);
40 changes: 40 additions & 0 deletions src/components/Empty/Empty.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { OcBaseProps } from '../OcBase';

export enum EmptyMode {
data = 'data',
jhoward-eightfold marked this conversation as resolved.
Show resolved Hide resolved
messages = 'messages',
tasks = 'tasks',
search = 'search',
error = 'error',
}

export interface EmptyProps extends OcBaseProps<HTMLDivElement> {
/**
* The empty component children.
*/
children?: React.ReactNode;
/**
* The empty component description
*/
description?: string;
/**
* The empty component mode
*/
mode?: EmptyMode;
/**
* The empty component image.
*/
image?: React.ReactNode;
/**
* The empty component image style.
*/
imageStyle?: React.CSSProperties;
/**
* The empty component style.
*/
style?: React.CSSProperties;
/**
* The empty component title
*/
title?: string;
}
56 changes: 56 additions & 0 deletions src/components/Empty/SVG/DefaultEmptyDataImg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';

export const DefaultEmptyDataImg = (): JSX.Element => {
return (
<svg
className={'empty-data-image-default'}
width="200"
height="184"
viewBox="0 0 200 184"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M39.0718 177.839C21.8078 171.861 3.66074 162.325 0.510726 150.175C-2.63929 138.025 9.18787 123.359 24.8068 115.637C40.5358 108.029 59.9069 107.449 71.9662 102.257C83.9156 96.9498 88.5731 86.9309 99.9189 77.9985C111.265 69.066 129.299 61.2199 140.372 64.9517C151.555 68.798 155.648 84.2069 164.716 96.6426C173.785 109.078 187.68 118.625 194.904 131.657C202.237 144.804 202.768 161.422 189.061 166.219C175.355 171.016 147.3 163.877 128.36 165.625C109.569 167.288 99.7624 177.823 86.4541 181.955C73.2954 186.004 56.4854 183.734 39.0718 177.839Z"
fill="#EBF7FF"
/>
<path d="M26 108L38.5 59H160.5L174 108V154H26V108Z" fill="white" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M118.495 108.258C118.495 117.504 110.214 123.333 99.9993 123.333C89.7843 123.333 81.5034 117.504 81.5034 108.258C81.5034 107.958 81.5121 105.994 81.5293 105.698H46.666L60.2683 72.6258C60.8547 71.0563 62.4813 70 64.3116 70H135.687C137.517 70 139.144 71.0563 139.73 72.6258L153.333 105.698H118.469C118.487 105.994 118.495 107.958 118.495 108.258Z"
fill="#B0F3FE"
jhoward-eightfold marked this conversation as resolved.
Show resolved Hide resolved
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M152.505 59.9999L47.4938 60.0006C43.6128 60.0006 40.0926 62.6542 38.7909 66.7166L27.5922 101.666L74.0905 101.666C78.722 101.666 83.0934 103.873 86.0399 107.654L92.9658 116.542C96.6469 121.266 103.355 121.266 107.037 116.542L113.962 107.654C116.909 103.873 121.28 101.666 125.912 101.666L172.408 101.666L161.208 66.7158C159.906 62.6534 156.386 59.9999 152.505 59.9999ZM173.651 108.333L125.912 108.333C123.217 108.333 120.638 109.615 118.876 111.876L111.95 120.763C105.729 128.747 94.2733 128.747 88.0518 120.763L81.126 111.876C79.3647 109.615 76.7854 108.333 74.0906 108.333L26.3492 108.333L26.3493 143.333C26.3493 148.934 30.548 153.333 35.5634 153.333L164.436 153.333C169.452 153.333 173.65 148.934 173.651 143.333L173.651 108.333ZM47.4937 53.3339L152.505 53.3333C159.195 53.3332 165.081 57.8972 167.226 64.5898L180 104.454L180 143.333C180 152.46 173.105 159.999 164.436 159.999L35.5635 160C26.8946 160 20.0001 152.46 20 143.333L20 104.454L32.7731 64.5908C34.9176 57.898 40.804 53.334 47.4937 53.3339Z"
fill="#8ED0FA"
/>
<rect
x="93.334"
width="13.3333"
height="40"
rx="6.66667"
fill="#B0F3FE"
/>
<rect
x="46.666"
y="6.66675"
width="13.3333"
height="40"
rx="6.66667"
transform="rotate(-30 46.666 6.66675)"
fill="#B0F3FE"
/>
<rect
x="141.666"
width="13.3333"
height="40"
rx="6.66667"
transform="rotate(30 141.666 0)"
fill="#B0F3FE"
/>
</svg>
);
};
43 changes: 43 additions & 0 deletions src/components/Empty/SVG/DefaultEmptyMessagesImg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

export const DefaultEmptyMessagesImg = (): JSX.Element => {
return (
<svg
className={'empty-messages-image-default'}
width="200"
height="184"
viewBox="0 0 200 184"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M39.0718 177.839C21.8078 171.861 3.66074 162.325 0.510726 150.175C-2.63929 138.025 9.18787 123.359 24.8068 115.637C40.5358 108.029 59.9069 107.449 71.9662 102.257C83.9156 96.9498 88.5731 86.9309 99.9189 77.9985C111.265 69.066 129.299 61.2199 140.372 64.9517C151.555 68.798 155.648 84.2069 164.716 96.6426C173.785 109.078 187.68 118.625 194.904 131.657C202.237 144.804 202.768 161.422 189.061 166.219C175.355 171.016 147.3 163.877 128.36 165.625C109.569 167.288 99.7624 177.823 86.4541 181.955C73.2954 186.004 56.4854 183.734 39.0718 177.839Z"
fill="#EBF7FF"
/>
<path
d="M131.098 145.069C125.349 140.58 119.665 136.448 116.52 133.384C116.016 132.893 115.288 132.671 114.598 132.81C35.5718 148.779 -9.97428 65.6821 65.669 27.1035C65.7558 27.0592 65.8525 27.0185 65.9449 26.9875C142.983 1.22162 204.241 81.5443 135.362 124.844C134.772 125.214 134.381 125.876 134.381 126.572L134.381 143.483C134.381 145.166 132.424 146.105 131.098 145.069Z"
fill="white"
stroke="#8ED0FA"
stroke-width="6"
/>
<path
d="M66.3926 86.6624C70.9586 86.6624 74.3926 82.703 74.3926 78.1624C74.3926 73.6217 70.9586 69.6624 66.3926 69.6624C61.8266 69.6624 58.3926 73.6217 58.3926 78.1624C58.3926 82.703 61.8266 86.6624 66.3926 86.6624Z"
fill="#B0F3FE"
stroke="#B0F3FE"
stroke-width="4"
/>
<path
d="M94.3926 86.6624C98.9586 86.6624 102.393 82.703 102.393 78.1624C102.393 73.6217 98.9586 69.6624 94.3926 69.6624C89.8266 69.6624 86.3926 73.6217 86.3926 78.1624C86.3926 82.703 89.8266 86.6624 94.3926 86.6624Z"
fill="#B0F3FE"
stroke="#B0F3FE"
stroke-width="4"
/>
<path
d="M122.393 86.6624C126.959 86.6624 130.393 82.703 130.393 78.1624C130.393 73.6217 126.959 69.6624 122.393 69.6624C117.827 69.6624 114.393 73.6217 114.393 78.1624C114.393 82.703 117.827 86.6624 122.393 86.6624Z"
fill="#B0F3FE"
stroke="#B0F3FE"
stroke-width="4"
/>
</svg>
);
};
47 changes: 47 additions & 0 deletions src/components/Empty/SVG/DefaultEmptySearchImg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';

export const DefaultEmptySearchImg = (): JSX.Element => {
return (
<svg
className={'empty-search-image-default'}
width="200"
height="184"
viewBox="0 0 200 184"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M39.0718 177.839C21.8078 171.861 3.66074 162.325 0.510726 150.175C-2.63929 138.025 9.18787 123.359 24.8068 115.637C40.5358 108.029 59.9069 107.449 71.9662 102.257C83.9156 96.9498 88.5731 86.9309 99.9189 77.9985C111.265 69.066 129.299 61.2199 140.372 64.9517C151.555 68.798 155.648 84.2069 164.716 96.6426C173.785 109.078 187.68 118.625 194.904 131.657C202.237 144.804 202.768 161.422 189.061 166.219C175.355 171.016 147.3 163.877 128.36 165.625C109.569 167.288 99.7624 177.823 86.4541 181.955C73.2954 186.004 56.4854 183.734 39.0718 177.839Z"
fill="#EBF7FF"
/>
<path
d="M183 91C188.192 91 192 86.5161 192 81.5C192 76.4839 188.192 72 183 72C177.808 72 174 76.4839 174 81.5C174 86.5161 177.808 91 183 91Z"
stroke="#B0F3FE"
stroke-width="6"
/>
<circle cx="139" cy="47" r="5" fill="#B0F3FE" />
<rect x="148" y="42" width="20" height="10" rx="5" fill="#B0F3FE" />
<rect x="10" y="83" width="30" height="10" rx="5" fill="#B0F3FE" />
<path
d="M111.824 36.9412C130.569 55.6864 130.569 86.0783 111.824 104.823C93.0788 123.569 62.6869 123.569 43.9417 104.823C25.1965 86.0783 25.1965 55.6864 43.9417 36.9412C62.6869 18.196 93.0788 18.196 111.824 36.9412Z"
fill="white"
/>
<path
d="M127.38 131.693C124.256 128.569 124.256 123.504 127.38 120.38C130.504 117.256 135.57 117.256 138.694 120.38L155.664 137.35C158.789 140.475 158.789 145.54 155.664 148.664C152.54 151.788 147.475 151.788 144.351 148.664L127.38 131.693Z"
fill="white"
/>
<path
d="M125.26 118.259L118.189 111.187M111.824 36.9412C130.569 55.6864 130.569 86.0783 111.824 104.823C93.0788 123.569 62.6869 123.569 43.9417 104.823C25.1965 86.0783 25.1965 55.6864 43.9417 36.9412C62.6869 18.196 93.0788 18.196 111.824 36.9412ZM155.664 148.664C152.54 151.788 147.475 151.788 144.351 148.664L127.38 131.693C124.256 128.569 124.256 123.504 127.38 120.38C130.504 117.256 135.57 117.256 138.694 120.38L155.664 137.35C158.789 140.475 158.789 145.54 155.664 148.664Z"
stroke="#8ED0FA"
stroke-width="6"
strokeLinecap="round"
/>
<path
d="M56.3926 96.6624C60.9586 96.6624 64.3926 92.703 64.3926 88.1624C64.3926 83.6217 60.9586 79.6624 56.3926 79.6624C51.8266 79.6624 48.3926 83.6217 48.3926 88.1624C48.3926 92.703 51.8266 96.6624 56.3926 96.6624Z"
fill="#B0F3FE"
stroke="#B0F3FE"
stroke-width="4"
/>
</svg>
);
};
Loading