Skip to content

Commit

Permalink
feat: support sections and a decorative background for EmptyStateCard (
Browse files Browse the repository at this point in the history
…#1465)

## 📝 Changes

- Added support for a decorative background for `EmptyStateCard`
- Added support for `EmptyStateCard.MultiSection` and
`EmptyStateCard.Section`
- Reworked architecture to allow for customized configurations

## ✅ 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 14, 2024
1 parent 00a9497 commit 6161a5c
Show file tree
Hide file tree
Showing 10 changed files with 479 additions and 443 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-lies-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@easypost/easy-ui": minor
---

feat: support sections and a decorative background for EmptyStateCard
222 changes: 89 additions & 133 deletions documentation/specs/EmptyStateCard.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,23 @@

## Overview

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.
An `EmptyStateCard` is a styled container designed to display relevant information when there is no nearby data to display.

### Use Cases

- Use to draw attention to encourage interaction when there is no nearby data to display

### Features

- Header, body, and action components are broken into composable pieces
- Components are broken into composable pieces

## Design

An `EmptyStateCard` is a very simple compound component consisting of`EmptyStateCard.Header`, `EmptyStateCard.HeaderText`, `EmptyStateCard.Body`, `EmptyStateCard.BodyText`, and `EmptyStateCard.Action`.
An `EmptyStateCard` is a compound component consisting of `EmptyStateCard.MultiSection`, `EmptyStateCard.Section`, `EmptyStateCard.TextGroup`, `EmptyStateCard.HeaderText`, `EmptyStateCard.BodyText`, and `EmptyStateCard.ActionGroup`. The styled container will be handled by the `Card` component.

The `EmptyStateCard` will render `EmptyStateCard.Header`,` EmptyStateCard.Body`, and`EmptyStateCard.Action` via children, while delegating the styled container to the `Card` component.
A `EmptyStateCard.Section` is a simple container that can render `EmptyStateCard.TextGroup`, `EmptyStateCard.HeaderText`, `EmptyStateCard.BodyText`, and `EmptyStateCard.ActionGroup`. It can also support custom markup. To handle multiple `EmptyStateCard.Section` components, consumers should use `EmptyStateCard.MultiSection`.

`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.
The `EmptyStateCard.HeaderText` and` EmptyStateCard.BodyText` components will be lightweight wrappers around the `Text` component.

No new external dependencies will be introduced.

Expand All @@ -29,48 +27,17 @@ No new external dependencies will be introduced.
```ts
export type EmptyStateCardProps = {
/**
* The children of the <EmptyStateCard> element. Should render
* `<EmptyStateCard.Header>`, `<EmptyStateCard.Body>`, and
* `<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 = {
/**
* Header content of card
* The children of the <EmptyStateCard> element.
*/
children: ReactNode;
};

export type EmptyStateCardBodyProps = {
export type EmptyStateCardSectionProps = VerticalStackProps & {
/**
* Body content of card
* Renders a section with a decorative background.
* @default false
*/
children: ReactNode;
};

export type EmptyStateCardActionProps = {
/**
* Action content of card
*/
children: ReactNode;
hasDecorativeBackground?: boolean;
};
```

Expand All @@ -85,53 +52,23 @@ import { Button } from "@easypost/easy-ui/Button";

function Component() {
return (
<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>,
);
}
```

_Gap:_

```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 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>,
<EmptyStateCard>
<EmptyStateCard.Section>
<EmptyStateCard.TextGroup>
<EmptyStateCard.HeaderText>
Shipment Insurance
</EmptyStateCard.HeaderText>
<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.TextGroup>
<EmptyStateCard.ActionGroup>
<Button>Manage Insurance Settings</Button>
</EmptyStateCard.ActionGroup>
</EmptyStateCard.Section>
</EmptyStateCard>
);
}
```
Expand All @@ -145,23 +82,23 @@ 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>,
<EmptyStateCard>
<EmptyStateCard.Section inlineAlign="center">
<EmptyStateCard.TextGroup gap="2">
<EmptyStateCard.HeaderText>
Shipment Insurance
</EmptyStateCard.HeaderText>
<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.TextGroup>
<EmptyStateCard.ActionGroup>
<Button>Manage Insurance Settings</Button>
</EmptyStateCard.ActionGroup>
</EmptyStateCard.Section>
</EmptyStateCard>
);
}
```
Expand All @@ -171,58 +108,75 @@ function Component() {
```tsx
import { Card } from "../Card";
import { Text, TextProps } from "../Text";
import { VerticalStack } from "../VerticalStack";
import { HorizontalStack, HorizontalStackProps } from "../HorizontalStack";
import { VerticalStack, VerticalStackProps } from "../VerticalStack";

export function EmptyStateCard(props: EmptyStateCardProps) {
const { children, ...verticalStackProps } = props;
const { children } = props;

return (
<Card>
<VerticalStack {...verticalStackProps}>{children}</VerticalStack>
</Card>
);
return <Card>{children}</Card>;
}

