Skip to content

Commit 1d98473

Browse files
author
Haider Alshamma
committed
fix: improve Table component types
1 parent fdbfb6b commit 1d98473

18 files changed

+158
-227
lines changed

src/Table/BaseTable.story.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
/* eslint-disable react/prop-types */
21
import React from "react";
32
import styled from "styled-components";
43
import { boolean, text } from "@storybook/addon-knobs";
54
import { action } from "@storybook/addon-actions";
65
import { Box, DropdownButton, DropdownMenu, Button, Text } from "..";
6+
import { ColumnType } from "./Table.types";
77
import { getMockRows, mockColumns } from "./Table.mock-utils";
8-
import { Columns } from "./Table.types";
98
import { Table } from ".";
109

1110
const dateToString = ({ cellData }) => {
@@ -31,7 +30,15 @@ const dropdownCellRenderer = ({ cellData }) => (
3130
</Box>
3231
);
3332

34-
const columns: Columns = [
33+
type Row = {
34+
date: string;
35+
expectedQuantity: string;
36+
actualQuantity: string;
37+
note?: string;
38+
id: string;
39+
};
40+
41+
const columns: ColumnType<Row>[] = [
3542
{ label: "Date", dataKey: "date" },
3643
{ label: "Expected Quantity", dataKey: "expectedQuantity" },
3744
{ label: "Actual Quantity", dataKey: "actualQuantity", align: "right" },
@@ -185,7 +192,7 @@ const columnsWithFormatter = [
185192
{ label: "Actual Quantity", dataKey: "actualQuantity" },
186193
];
187194

188-
const columnsWithAlignment: Columns = [
195+
const columnsWithAlignment: ColumnType<Row>[] = [
189196
{ label: "Date", dataKey: "date" },
190197
{ label: "Expected Eaches", dataKey: "expectedQuantity" },
191198
{ label: "Actual Eaches", dataKey: "actualQuantity", align: "right" },
@@ -392,7 +399,6 @@ export const WithAFooter = () => (
392399
WithAFooter.story = {
393400
name: "with a footer",
394401
};
395-
/* eslint-enable react/prop-types */
396402

397403
const TableWithBorderedRows = styled(Table)`
398404
border-collapse: collapse;

src/Table/BaseTable.tsx

Lines changed: 49 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,86 @@
11
import React from "react";
22
import styled from "styled-components";
3-
import PropTypes from "prop-types";
4-
import { space } from "styled-system";
5-
import propTypes from "@styled-system/prop-types";
3+
import { addStyledProps } from "../StyledProps";
64
import TableHead from "./TableHead";
75
import TableBody from "./TableBody";
86
import TableFoot from "./TableFoot";
9-
import { rowsPropType, RowType, Columns } from "./Table.types";
7+
import { ColumnType } from "./Table.types";
108

11-
export type BaseTableProps = {
12-
columns: Columns;
13-
rows: RowType[];
9+
export type BaseTableProps<Row extends unknown> = {
10+
columns: ColumnType<Row>[];
11+
rows: Row[];
1412
noRowsContent?: string;
1513
keyField?: string;
1614
id?: string;
1715
loading?: boolean;
18-
footerRows?: any;
16+
footerRows?: Row[];
1917
rowHovers?: boolean;
2018
compact?: boolean;
2119
className?: string;
2220
stickyHeader?: boolean;
23-
onRowMouseEnter?: (...args: any[]) => any;
24-
onRowMouseLeave?: (...args: any[]) => any;
25-
onMouseEnter?: any;
26-
onMouseLeave?: any;
21+
onRowMouseEnter?: React.DOMAttributes<HTMLTableRowElement>["onMouseEnter"];
22+
onRowMouseLeave?: React.DOMAttributes<HTMLTableRowElement>["onMouseLeave"];
23+
onMouseEnter?: React.DOMAttributes<HTMLTableElement>["onMouseEnter"];
24+
onMouseLeave?: React.DOMAttributes<HTMLTableElement>["onMouseLeave"];
2725
};
2826

29-
const StyledTable = styled.table<any>(space, {
30-
borderCollapse: "collapse",
31-
width: "100%",
32-
background: "white",
33-
position: "relative",
34-
});
27+
const StyledTable = styled.table(
28+
{
29+
borderCollapse: "collapse",
30+
width: "100%",
31+
background: "white",
32+
position: "relative",
33+
},
34+
addStyledProps
35+
);
3536

36-
const BaseTable: React.FC<BaseTableProps> = ({
37+
export default function BaseTable<Row>({
3738
columns,
3839
rows,
3940
noRowsContent = "No records have been created for this table.",
40-
keyField = "id",
41+
keyField,
4142
id,
4243
loading,
43-
footerRows = [],
44-
rowHovers = true,
44+
footerRows,
45+
rowHovers,
4546
compact,
4647
className,
4748
stickyHeader,
48-
onRowMouseEnter = () => {},
49-
onRowMouseLeave = () => {},
49+
onRowMouseEnter,
50+
onRowMouseLeave,
5051
...props
51-
}) => (
52-
<StyledTable id={id} className={className} {...props}>
53-
<TableHead columns={columns} compact={compact} sticky={stickyHeader} />
54-
<TableBody
55-
columns={columns}
56-
rows={rows}
57-
keyField={keyField}
58-
noRowsContent={noRowsContent}
59-
loading={loading}
60-
rowHovers={rowHovers}
61-
compact={compact}
62-
onRowMouseLeave={onRowMouseLeave}
63-
onRowMouseEnter={onRowMouseEnter}
64-
/>
65-
{footerRows && (
66-
<TableFoot columns={columns} rows={footerRows} keyField={keyField} loading={loading} compact={compact} />
67-
)}
68-
</StyledTable>
69-
);
52+
}: BaseTableProps<Row>) {
53+
return (
54+
<StyledTable id={id} className={className} {...props}>
55+
<TableHead columns={columns} compact={compact} sticky={stickyHeader} />
56+
<TableBody
57+
columns={columns}
58+
rows={rows}
59+
keyField={keyField}
60+
noRowsContent={noRowsContent}
61+
loading={loading}
62+
rowHovers={rowHovers}
63+
compact={compact}
64+
onRowMouseLeave={onRowMouseLeave}
65+
onRowMouseEnter={onRowMouseEnter}
66+
/>
67+
{footerRows && (
68+
<TableFoot columns={columns} rows={footerRows} keyField={keyField} loading={loading} compact={compact} />
69+
)}
70+
</StyledTable>
71+
);
72+
}
7073

71-
BaseTable.propTypes = {
72-
...propTypes.space,
73-
columns: PropTypes.any,
74-
rows: PropTypes.any,
75-
noRowsContent: PropTypes.string,
76-
keyField: PropTypes.string,
77-
id: PropTypes.string,
78-
loading: PropTypes.bool,
79-
footerRows: rowsPropType,
80-
rowHovers: PropTypes.bool,
81-
compact: PropTypes.bool,
82-
className: PropTypes.string,
83-
stickyHeader: PropTypes.bool,
84-
onRowMouseEnter: PropTypes.func,
85-
onRowMouseLeave: PropTypes.func,
86-
};
74+
const noop = () => {};
8775

8876
BaseTable.defaultProps = {
8977
noRowsContent: "No records have been created for this table.",
9078
keyField: "id",
91-
id: undefined,
9279
loading: false,
9380
footerRows: [],
9481
rowHovers: true,
9582
compact: false,
96-
className: undefined,
9783
stickyHeader: false,
98-
onRowMouseEnter: () => {},
99-
onRowMouseLeave: () => {},
84+
onRowMouseEnter: noop,
85+
onRowMouseLeave: noop,
10086
};
101-
102-
export default BaseTable;

src/Table/SortingColumnHeader.story.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react";
22
import { action } from "@storybook/addon-actions";
3-
import { withKnobs, text, boolean } from "@storybook/addon-knobs";
3+
import { text, boolean } from "@storybook/addon-knobs";
44
import { Table } from "../index";
55

66
export default {

src/Table/StatefulTable.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React, { Component } from "react";
2+
import PropTypes from "prop-types";
3+
import propTypes from "@styled-system/prop-types";
24
import { Pagination } from "../Pagination";
35
import { getSubset } from "../utils/subset";
46
import BaseTable, { BaseTableProps } from "./BaseTable";
57
import { addExpandableControl } from "./addExpandableControl";
68
import { addSelectableControl } from "./addSelectableControl";
79

8-
export type StatefulTableProps = BaseTableProps & {
10+
export type StatefulTableProps = BaseTableProps<unknown> & {
911
selectedRows?: string[];
1012
onRowSelectionChange?: (...args: any[]) => any;
1113
onRowExpansionChange?: (...args: any[]) => any;
@@ -224,7 +226,33 @@ class StatefulTable extends Component<StatefulTableProps, StatefulTableState> {
224226
render() {
225227
const { paginatedRows, currentPage } = this.state;
226228
const { rowsPerPage, paginationProps, paginationCss } = this.props;
227-
const baseTableProps = getSubset(this.getControlProps(), BaseTable.propTypes);
229+
const baseTableProps = getSubset(this.getControlProps(), {
230+
...propTypes.space,
231+
columns: PropTypes.any,
232+
rows: PropTypes.any,
233+
noRowsContent: PropTypes.string,
234+
keyField: PropTypes.string,
235+
id: PropTypes.string,
236+
loading: PropTypes.bool,
237+
rowHovers: PropTypes.bool,
238+
compact: PropTypes.bool,
239+
className: PropTypes.string,
240+
stickyHeader: PropTypes.bool,
241+
onRowMouseEnter: PropTypes.func,
242+
onRowMouseLeave: PropTypes.func,
243+
footerRows: PropTypes.arrayOf(
244+
PropTypes.objectOf(
245+
PropTypes.oneOfType([
246+
PropTypes.number,
247+
PropTypes.string,
248+
PropTypes.bool,
249+
PropTypes.func,
250+
PropTypes.node,
251+
PropTypes.shape({}),
252+
])
253+
)
254+
),
255+
});
228256
return (
229257
<>
230258
<BaseTable {...baseTableProps} />

src/Table/StyledTh.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import styled, { CSSObject } from "styled-components";
1+
import styled, { CSSObject, CSSProperties } from "styled-components";
22
import { DefaultNDSThemeType } from "../theme.type";
3+
34
const stickyStyles = (theme: DefaultNDSThemeType): CSSObject => ({
45
position: "sticky",
56
top: 0,
@@ -9,13 +10,15 @@ const stickyStyles = (theme: DefaultNDSThemeType): CSSObject => ({
910
});
1011

1112
type StyledThProps = {
12-
width?: any;
13+
width?: CSSProperties["width"];
1314
compact?: boolean;
1415
theme?: DefaultNDSThemeType;
15-
sticky?: any;
16+
sticky?: boolean;
1617
};
17-
const StyledTh = styled.th(({ width, compact, theme, sticky }: StyledThProps): CSSObject => {
18+
19+
const StyledTh = styled.th<StyledThProps>(({ width, compact, theme, sticky }) => {
1820
const padding = compact ? theme.space.x1 : theme.space.x2;
21+
1922
return {
2023
fontWeight: "normal",
2124
textAlign: "left",
@@ -29,4 +32,5 @@ const StyledTh = styled.th(({ width, compact, theme, sticky }: StyledThProps): C
2932
width: width || "auto",
3033
};
3134
});
35+
3236
export default StyledTh;

src/Table/Table.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
import React from "react";
2-
import StatefulTable from "./StatefulTable";
2+
import StatefulTable, { StatefulTableProps } from "./StatefulTable";
33
import BaseTable from "./BaseTable";
44
import SortingColumnHeader from "./SortingColumnHeader";
5-
import { StatefulTableProps } from "./StatefulTable";
6-
import { ColumnType, RowType, CellInfoType } from "./Table.types";
75

8-
export type TableProps = StatefulTableProps;
9-
export type TableColumnType = ColumnType;
10-
export type TableRowType = RowType;
11-
export type TableCellInfoType = CellInfoType;
12-
13-
const Table = ({
6+
export default function Table({
147
hasSelectableRows,
158
rowsPerPage,
169
hasExpandableRows,
@@ -23,8 +16,8 @@ const Table = ({
2316
paginationCss,
2417
paginationProps,
2518
...props
26-
}: TableProps) =>
27-
hasSelectableRows || rowsPerPage || hasExpandableRows ? (
19+
}: StatefulTableProps) {
20+
return hasSelectableRows || rowsPerPage || hasExpandableRows ? (
2821
<StatefulTable
2922
hasExpandableRows={hasExpandableRows}
3023
hasSelectableRows={hasSelectableRows}
@@ -42,6 +35,6 @@ const Table = ({
4235
) : (
4336
<BaseTable {...props} />
4437
);
45-
Table.SortingHeader = SortingColumnHeader;
38+
}
4639

47-
export default Table;
40+
Table.SortingHeader = SortingColumnHeader;

src/Table/Table.types.ts

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,27 @@
11
import type { Key } from "react";
2-
import PropTypes from "prop-types";
32

4-
export type RowType = unknown;
5-
6-
export interface CellInfoType {
3+
export interface CellInfoType<Row extends unknown> {
74
cellData: unknown;
8-
column: ColumnType;
9-
row: RowType;
5+
column: ColumnType<Row>[];
6+
row: Row;
107
}
118

129
interface ColumnInfoType {
1310
align?: ColumnAlignment;
1411
label: string;
1512
dataKey?: Key;
1613
width?: string | number;
14+
metadata?: Record<string, unknown>; // explore the ability to infer the type of metadata using a generic
1715
}
1816

19-
type ColumnAlignment = "left" | "right" | "center";
17+
export type ColumnAlignment = "left" | "right" | "center";
2018

21-
export type ColumnType = {
19+
export type ColumnType<Row extends unknown> = {
2220
align?: ColumnAlignment;
2321
label?: string;
24-
cellFormatter?: (cell: CellInfoType) => React.ReactNode;
25-
cellRenderer?: (cell: CellInfoType) => React.ReactNode;
22+
cellFormatter?: (cell: CellInfoType<Row>) => React.ReactNode;
23+
cellRenderer?: (cell: CellInfoType<Row>) => React.ReactNode;
2624
headerRenderer?: (column: ColumnInfoType) => React.ReactNode;
2725
headerFormatter?: (column: ColumnInfoType) => React.ReactNode;
2826
width?: string | number;
2927
} & ({ key: Key; dataKey?: never | undefined } | { dataKey: Key; key?: never | undefined });
30-
31-
export type Columns = ColumnType[];
32-
33-
export const columnPropType = PropTypes.shape({
34-
align: PropTypes.oneOf(["right", "left", "center"]),
35-
label: PropTypes.string,
36-
dataKey: PropTypes.oneOf([PropTypes.string, PropTypes.number]),
37-
key: PropTypes.oneOf([PropTypes.string, PropTypes.number]),
38-
cellFormatter: PropTypes.func,
39-
cellRenderer: PropTypes.func,
40-
headerRenderer: PropTypes.func,
41-
width: PropTypes.string,
42-
});
43-
44-
export const rowPropType = PropTypes.objectOf(
45-
PropTypes.oneOfType([
46-
PropTypes.number,
47-
PropTypes.string,
48-
PropTypes.bool,
49-
PropTypes.func,
50-
PropTypes.node,
51-
PropTypes.shape({}),
52-
])
53-
);
54-
55-
export const columnsPropType = PropTypes.arrayOf(columnPropType);
56-
57-
export const rowsPropType = PropTypes.arrayOf(rowPropType);

0 commit comments

Comments
 (0)