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

Create <Table /> spec #535

Merged
merged 7 commits into from
Aug 17, 2023
Merged
Changes from 6 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
238 changes: 238 additions & 0 deletions documentation/specs/DisplayTable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# `DisplayTable` Component Specification
stephenjwatkins marked this conversation as resolved.
Show resolved Hide resolved

## Overview

A `DisplayTable` is used for presenting information displayed across columns and rows.

---

## Design

`DisplayTable` is for presentational concerns only. It doesn't support interactions such as selection, sorting, and row expansion. See the `Table` component for an interactive table accommodating advanced use cases.

Without interaction, complexities are shifted to surfacing a simple component API, structuring the table for style requirements, and ensuring appropriate application of accessibility markup.

`DisplayTable` will use React Aria's `useTable` under the hood to reliably generate accessibile markup for the table.

`DisplayTable` will use `columns` and `rows` as the primary mechanism for passing data to the table. While this is a slight departure from our dot-notation pattern used throughout the rest of Easy UI, this makes for a consistent data input pattern with our `Table`-related components.

### API

```ts
type DisplayTableProps = AriaLabelingProps & {
/** Columns for the table. */
columns: Column[];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for DisplayTable, columns and rows follow a highly-specific data structure as noted in the type definitions below. this is a bit of a departure from our Table component, where the actual cell data is largely arbitrary and the rendering is controlled by the consumer (via renderHeaderCell, renderBodyCell, etc).

i think (a) i wanted to try to match our current MarketingTable API as much as we could where reasonable; and (b) it seems like to me for a display table where so much of the styling is governed by the internals of the component itself, it makes sense for it to not to defer rendering to the consumer and instead opt for a specific data structure.

let me know if that makes sense and feels cohesive enough in our component library for having the two approaches, and/or if there are better ideas


/**
* Horizontal alignment of data within their cells.
* @default start
*/
dataAlignment?: "start" | "center" | "end";
stephenjwatkins marked this conversation as resolved.
Show resolved Hide resolved

/**
* Marks the table as having no row headers. For accessibility reasons, this should be avoided when possible.
*/
hasNoRowHeaders?: boolean;
stephenjwatkins marked this conversation as resolved.
Show resolved Hide resolved

/** Rows for the table. */
rows: Row[];
};

type Row = Cell[];

type Cell =
| ReactNode
| {
/** Cell text. */
text?: ReactNode;

/** Cell icon. */
symbol?: IconSymbol;
stephenjwatkins marked this conversation as resolved.
Show resolved Hide resolved

/** Mark cell as highlighted. Not applicable on row headers. */
isHighlighted?: boolean;
};

type Column =
| ReactNode
| {
/** Column heading. */
heading?: ReactNode;

/** Column subheading. */
subheading?: ReactNode;

/** Column heading icon or image. */
media?: ImgMedia | IconMedia;
};

type IconMedia = {
symbol: IconSymbol;
};

type ImgMedia = {
src: string;
alt: string;
};
```

### Example Usage

_Basic usage_:

```tsx
import { DisplayTable } from "@easypost/easy-ui/DisplayTable";

function CustomDisplayTable() {
return (
<DisplayTable
aria-label="Example basic display table"
columns={[
"Weight",
"Priority",
"ParcelSelect",
"First",
"Express",
"LibraryMail",
"MediaMail",
]}
rows={[
["1 oz.", "$7.64", "$6.99", "$3.59", "$24.90", "$3.47", "$3.65"],
["2 oz.", "$7.64", "$6.99", "$3.59", "$24.90", "$3.47", "$3.65"],
["3 oz.", "$7.64", "$6.99", "$3.59", "$24.90", "$3.47", "$3.65"],
["4 oz.", "$7.64", "$6.99", "$3.99", "$24.90", "$3.47", "$3.65"],
["5 oz.", "$7.64", "$6.99", "$3.99", "$24.90", "$3.47", "$3.65"],
]}
/>
);
}
```

_Advanced usage_:

```tsx
import { DisplayTable } from "@easypost/easy-ui/DisplayTable";

function CustomDisplayTable() {
return (
<DisplayTable
aria-label="Example advanced display table"
hasRowHeaders
columns={[
null,
{
heading: "Uptime",
subheading: "(Peak 2020)",
media: {
src: "/assets/images/carriers/apc-logo-ca.png",
alt: "",
},
},
{
heading: "Outage events",
subheading: "(Peak 2020)",
media: {
src: "/assets/images/carriers/dai-post-logo.png",
alt: "",
},
},
{
heading: "Uptime",
subheading: "(2021)",
media: { symbol: IconSymbol },
},
{
heading: "Outage events",
subheading: "(2021)",
media: { symbol: IconSymbol },
},
{
heading: "Uptime",
subheading: "(Q2 2021)",
media: { symbol: IconSymbol },
},
{
heading: "Outage events ",
subheading: "(Q2 2021)",
media: { symbol: IconSymbol },
},
]}
rows={[
[
"Website",
{ symbol: CheckIconSymbol },
"",
{ symbol: CheckIconSymbol },
"",
{ symbol: CheckIconSymbol },
"",
],
[
"Shipping API",
{
text: "*US Exports Only",
symbol: CheckIconSymbol,
isHighlighted: true,
},
{ symbol: CheckIconSymbol },
"",
{ symbol: CheckIconSymbol },
"",
"",
],
[
"Tracking API",
{ symbol: CheckIconSymbol },
"",
{ text: "*US Exports Only", symbol: CheckIconSymbol },
"",
"",
{ symbol: CheckIconSymbol },
],
[
"Address Verification API",
{ symbol: CheckIconSymbol, isHighlighted: true },
{ symbol: CheckIconSymbol },
"",
"",
"",
{ symbol: CheckIconSymbol },
],
[
"Events Webhooks",
{ symbol: CheckIconSymbol },
"",
{ symbol: CheckIconSymbol },
{ symbol: CheckIconSymbol },
{
text: "*US Exports Only",
symbol: CheckIconSymbol,
isHighlighted: true,
},
{ symbol: CheckIconSymbol },
],
]}
/>
);
}
```

---

## Behavior

### Accessibility

Tables are used to organize data with a logical relationship in grids. Accessible tables need HTML markup that indicates header cells and data cells and defines their relationship. Assistive technologies use this information to provide context to users.

- Tables must have a thead, containing a th for each column.
- Tables must have a tbody wrapping the table body of rows.
- Tables should try to include row headers as the first cell in a row.
- `DisplayTable` should contain static textual and numeric data rather than actionable components. For more advanced cases, see `Table`.

Most accessibility concerns will be handled through React Aria.

## Dependencies

- `react-aria`—`useTable`
- `@easypost/easy-ui`—`useIntersectionDetection` for the stuck styles