diff --git a/src/components/empty-data/__tests__/__snapshots__/empty-data.test.tsx.snap b/src/components/empty-data/__tests__/__snapshots__/empty-data.test.tsx.snap new file mode 100644 index 00000000..0a137d98 --- /dev/null +++ b/src/components/empty-data/__tests__/__snapshots__/empty-data.test.tsx.snap @@ -0,0 +1,25 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`EmptyData > should render default component properly and match snapshot 1`] = ` + + + + Description + + + Secondary Description + + + Action Button Text + + + +`; diff --git a/src/components/empty-data/__tests__/empty-data.test.tsx b/src/components/empty-data/__tests__/empty-data.test.tsx new file mode 100644 index 00000000..a7517944 --- /dev/null +++ b/src/components/empty-data/__tests__/empty-data.test.tsx @@ -0,0 +1,15 @@ +import { render } from '@testing-library/react' +import { EmptyData } from '../empty-data' + +describe('EmptyData', () => { + it('should render default component properly and match snapshot', () => { + const { asFragment } = render( + + Description + Secondary Description + Action Button Text + , + ) + expect(asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/components/empty-data/empty-data.mdx b/src/components/empty-data/empty-data.mdx new file mode 100644 index 00000000..ac8a7aed --- /dev/null +++ b/src/components/empty-data/empty-data.mdx @@ -0,0 +1,32 @@ +import { Meta, Story, Canvas, Controls, Description } from '@storybook/blocks' +import { RenderHtmlMarkup } from '../../storybook/render-html-markup' +import * as EmptyDataStories from './empty-data.stories' + + + + +# Empty Data + + + + + +## Default + + + + + +## Description Only + + + + + + +## Action Only + + + + + diff --git a/src/components/empty-data/empty-data.stories.tsx b/src/components/empty-data/empty-data.stories.tsx new file mode 100644 index 00000000..300ef579 --- /dev/null +++ b/src/components/empty-data/empty-data.stories.tsx @@ -0,0 +1,50 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { EmptyData } from './empty-data' + +const meta: Meta = { + title: 'Components/Empty Data', +} + +export default meta +type Story = StoryObj + +/** + * The EmptyData component is designed to emphasize that current section of the page + * has empty data, but could possibly have different content when there is data available. + * The default variant renders a circle with children. + * + * There are three variants available for the EmptyData component: + * Default (full version), Description Only, and Action Only. + * See the examples below for more details of the usage. + */ +export const Default: Story = { + render() { + return ( + + No things found + Secondary text + Add Thing + + ) + }, +} + +export const DescriptionOnly: Story = { + render() { + return ( + + No things found + Secondary text + + ) + }, +} +export const ActionOnly: Story = { + render() { + return ( + + Action + + ) + }, +} diff --git a/src/components/empty-data/empty-data.tsx b/src/components/empty-data/empty-data.tsx new file mode 100644 index 00000000..dd12e21d --- /dev/null +++ b/src/components/empty-data/empty-data.tsx @@ -0,0 +1,17 @@ +import type { HTMLAttributes } from 'react' +import { ElEmptyData, ElEmptyDataActionButton, ElEmptyDataDescription, ElEmptyDataSecondaryDescription } from './styles' +import type React from 'react' + +type EmptyDataFC = React.FC> & { + Description: typeof ElEmptyDataDescription + SecondaryDescription: typeof ElEmptyDataSecondaryDescription + ActionButton: typeof ElEmptyDataActionButton +} + +const EmptyData: EmptyDataFC = ({ children, ...props }) => {children} + +EmptyData.Description = ElEmptyDataDescription +EmptyData.SecondaryDescription = ElEmptyDataSecondaryDescription +EmptyData.ActionButton = ElEmptyDataActionButton + +export { EmptyData } diff --git a/src/components/empty-data/index.tsx b/src/components/empty-data/index.tsx new file mode 100644 index 00000000..fb14312d --- /dev/null +++ b/src/components/empty-data/index.tsx @@ -0,0 +1,2 @@ +export * from './empty-data.js' +export * from './styles.js' diff --git a/src/components/empty-data/styles.ts b/src/components/empty-data/styles.ts new file mode 100644 index 00000000..87690e71 --- /dev/null +++ b/src/components/empty-data/styles.ts @@ -0,0 +1,50 @@ +import { styled } from '@linaria/react' + +export const ElEmptyDataDescription = styled.div` + color: var(text-primary); + font-size: var(--font-size-base); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-base); + letter-spacing: var(--letter-spacing-base); +` + +export const ElEmptyDataSecondaryDescription = styled.div` + color: var(--text-secondary); + font-size: var(--font-size-sm,); + font-weight: var(--font-weight-regular,); + line-height: var(--line-height-sm,); + letter-spacing: var(--letter-spacing-sm); +` + +export const ElEmptyDataActionButton = styled.button` + color: var(--text-action); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + line-height: var(--line-height-sm); + letter-spacing: var(--letter-spacing-sm); + + border: none; + background: none; + cursor: pointer; +` + +export const ElEmptyData = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + // TODO: ask design team to clarify + height: 186px; + padding: var(--spacing-6); + border-radius: var(--corner-lg); + background: var(--fill-default-lightest); + + ${ElEmptyDataSecondaryDescription} + ${ElEmptyDataActionButton} { + margin-top: var(--spacing-1); + } + + & * { + font-family: var(--font-family); + } +` diff --git a/src/index.ts b/src/index.ts index 7993408d..2a04cb4c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,6 +59,7 @@ export * from './components/avatar' export * from './components/skeleton' export * from './components/table/table-container' export * from './components/table/table-toolbar' +export * from './components/empty-data' export * from './hooks/use-portal' export * from './hooks/use-snack'