Skip to content

Commit

Permalink
T79384 feat: create EmptyStateCard component (#1453)
Browse files Browse the repository at this point in the history
## πŸ“ Changes

- Creates `EmptyStateCard` component

## βœ… Checklist

Easy UI has certain UX standards that must be met. In general,
non-trivial changes should meet the following criteria:

- [x] Visuals match Design Specs in Figma
- [x] Stories accompany any component changes
- [x] Code is in accordance with our style guide
- [x] Design tokens are utilized
- [x] Unit tests accompany any component changes
- [x] TSDoc is written for any API surface area
- [x] Specs are up-to-date
- [x] Console is free from warnings
- [x] No accessibility violations are reported
- [x] Cross-browser check is performed (Chrome, Safari, Firefox)
- [x] Changeset is added

~Strikethrough~ any items that are not applicable to this pull request.
  • Loading branch information
OskiTheCoder authored Nov 6, 2024
1 parent e6c8040 commit 712edb9
Show file tree
Hide file tree
Showing 7 changed files with 659 additions and 38 deletions.
5 changes: 5 additions & 0 deletions .changeset/fair-mayflies-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@easypost/easy-ui": minor
---

feat: create EmptyStateCard component
143 changes: 105 additions & 38 deletions documentation/specs/EmptyStateCard.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,42 @@ An `EmptyStateCard` is a styled container with a header, body, and action sectio

## Design

An `EmptyStateCard` is a very simple compound component consisting of`EmptyStateCard.Header`, `EmptyStateCard.Body`, and `EmptyStateCard.Action`.
An `EmptyStateCard` is a very simple compound component consisting of`EmptyStateCard.Header`, `EmptyStateCard.HeaderText`, `EmptyStateCard.Body`, `EmptyStateCard.BodyText`, and `EmptyStateCard.Action`.

The `EmptyStateCard` will render `EmptyStateCard.Header`,` EmptyStateCard.Body`, and`EmptyStateCard.Action` via children, while delegating the styled container to the `Card` component.

The `EmptyStateCard.Header`,` EmptyStateCard.Body`, and`EmptyStateCard.Action` components will only be lightweight wrappers to handle the rendering for the content passed by consumers.
`EmptyStateCard.Header` will render `EmptyStateCard.HeaderText` and `EmptyStateCard.Body` will render `EmptyStateCard.BodyText`; both text components will handle the styles to avoid the consumer having to pass them in explicitly.

The `EmptyStateCard.HeaderText`,` EmptyStateCard.BodyText`, and`EmptyStateCard.Action` components will be lightweight wrappers to handle the rendering for the content passed by consumers.

No new external dependencies will be introduced.

### API

```ts
export type EmptyStateCardProps = Omit<
VerticalStackProps,
"children" | "as" | "reverseOrder"
> & {
export type EmptyStateCardProps = {
/**
* The children of the <EmptyStateCard> element. Should render
* `<EmptyStateCard.Header>`, `<EmptyStateCard.Body>`, and
* `<EmptyStateCard.Action>`
* `<EmptyStateCard.Action>` at minimum.
*/
children: ReactNode;
/**
* Gap between `<EmptyStateCard.Header>` and `<EmptyStateCard.Body>`
* block and `<EmptyStateCard.Action>`
* @default 2
*/
blockGap?: ResponsiveSpaceScale;
/**
* Gap between `<EmptyStateCard.Header>` and `<EmptyStateCard.Body>`
* @default 2
*/
textGap?: ResponsiveSpaceScale;
/**
* Content alignment
* @default start
*/
contentAlignment?: "start" | "center";
};

export type EmptyStateCardHeaderProps = {
Expand Down Expand Up @@ -70,26 +85,28 @@ import { Button } from "@easypost/easy-ui/Button";

function Component() {
return (
<EmptyStateCard>
<EmptyStateCard.Header>
<Text variant="heading5" color="neutral.000">
Analytics
</Text>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<Text variant="subtitle1" color="neutral.000">
Start shipping to get insights on your shipping costs and performance.
</Text>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Buy a label</Button>
</EmptyStateCard.Action>
</EmptyStateCard>,
<EmptyStateCard>
<EmptyStateCard.Header>
<EmptyStateCard.HeaderText>
Shipment Insurance
</EmptyStateCard.HeaderText>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<EmptyStateCard.BodyText>
Rest easy knowing if one of your customers orders is damaged, lost
in transit or stolen you are covered! Automatically add insurance to
all your shipments
</EmptyStateCard.BodyText>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Manage Insurance Settings</Button>
</EmptyStateCard.Action>
</EmptyStateCard>,
);
}
```

_Center aligned:_
_Gap:_

```tsx
import { EmptyStateCard } from "@easypost/easy-ui/EmptyStateCard";
Expand All @@ -98,21 +115,53 @@ import { Button } from "@easypost/easy-ui/Button";

function Component() {
return (
<EmptyStateCard inlineAlign="center">
<EmptyStateCard.Header>
<Text variant="heading5" color="neutral.000">
Analytics
</Text>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<Text variant="subtitle1" color="neutral.000">
Start shipping to get insights on your shipping costs and performance.
</Text>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Buy a label</Button>
</EmptyStateCard.Action>
</EmptyStateCard>,
<EmptyStateCard textGap="1">
<EmptyStateCard.Header>
<EmptyStateCard.HeaderText>
Shipment Insurance
</EmptyStateCard.HeaderText>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<EmptyStateCard.BodyText>
Rest easy knowing if one of your customers orders is damaged, lost
in transit or stolen you are covered! Automatically add insurance to
all your shipments
</EmptyStateCard.BodyText>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Manage Insurance Settings</Button>
</EmptyStateCard.Action>
</EmptyStateCard>,
);
}
```

_Alignment:_

```tsx
import { EmptyStateCard } from "@easypost/easy-ui/EmptyStateCard";
import { Text } from "@easypost/easy-ui/Text";
import { Button } from "@easypost/easy-ui/Button";

function Component() {
return (
<EmptyStateCard contentAlignment = "center">
<EmptyStateCard.Header>
<EmptyStateCard.HeaderText>
Shipment Insurance
</EmptyStateCard.HeaderText>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<EmptyStateCard.BodyText>
Rest easy knowing if one of your customers orders is damaged, lost
in transit or stolen you are covered! Automatically add insurance to
all your shipments
</EmptyStateCard.BodyText>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Manage Insurance Settings</Button>
</EmptyStateCard.Action>
</EmptyStateCard>,
);
}
```
Expand All @@ -121,6 +170,7 @@ function Component() {

```tsx
import { Card } from "../Card";
import { Text, TextProps } from "../Text";
import { VerticalStack } from "../VerticalStack";

export function EmptyStateCard(props: EmptyStateCardProps) {
Expand All @@ -139,12 +189,24 @@ function EmptyStateCardHeader(props: EmptyStateCardHeaderProps) {
return <div>{children}</div>;
}

function EmptyStateCardHeaderText(props: TextProps) {
const { ...textProps } = props;

return <Text {...textProps} />;
}

function EmptyStateCardBody(props: EmptyStateCardBodyProps) {
const { children } = props;

return <div>{children}</div>;
}

function EmptyStateCardBodyText(props: TextProps) {
const { ..textProps } = props;

return <Text {...restTextProps} />;
}

function EmptyStateCardAction(props: EmptyStateCardActionProps) {
const { children } = props;

Expand All @@ -153,8 +215,12 @@ function EmptyStateCardAction(props: EmptyStateCardActionProps) {

EmptyStateCard.Header = EmptyStateCardHeader;

EmptyStateCard.HeaderText = EmptyStateCardHeaderText;

EmptyStateCard.Body = EmptyStateCardBody;

EmptyStateCard.BodyText = EmptyStateCardBodyText;

EmptyStateCard.Action = EmptyStateCardAction;
```

Expand All @@ -170,3 +236,4 @@ There are no major accessibility concerns to highlight for this component

- `Card`
- `VerticalStack`
- `Text`
50 changes: 50 additions & 0 deletions easy-ui-react/src/EmptyStateCard/EmptyStateCard.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { ArgTypes, Canvas, Meta, Controls } from "@storybook/blocks";
import { EmptyStateCard } from "./EmptyStateCard";
import * as EmptyStateCardStories from "./EmptyStateCard.stories";

<Meta of={EmptyStateCardStories} />

# EmptyStateCard

An `<EmptyStateCard />` is a styled container with a header, body, and action sections, designed to display relevant information when there is no nearby data to display.

It is a simple compound component consisting of `<EmptyStateCard.Header />`, `<EmptyStateCard.HeaderText />`, `<EmptyStateCard.Body />`, `<EmptyStateCard.BodyText />`, and `<EmptyStateCard.Action />`.

## Gap

Use `blockGap` and `textGap`to configure the content spacing.

<Canvas of={EmptyStateCardStories.Gap} />

<Controls of={EmptyStateCardStories.Gap} />

## Positioning

Use `contentAlignment` to configure the content alignment.

<Canvas of={EmptyStateCardStories.Position} />

<Controls of={EmptyStateCardStories.Position} />

## Properties

### EmptyStateCard

<ArgTypes of={EmptyStateCard} />

### EmptyStateCard.Header

<ArgTypes of={EmptyStateCard.Header} />

### EmptyStateCard.HeaderText

<ArgTypes of={EmptyStateCard.HeaderText} />

### EmptyStateCard.Body

<ArgTypes of={EmptyStateCard.Body} />

### EmptyStateCard.BodyText

<ArgTypes of={EmptyStateCard.BodyText} />
83 changes: 83 additions & 0 deletions easy-ui-react/src/EmptyStateCard/EmptyStateCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from "react";
import { Meta, StoryObj } from "@storybook/react";
import { EmptyStateCard, EmptyStateCardProps } from "./EmptyStateCard";
import { Button } from "../Button";
import { getDesignTokensControl } from "../utilities/storybook";

type Story = StoryObj<typeof EmptyStateCard>;

const Template = (args: EmptyStateCardProps) => {
const { children, ...restArgs } = args;
return <EmptyStateCard {...restArgs}>{children}</EmptyStateCard>;
};

const meta: Meta<typeof EmptyStateCard> = {
title: "Components/Cards/EmptyStateCard",
component: EmptyStateCard,
argTypes: {
blockGap: getDesignTokensControl("space.{alias}"),
textGap: getDesignTokensControl("space.{alias}"),
},
};

export default meta;

export const Gap: Story = {
render: Template.bind({}),
args: {
children: (
<>
<EmptyStateCard.Header>
<EmptyStateCard.HeaderText>
Shipment Insurance
</EmptyStateCard.HeaderText>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<EmptyStateCard.BodyText>
Rest easy knowing if one of your customers orders is damaged, lost
in transit or stolen you are covered! Automatically add insurance to
all your shipments
</EmptyStateCard.BodyText>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Manage Insurance Settings</Button>
</EmptyStateCard.Action>
</>
),
blockGap: "2",
textGap: "1",
},
parameters: {
controls: {
include: ["blockGap", "textGap"],
},
},
};

export const Position: Story = {
render: Template.bind({}),
args: {
children: (
<>
<EmptyStateCard.Header>
<EmptyStateCard.HeaderText>Analytics</EmptyStateCard.HeaderText>
</EmptyStateCard.Header>
<EmptyStateCard.Body>
<EmptyStateCard.BodyText>
Start shipping to get insights on your shipping costs and
performance.
</EmptyStateCard.BodyText>
</EmptyStateCard.Body>
<EmptyStateCard.Action>
<Button>Buy a Label</Button>
</EmptyStateCard.Action>
</>
),
contentAlignment: "center",
},
parameters: {
controls: {
include: ["contentAlignment"],
},
},
};
Loading

0 comments on commit 712edb9

Please sign in to comment.