@sproutsocial/seeds-react-table 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +6 -0
- package/.eslintrc.js +4 -0
- package/.turbo/turbo-build.log +53 -0
- package/CHANGELOG.md +7 -0
- package/dist/TableCell-B8GMRlv7.d.mts +13 -0
- package/dist/TableCell-CN71R1B5.d.ts +13 -0
- package/dist/TableCellTypes-Cp-8r7l1.d.mts +21 -0
- package/dist/TableCellTypes-Cp-8r7l1.d.ts +21 -0
- package/dist/TableHeaderCell-DnwlruQg.d.ts +12 -0
- package/dist/TableHeaderCell-DsJpGb2j.d.mts +12 -0
- package/dist/TableHeaderCellTypes-CH_zzW6X.d.ts +25 -0
- package/dist/TableHeaderCellTypes-CsJQBwu2.d.mts +25 -0
- package/dist/TableTypes-Dg7QrcGt.d.ts +37 -0
- package/dist/TableTypes-jS0O3bwQ.d.mts +37 -0
- package/dist/esm/chunk-67DCEN4G.js +140 -0
- package/dist/esm/chunk-67DCEN4G.js.map +1 -0
- package/dist/esm/chunk-XJMS6762.js +62 -0
- package/dist/esm/chunk-XJMS6762.js.map +1 -0
- package/dist/esm/index.js +106 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/tableCell.js +9 -0
- package/dist/esm/tableCell.js.map +1 -0
- package/dist/esm/tableHeaderCell.js +11 -0
- package/dist/esm/tableHeaderCell.js.map +1 -0
- package/dist/esm/tableRowAccordion.js +110 -0
- package/dist/esm/tableRowAccordion.js.map +1 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +327 -0
- package/dist/index.js.map +1 -0
- package/dist/tableCell.d.mts +9 -0
- package/dist/tableCell.d.ts +9 -0
- package/dist/tableCell.js +98 -0
- package/dist/tableCell.js.map +1 -0
- package/dist/tableHeaderCell.d.mts +10 -0
- package/dist/tableHeaderCell.d.ts +10 -0
- package/dist/tableHeaderCell.js +177 -0
- package/dist/tableHeaderCell.js.map +1 -0
- package/dist/tableRowAccordion.d.mts +25 -0
- package/dist/tableRowAccordion.d.ts +25 -0
- package/dist/tableRowAccordion.js +200 -0
- package/dist/tableRowAccordion.js.map +1 -0
- package/jest.config.js +9 -0
- package/package.json +65 -0
- package/src/Table.stories.tsx +403 -0
- package/src/Table.tsx +111 -0
- package/src/TableCell/TableCell.stories.tsx +30 -0
- package/src/TableCell/TableCell.tsx +44 -0
- package/src/TableCell/TableCellTypes.ts +30 -0
- package/src/TableCell/__tests__/TabelCell.test.tsx +36 -0
- package/src/TableCell/__tests__/TableCell.typetest.tsx +34 -0
- package/src/TableCell/index.ts +5 -0
- package/src/TableCell/styles.ts +16 -0
- package/src/TableHeaderCell/TableHeaderCell.stories.tsx +46 -0
- package/src/TableHeaderCell/TableHeaderCell.tsx +120 -0
- package/src/TableHeaderCell/TableHeaderCellTypes.ts +26 -0
- package/src/TableHeaderCell/__tests__/TableHeaderCell.test.tsx +28 -0
- package/src/TableHeaderCell/__tests__/TableHeaderCell.typetest.tsx +31 -0
- package/src/TableHeaderCell/constants.ts +4 -0
- package/src/TableHeaderCell/index.ts +6 -0
- package/src/TableHeaderCell/styles.ts +46 -0
- package/src/TableRowAccordion/TableRowAccordion.stories.tsx +63 -0
- package/src/TableRowAccordion/TableRowAccordion.tsx +75 -0
- package/src/TableRowAccordion/TableRowAccordionTypes.ts +20 -0
- package/src/TableRowAccordion/__tests__/TableRowAccordion.test.tsx +104 -0
- package/src/TableRowAccordion/__tests__/TableRowAccordion.typetest.tsx +51 -0
- package/src/TableRowAccordion/index.ts +5 -0
- package/src/TableRowAccordion/styles.ts +25 -0
- package/src/TableTypes.ts +54 -0
- package/src/__tests__/Table.test.tsx +106 -0
- package/src/__tests__/Table.typetest.tsx +145 -0
- package/src/index.ts +5 -0
- package/src/styles.ts +21 -0
- package/styled.d.ts +7 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +17 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@sproutsocial/seeds-react-testing-library";
|
|
3
|
+
import { TableCell } from "../TableCell";
|
|
4
|
+
|
|
5
|
+
describe("TableCell", () => {
|
|
6
|
+
it("should render properly", () => {
|
|
7
|
+
render(<TableCell id="stuff" content="more stuff here" />);
|
|
8
|
+
expect(
|
|
9
|
+
screen.getByDataQaLabel({
|
|
10
|
+
"table-cell": "",
|
|
11
|
+
})
|
|
12
|
+
).toBeTruthy();
|
|
13
|
+
expect(screen.getByText("more stuff here")).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("renders the children if present", () => {
|
|
17
|
+
render(<TableCell id="test">Child</TableCell>);
|
|
18
|
+
expect(screen.getByText("Child")).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("renders as td when scope is not present", () => {
|
|
22
|
+
render(<TableCell id="scope-test" content="this is a td" />);
|
|
23
|
+
const tdElement = screen.getByRole("cell");
|
|
24
|
+
expect(tdElement).toBeInTheDocument();
|
|
25
|
+
expect(tdElement.tagName).toBe("TD");
|
|
26
|
+
expect(tdElement).not.toHaveAttribute("scope");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("renders as th when scope is present", () => {
|
|
30
|
+
render(<TableCell id="scope-test" scope="row" content="this is a td" />);
|
|
31
|
+
const thElement = screen.getByRole("rowheader");
|
|
32
|
+
expect(thElement).toBeInTheDocument();
|
|
33
|
+
expect(thElement.tagName).toBe("TH");
|
|
34
|
+
expect(thElement).toHaveAttribute("scope", "row");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { TableCell } from "../TableCell";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
function TableCellTypes() {
|
|
6
|
+
return (
|
|
7
|
+
<>
|
|
8
|
+
<TableCell
|
|
9
|
+
id="bacon_smokehouse"
|
|
10
|
+
content="🍔 Bacon Smokehouse Burger"
|
|
11
|
+
color="text.body"
|
|
12
|
+
/>
|
|
13
|
+
<TableCell
|
|
14
|
+
id="bacon_smokehouse"
|
|
15
|
+
content="🍔 Bacon Smokehouse Burger"
|
|
16
|
+
color="text.body"
|
|
17
|
+
width={300}
|
|
18
|
+
colSpan={2}
|
|
19
|
+
align="center"
|
|
20
|
+
/>
|
|
21
|
+
<TableCell
|
|
22
|
+
id="bacon_smokehouse"
|
|
23
|
+
content="🍔 Bacon Smokehouse Burger"
|
|
24
|
+
color="text.body"
|
|
25
|
+
width={300}
|
|
26
|
+
colSpan={2}
|
|
27
|
+
align="center"
|
|
28
|
+
scope="row"
|
|
29
|
+
/>
|
|
30
|
+
{/* @ts-expect-error - test that missing required props is rejected */}
|
|
31
|
+
<TableCell />
|
|
32
|
+
</>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { COMMON } from "@sproutsocial/seeds-react-system-props";
|
|
3
|
+
import type { TypeTableCellProps } from "./TableCellTypes";
|
|
4
|
+
|
|
5
|
+
const Container = styled.td<{
|
|
6
|
+
alignment: TypeTableCellProps["align"];
|
|
7
|
+
}>`
|
|
8
|
+
${(props) => props.theme.typography[200]}
|
|
9
|
+
font-weight: ${(props) => props.theme.fontWeights.normal};
|
|
10
|
+
padding: ${(props) => props.theme.space[300]};
|
|
11
|
+
text-align: ${(props) => props.alignment};
|
|
12
|
+
|
|
13
|
+
${COMMON}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
export default Container;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { TableHeaderCell } from "./TableHeaderCell";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof TableHeaderCell> = {
|
|
6
|
+
title: "Components/Table/TableHeaderCell",
|
|
7
|
+
component: TableHeaderCell,
|
|
8
|
+
argTypes: {
|
|
9
|
+
isSortable: {
|
|
10
|
+
control: "boolean",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
content: "Food Item",
|
|
15
|
+
id: "1",
|
|
16
|
+
isSortable: undefined,
|
|
17
|
+
align: undefined,
|
|
18
|
+
width: undefined,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export default meta;
|
|
22
|
+
|
|
23
|
+
type Story = StoryObj<typeof TableHeaderCell>;
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
color: "text.headline",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Sortable: Story = {
|
|
32
|
+
args: {
|
|
33
|
+
...Default.args,
|
|
34
|
+
isSortable: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const AdditionalProps: Story = {
|
|
39
|
+
args: {
|
|
40
|
+
...Sortable.args,
|
|
41
|
+
content: "Calories",
|
|
42
|
+
id: "2",
|
|
43
|
+
align: "center",
|
|
44
|
+
width: 100,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import Icon, { type TypeIconName } from "@sproutsocial/seeds-react-icon";
|
|
3
|
+
import Container, { SortIcon, UnstyledButton } from "./styles";
|
|
4
|
+
import { SORT_DIRECTIONS } from "./constants";
|
|
5
|
+
import type { TypeTableHeaderCellProps } from "./TableHeaderCellTypes";
|
|
6
|
+
|
|
7
|
+
export class TableHeaderCell extends React.Component<TypeTableHeaderCellProps> {
|
|
8
|
+
static displayName: string;
|
|
9
|
+
|
|
10
|
+
getSortIcon = (
|
|
11
|
+
isSorted: boolean,
|
|
12
|
+
ariaSortDirection: "ascending" | "descending" | undefined
|
|
13
|
+
) => {
|
|
14
|
+
const { sortDirection } = this.props;
|
|
15
|
+
let iconName: TypeIconName = "caret-up-down-outline";
|
|
16
|
+
|
|
17
|
+
if (isSorted && sortDirection === SORT_DIRECTIONS.ASC) {
|
|
18
|
+
iconName = "caret-up-solid";
|
|
19
|
+
} else if (isSorted && sortDirection === SORT_DIRECTIONS.DESC) {
|
|
20
|
+
iconName = "caret-down-solid";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<SortIcon>
|
|
25
|
+
<Icon size="mini" name={iconName} aria-label={ariaSortDirection} />
|
|
26
|
+
</SortIcon>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// @ts-note: If this is set to an HTMLButtonElement event, then the `Container` freaks out
|
|
31
|
+
handleClick = (
|
|
32
|
+
e: React.MouseEvent<HTMLTableCellElement | HTMLButtonElement>
|
|
33
|
+
) => {
|
|
34
|
+
const { onClick, onSort, isSortable, id } = this.props;
|
|
35
|
+
|
|
36
|
+
if (onClick || isSortable) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (onClick) {
|
|
41
|
+
// @ts-note: Right now the `onClick` is incorrectly set to consume an HTMLButtonElement event
|
|
42
|
+
onClick(e as unknown as React.MouseEvent<HTMLButtonElement>);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!isSortable || !onSort) return;
|
|
47
|
+
onSort(id);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
override render() {
|
|
51
|
+
const {
|
|
52
|
+
id,
|
|
53
|
+
content,
|
|
54
|
+
colSpan,
|
|
55
|
+
width,
|
|
56
|
+
align,
|
|
57
|
+
isSortable,
|
|
58
|
+
sortId,
|
|
59
|
+
sortDirection,
|
|
60
|
+
children,
|
|
61
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
62
|
+
shouldTruncate,
|
|
63
|
+
onSort,
|
|
64
|
+
onClick,
|
|
65
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
66
|
+
color,
|
|
67
|
+
...rest
|
|
68
|
+
} = this.props;
|
|
69
|
+
|
|
70
|
+
const isSorted = sortId === id;
|
|
71
|
+
const ariaSort = isSorted
|
|
72
|
+
? sortDirection === SORT_DIRECTIONS.ASC
|
|
73
|
+
? "ascending"
|
|
74
|
+
: "descending"
|
|
75
|
+
: undefined;
|
|
76
|
+
|
|
77
|
+
const buttonProps = isSortable
|
|
78
|
+
? {
|
|
79
|
+
role: "button",
|
|
80
|
+
onClick: this.handleClick,
|
|
81
|
+
}
|
|
82
|
+
: {};
|
|
83
|
+
|
|
84
|
+
const scope = colSpan ? "colgroup" : "col";
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Container
|
|
88
|
+
{...rest}
|
|
89
|
+
key={id}
|
|
90
|
+
alignment={align || "left"}
|
|
91
|
+
sortable={isSortable}
|
|
92
|
+
colSpan={colSpan}
|
|
93
|
+
scope={scope}
|
|
94
|
+
width={width}
|
|
95
|
+
onClick={this.handleClick}
|
|
96
|
+
data-tableheadercell-sortable={isSortable}
|
|
97
|
+
data-qa-table-header-cell=""
|
|
98
|
+
data-qa-table-header-cell-sortdirection={sortDirection}
|
|
99
|
+
// TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
color={color}
|
|
103
|
+
aria-sort={ariaSort}
|
|
104
|
+
>
|
|
105
|
+
{isSortable ? (
|
|
106
|
+
<UnstyledButton {...buttonProps}>
|
|
107
|
+
<div aria-live="polite" role="status">
|
|
108
|
+
{children || content}
|
|
109
|
+
{!children && this.getSortIcon(id === sortId, ariaSort)}
|
|
110
|
+
</div>
|
|
111
|
+
</UnstyledButton>
|
|
112
|
+
) : (
|
|
113
|
+
children || content
|
|
114
|
+
)}
|
|
115
|
+
</Container>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default TableHeaderCell;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { TypeTableCellProps } from "../TableCell/TableCellTypes";
|
|
3
|
+
import { SORT_DIRECTIONS } from "./constants";
|
|
4
|
+
|
|
5
|
+
export type TypeEnumSortDirections = keyof typeof SORT_DIRECTIONS;
|
|
6
|
+
|
|
7
|
+
export interface TypeTableHeaderCellProps
|
|
8
|
+
extends Omit<TypeTableCellProps, "onClick"> {
|
|
9
|
+
/** Legacy Deteremines if a table column is sortable (optional) */
|
|
10
|
+
isSortable?: boolean;
|
|
11
|
+
|
|
12
|
+
/** Truncates text into a singular line with ellipsis (optional) */
|
|
13
|
+
shouldTruncate?: boolean;
|
|
14
|
+
|
|
15
|
+
/** Legacy: Callback for Sorting Table Columns (optional) */
|
|
16
|
+
onSort?: (id: string) => void;
|
|
17
|
+
|
|
18
|
+
/** Legacy: Controls which column is being sorted (optional) */
|
|
19
|
+
sortId?: string;
|
|
20
|
+
|
|
21
|
+
/** Legacy: Controls the current sort direction (optional) */
|
|
22
|
+
sortDirection?: TypeEnumSortDirections;
|
|
23
|
+
|
|
24
|
+
/** Callback for Click Events. If Included will override onSort prop */
|
|
25
|
+
onClick?: (e: React.SyntheticEvent<HTMLButtonElement>) => void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen } from "@sproutsocial/seeds-react-testing-library";
|
|
3
|
+
import { TableHeaderCell } from "../TableHeaderCell";
|
|
4
|
+
describe("TableHeaderCell", () => {
|
|
5
|
+
it("should render properly", () => {
|
|
6
|
+
render(
|
|
7
|
+
<table>
|
|
8
|
+
<TableHeaderCell id="test" content="more stuff here" />
|
|
9
|
+
</table>
|
|
10
|
+
);
|
|
11
|
+
expect(
|
|
12
|
+
screen.getByDataQaLabel({
|
|
13
|
+
"table-header-cell": "",
|
|
14
|
+
})
|
|
15
|
+
).toBeTruthy();
|
|
16
|
+
expect(screen.getByText("more stuff here")).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
it("renders the children if present", () => {
|
|
19
|
+
render(
|
|
20
|
+
<table>
|
|
21
|
+
<TableHeaderCell id="test" content="content">
|
|
22
|
+
Child
|
|
23
|
+
</TableHeaderCell>
|
|
24
|
+
</table>
|
|
25
|
+
);
|
|
26
|
+
expect(screen.getByText("Child")).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { TableHeaderCell } from "../TableHeaderCell";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
function TableHeaderCellTypes() {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
content: "Food Item",
|
|
8
|
+
id: "1",
|
|
9
|
+
color: "text.headline",
|
|
10
|
+
isSortable: undefined,
|
|
11
|
+
align: undefined,
|
|
12
|
+
width: undefined,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<TableHeaderCell {...defaultProps} />
|
|
18
|
+
<TableHeaderCell {...defaultProps} isSortable />
|
|
19
|
+
<TableHeaderCell
|
|
20
|
+
{...defaultProps}
|
|
21
|
+
isSortable
|
|
22
|
+
content="Calories"
|
|
23
|
+
id="2"
|
|
24
|
+
align="center"
|
|
25
|
+
width={100}
|
|
26
|
+
/>
|
|
27
|
+
{/* @ts-expect-error - test that missing required props is rejected */}
|
|
28
|
+
<TableHeaderCell />
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import styled, { css } from "styled-components";
|
|
2
|
+
import { COMMON } from "@sproutsocial/seeds-react-system-props";
|
|
3
|
+
import type { TypeTableHeaderCellProps } from "./TableHeaderCellTypes";
|
|
4
|
+
|
|
5
|
+
const Container = styled.th<{
|
|
6
|
+
sortable?: boolean;
|
|
7
|
+
alignment: TypeTableHeaderCellProps["align"];
|
|
8
|
+
width: TypeTableHeaderCellProps["width"];
|
|
9
|
+
}>`
|
|
10
|
+
${(props) => props.theme.typography[100]}
|
|
11
|
+
font-weight: ${(props) => props.theme.fontWeights.semibold};
|
|
12
|
+
padding: ${(props) => props.theme.space[300]};
|
|
13
|
+
text-align: ${(props) => props.alignment};
|
|
14
|
+
|
|
15
|
+
${(props) =>
|
|
16
|
+
props.sortable &&
|
|
17
|
+
css`
|
|
18
|
+
position: relative;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
`}
|
|
21
|
+
|
|
22
|
+
${COMMON}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
export const SortIcon = styled.span`
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 50%;
|
|
28
|
+
transform: translateY(-50%);
|
|
29
|
+
font-size: 0;
|
|
30
|
+
padding: 0 ${(props) => props.theme.space[200]};
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
export const UnstyledButton = styled.button`
|
|
34
|
+
background: none;
|
|
35
|
+
border: none;
|
|
36
|
+
color: inherit;
|
|
37
|
+
font: inherit;
|
|
38
|
+
line-height: normal;
|
|
39
|
+
overflow: visible;
|
|
40
|
+
padding: 0;
|
|
41
|
+
-webkit-appearance: none;
|
|
42
|
+
-moz-appearance: none;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
export default Container;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import TableRowAccordion from "./TableRowAccordion";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof TableRowAccordion> = {
|
|
6
|
+
title: "Components/Table/TableRowAccordion",
|
|
7
|
+
component: TableRowAccordion,
|
|
8
|
+
};
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof TableRowAccordion>;
|
|
12
|
+
|
|
13
|
+
export const Collapsed: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
id: "1",
|
|
16
|
+
cells: [
|
|
17
|
+
{
|
|
18
|
+
id: "1",
|
|
19
|
+
content: "🍔 Bacon Smokehouse Burger",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "2",
|
|
23
|
+
content: "840",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "3",
|
|
27
|
+
content: "45g",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "4",
|
|
31
|
+
content: "62g",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "5",
|
|
35
|
+
content: "46g",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
isExpanded: false,
|
|
39
|
+
onToggle: () => {},
|
|
40
|
+
detail: (
|
|
41
|
+
<img
|
|
42
|
+
src="//i.kym-cdn.com/entries/icons/mobile/000/013/564/doge.jpg"
|
|
43
|
+
alt="doge"
|
|
44
|
+
/>
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Expanded: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
...Collapsed.args,
|
|
52
|
+
isExpanded: true,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const WithToggleFunction: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
...Collapsed.args,
|
|
59
|
+
onToggle: (id) => {
|
|
60
|
+
alert(`clicked with id ${id}`);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import Icon from "@sproutsocial/seeds-react-icon";
|
|
3
|
+
import TableCell from "../TableCell";
|
|
4
|
+
import Container, { Detail, Trigger } from "./styles";
|
|
5
|
+
import type { TypeTableRowAccordionProps } from "./TableRowAccordionTypes";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The table row accordion component allows for rendering a row of tabular data along with a addtional content to be rendered inside of an accordion drawer. This component is meant to be used with the table components rowRender mentod.
|
|
9
|
+
*/
|
|
10
|
+
export default class TableRowAccordion extends React.Component<TypeTableRowAccordionProps> {
|
|
11
|
+
handleToggle: React.MouseEventHandler<HTMLTableRowElement> = (e) => {
|
|
12
|
+
const { onToggle, id } = this.props;
|
|
13
|
+
e.stopPropagation();
|
|
14
|
+
|
|
15
|
+
if (onToggle) {
|
|
16
|
+
onToggle(id);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
override render() {
|
|
21
|
+
const {
|
|
22
|
+
id,
|
|
23
|
+
cells,
|
|
24
|
+
detail,
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
26
|
+
onToggle,
|
|
27
|
+
isExpanded,
|
|
28
|
+
color,
|
|
29
|
+
...rest
|
|
30
|
+
} = this.props;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Container
|
|
34
|
+
{...rest}
|
|
35
|
+
data-qa-table-row-accordion={isExpanded}
|
|
36
|
+
key={id}
|
|
37
|
+
// TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
color={color}
|
|
41
|
+
>
|
|
42
|
+
<tr data-tablerowaccordion-summary onClick={this.handleToggle}>
|
|
43
|
+
{cells.map((td) => {
|
|
44
|
+
return <TableCell {...td} key={td.id} />;
|
|
45
|
+
})}
|
|
46
|
+
<TableCell
|
|
47
|
+
id="tableRowAccordion_trigger"
|
|
48
|
+
content={
|
|
49
|
+
// TODO: This trigger needs an accessible label passed in via props
|
|
50
|
+
<Trigger
|
|
51
|
+
data-tablerowaccordion-trigger
|
|
52
|
+
onClick={this.handleToggle}
|
|
53
|
+
role="button"
|
|
54
|
+
>
|
|
55
|
+
<Icon
|
|
56
|
+
name={
|
|
57
|
+
isExpanded ? "chevron-up-outline" : "chevron-down-outline"
|
|
58
|
+
}
|
|
59
|
+
aria-hidden
|
|
60
|
+
/>
|
|
61
|
+
</Trigger>
|
|
62
|
+
}
|
|
63
|
+
/>
|
|
64
|
+
</tr>
|
|
65
|
+
<Detail isExpanded={isExpanded} data-tablerowaccordion-detail>
|
|
66
|
+
<TableCell
|
|
67
|
+
id="tableRowAccordion_detail"
|
|
68
|
+
colSpan={100}
|
|
69
|
+
content={detail}
|
|
70
|
+
/>
|
|
71
|
+
</Detail>
|
|
72
|
+
</Container>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { TypeSystemCommonProps } from "@sproutsocial/seeds-react-system-props";
|
|
3
|
+
import type { TypeTableRow } from "../TableTypes";
|
|
4
|
+
|
|
5
|
+
export interface TypeTableRowAccordionProps
|
|
6
|
+
extends TypeTableRow,
|
|
7
|
+
TypeSystemCommonProps,
|
|
8
|
+
Omit<
|
|
9
|
+
React.ComponentPropsWithoutRef<"tbody">,
|
|
10
|
+
keyof TypeTableRow | keyof TypeSystemCommonProps
|
|
11
|
+
> {
|
|
12
|
+
/** Content to be rendered in accordion drawer */
|
|
13
|
+
detail: React.ReactNode;
|
|
14
|
+
|
|
15
|
+
/** Controls the display state of the accordion drawer */
|
|
16
|
+
isExpanded: boolean;
|
|
17
|
+
|
|
18
|
+
/** Callback for toggling the accordion drawer state */
|
|
19
|
+
onToggle: (id: string) => void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
render,
|
|
4
|
+
fireEvent,
|
|
5
|
+
screen,
|
|
6
|
+
} from "@sproutsocial/seeds-react-testing-library";
|
|
7
|
+
import TableRowAccordion from "../TableRowAccordion";
|
|
8
|
+
|
|
9
|
+
describe("TableRowAccordion", () => {
|
|
10
|
+
const props = {
|
|
11
|
+
id: "bacon_smokehouse_burger_row",
|
|
12
|
+
cells: [
|
|
13
|
+
{
|
|
14
|
+
id: "cell_1",
|
|
15
|
+
content: "🍔 Bacon Smokehouse Burger",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "cell_2",
|
|
19
|
+
content: "840",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "cell_3",
|
|
23
|
+
content: "45g",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "cell_4",
|
|
27
|
+
content: "62g",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "cell_5",
|
|
31
|
+
content: "46g",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
detail: (
|
|
35
|
+
<img
|
|
36
|
+
src="//i.kym-cdn.com/entries/icons/mobile/000/013/564/doge.jpg"
|
|
37
|
+
alt="doge"
|
|
38
|
+
/>
|
|
39
|
+
),
|
|
40
|
+
isExpanded: false,
|
|
41
|
+
onToggle: jest.fn(),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
it("should render the table cells RTL", () => {
|
|
45
|
+
render(
|
|
46
|
+
<table>
|
|
47
|
+
<TableRowAccordion {...props} />
|
|
48
|
+
</table>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// we should get back a length that is one greater than length of cells we pass due to the addition of the trigger cell and detail cell
|
|
52
|
+
expect(
|
|
53
|
+
screen.getAllByDataQaLabel({
|
|
54
|
+
"table-cell": "",
|
|
55
|
+
})
|
|
56
|
+
).toBeTruthy();
|
|
57
|
+
|
|
58
|
+
expect(
|
|
59
|
+
screen.getAllByDataQaLabel({
|
|
60
|
+
"table-cell": "",
|
|
61
|
+
}).length
|
|
62
|
+
).toEqual(props.cells.length + 2);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should render the detail section closed by default", () => {
|
|
66
|
+
render(
|
|
67
|
+
<table>
|
|
68
|
+
<TableRowAccordion {...props} />
|
|
69
|
+
</table>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(
|
|
73
|
+
screen.getAllByDataQaLabel({
|
|
74
|
+
"table-row-accordion": "false",
|
|
75
|
+
})
|
|
76
|
+
).toBeTruthy();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should render the detail section open when the isExpanded prop is set to true", () => {
|
|
80
|
+
render(
|
|
81
|
+
<table>
|
|
82
|
+
<TableRowAccordion {...props} isExpanded={true} />
|
|
83
|
+
</table>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
screen.getAllByDataQaLabel({
|
|
88
|
+
"table-row-accordion": "true",
|
|
89
|
+
})
|
|
90
|
+
).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("the onToggle method should return the rows id when clicked", () => {
|
|
94
|
+
const callback = jest.fn();
|
|
95
|
+
render(
|
|
96
|
+
<table>
|
|
97
|
+
<TableRowAccordion {...props} onToggle={callback} />
|
|
98
|
+
</table>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
fireEvent.click(screen.getByRole("button"));
|
|
102
|
+
expect(callback).toBeCalledWith(props.id);
|
|
103
|
+
});
|
|
104
|
+
});
|