function EmptyStateCardHeader(props: EmptyStateCardHeaderProps) {
const { children } = props;
function EmptyStateCardMultiSection(props: HorizontalStackProps) {
const { children, ...restProps } = props;
return <HorizontalStack {...restProps}>{children}</HorizontalStack>;
}

function EmptyStateCardSection(props: EmptyStateCardSectionProps) {
const { hasDecorativeBackground = false, children, ...restProps } = props;

return <div>{children}</div>;
return (
<div>
{!hasDecorativeBackground ? (
<Card.Area>
<VerticalStack {...restProps}>{children}</VerticalStack>
</Card.Area>
) : (
<Card.Area>
<div>
<div>
<VerticalStack {...restProps}>{children}</VerticalStack>
</div>
</div>
</Card.Area>
)}
</div>
);
}

function EmptyStateCardHeaderText(props: TextProps) {
const { ...textProps } = props;
function EmptyStateCardTextGroup(props: VerticalStackProps) {
const { children, ...restProps } = props;
return <VerticalStack {...restProps}>{children}</VerticalStack>;
}

return <Text {...textProps} />;
function EmptyStateCardActionGroup(props: HorizontalStackProps) {
const { children, ...restProps } = props;
return <HorizontalStack {...restProps}>{children}</HorizontalStack>;
}

function EmptyStateCardBody(props: EmptyStateCardBodyProps) {
const { children } = props;
function EmptyStateCardHeaderText(props: TextProps) {
const { ...restTextProps } = props;

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

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

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

function EmptyStateCardAction(props: EmptyStateCardActionProps) {
const { children } = props;
EmptyStateCard.MultiSection = EmptyStateCardMultiSection;

return <div>{children}</div>;
}
EmptyStateCard.Section = EmptyStateCardSection;

EmptyStateCard.Header = EmptyStateCardHeader;
EmptyStateCard.TextGroup = EmptyStateCardTextGroup;

EmptyStateCard.HeaderText = EmptyStateCardHeaderText;

EmptyStateCard.Body = EmptyStateCardBody;

EmptyStateCard.BodyText = EmptyStateCardBodyText;

EmptyStateCard.Action = EmptyStateCardAction;
```
EmptyStateCard.ActionGroup = EmptyStateCardActionGroup;

---

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

- `Card`
- `VerticalStack`
- `HorizontalStack`
- `Text`
```
52 changes: 36 additions & 16 deletions easy-ui-react/src/EmptyStateCard/EmptyStateCard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,64 @@ import * as EmptyStateCardStories from "./EmptyStateCard.stories";

# 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.
An `<EmptyStateCard />` is a styled container 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 />`.
It is a compound component consisting of `<EmptyStateCard.MultiSection />`, `<EmptyStateCard.Section />`, `<EmptyStateCard.TextGroup />`, `<EmptyStateCard.HeaderText />`, `<EmptyStateCard.BodyText />`, and `<EmptyStateCard.ActionGroup />`

## Gap
## Simple

Use `blockGap` and `textGap`to configure the content spacing.
<Canvas of={EmptyStateCardStories.Simple} />

<Canvas of={EmptyStateCardStories.Gap} />
## Alignment

<Controls of={EmptyStateCardStories.Gap} />
`<EmptyStateCard.Section />` and `<EmptyStateCard.TextGroup />` can accept `VerticalStack` props to override defaults and to assist with alignment. Similarly, `<EmptyStateCard.ActionGroup />` can accept `HorizontalStack` props.

## Positioning
<Canvas of={EmptyStateCardStories.Alignment} />

Use `contentAlignment` to configure the content alignment.
## Multiple Sections

<Canvas of={EmptyStateCardStories.Position} />
Use `EmptyStateCard.MultiSection` to render horizontally aligned sections; it can accept `HorizontalStack` props to assist with alignment.

<Controls of={EmptyStateCardStories.Position} />
### Decorative

Use `hasDecorativeBackground` to render a `<EmptyStateCard.Section />` with a decorative background.

**Note**: A decorative section is designed to render aligned to the end of a `EmptyStateCard.MultiSection` and has pre-configured positioning for content and collapses on smaller screens.

<Canvas of={EmptyStateCardStories.Decorative} />

### Custom Markup

A `<EmptyStateCard.Section />` can also render custom markup.

<Canvas of={EmptyStateCardStories.Custom} />

## Properties

### EmptyStateCard

<ArgTypes of={EmptyStateCard} />

### EmptyStateCard.Header
### EmptyStateCard.MultiSection

<ArgTypes of={EmptyStateCard.Header} />
<ArgTypes of={EmptyStateCard.MultiSection} />

### EmptyStateCard.HeaderText
### EmptyStateCard.Section

<ArgTypes of={EmptyStateCard.Section} />

### EmptyStateCard.TextGroup

<ArgTypes of={EmptyStateCard.HeaderText} />
<ArgTypes of={EmptyStateCard.TextGroup} />

### EmptyStateCard.Body
### EmptyStateCard.HeaderText

<ArgTypes of={EmptyStateCard.Body} />
<ArgTypes of={EmptyStateCard.TextGroup} />

### EmptyStateCard.BodyText

<ArgTypes of={EmptyStateCard.BodyText} />

### EmptyStateCard.ActionGroup

<ArgTypes of={EmptyStateCard.ActionGroup} />
Loading

0 comments on commit 6161a5c

Please sign in to comment.