Skip to content

Commit

Permalink
fix: allow passing metadata to the table columns
Browse files Browse the repository at this point in the history
  • Loading branch information
haideralsh committed May 8, 2024
1 parent fdbfb6b commit d18b814
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 128 deletions.
9 changes: 9 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
"parser": "@typescript-eslint/parser",
"extends": ["@nulogy/nulogy"],
"rules": {
"@typescript-eslint/ban-types": [
"error",
{
"extendDefaults": true,
"types": {
"{}": false
}
}
],
"jsx-a11y/label-has-associated-control": [
2,
{
Expand Down
23 changes: 14 additions & 9 deletions src/SortingTable/SortingTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { Table } from "../Table";
import type { TableProps } from "../Table";
import type { RowType, ColumnType } from "../Table/Table.types";

type SortingTableProps = TableProps & {
type SortingTableProps<ColumnMetadata> = TableProps<ColumnMetadata> & {
initialSortColumn: string;
};

type SortableColumnType = ColumnType & { numeric?: boolean };
type SortableColumnType<ColumnMetadata> = ColumnType<ColumnMetadata> & { numeric?: boolean };

type SortState = {
ascending: boolean;
Expand All @@ -17,26 +17,31 @@ type SortState = {
const numericAlphabeticalSort = (a, b, numeric) =>
String(a).localeCompare(b, undefined, { numeric, sensitivity: "base" });

const applySort = (rows: RowType[], sortColumn: string, columns: SortableColumnType[]) =>
[...rows].sort((a, b) => {
function applySort<ColumnMetadata>(rows: RowType[], sortColumn: string, columns: SortableColumnType<ColumnMetadata>[]) {
return [...rows].sort((a, b) => {
const column = columns.find((col) => col.dataKey === sortColumn);
const { numeric } = column;

return numericAlphabeticalSort(a[sortColumn], b[sortColumn], numeric);
});
}

const sortRows = (rows: RowType[], columns: SortableColumnType[], sortState: SortState) => {
function sortRows<ColumnMetadata>(
rows: RowType[],
columns: SortableColumnType<ColumnMetadata>[],
sortState: SortState
) {
const sortedRows = applySort(rows, sortState.sortColumn, columns);

return sortState.ascending ? sortedRows : sortedRows.reverse();
};
}

const SortingTable: React.FC<SortingTableProps> = ({
function SortingTable<ColumnMetadata>({
columns: incomingColumns,
rows: incomingRows,
initialSortColumn,
...props
}) => {
}: SortingTableProps<ColumnMetadata>) {
const [sortState, setSortState] = useState<SortState>({
ascending: true,
sortColumn: initialSortColumn,
Expand Down Expand Up @@ -77,6 +82,6 @@ const SortingTable: React.FC<SortingTableProps> = ({
const columns = incomingColumns.map((column) => transformColumn(column));

return <Table columns={columns} rows={rows} {...props} />;
};
}

export default SortingTable;
98 changes: 40 additions & 58 deletions src/Table/BaseTable.story.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* eslint-disable react/prop-types */
import React from "react";
import styled from "styled-components";
import { boolean, text } from "@storybook/addon-knobs";
import { action } from "@storybook/addon-actions";
import { Box, DropdownButton, DropdownMenu, Button, Text } from "..";
import { Box, DropdownButton, DropdownMenu, Button, Text, Flex } from "..";
import { getMockRows, mockColumns } from "./Table.mock-utils";
import { Columns } from "./Table.types";
import { Table } from ".";
Expand All @@ -12,8 +11,6 @@ const dateToString = ({ cellData }) => {
return new Date(cellData).toUTCString().split(" ").splice(0, 4).join(" ");
};

const buttonRenderer = ({ label }) => <Button onClick={action("button clicked")}>{label}</Button>;

const sectionRow = ({ cellData }) => (
<Box bg="lightBlue" py="x1" px="x2">
<Text fontWeight="bold" color="blackBlue">
Expand All @@ -31,7 +28,7 @@ const dropdownCellRenderer = ({ cellData }) => (
</Box>
);

const columns: Columns = [
const columns: Columns<{}> = [
{ label: "Date", dataKey: "date" },
{ label: "Expected Quantity", dataKey: "expectedQuantity" },
{ label: "Actual Quantity", dataKey: "actualQuantity", align: "right" },
Expand All @@ -43,6 +40,7 @@ const columnsWithWidths = [
{ label: "Actual Quantity", dataKey: "actualQuantity" },
{ label: "Note", dataKey: "note", width: "50%" },
];

const rowData = [
{
date: "2019-10-01",
Expand Down Expand Up @@ -185,7 +183,7 @@ const columnsWithFormatter = [
{ label: "Actual Quantity", dataKey: "actualQuantity" },
];

const columnsWithAlignment: Columns = [
const columnsWithAlignment: Columns<{}> = [
{ label: "Date", dataKey: "date" },
{ label: "Expected Eaches", dataKey: "expectedQuantity" },
{ label: "Actual Eaches", dataKey: "actualQuantity", align: "right" },
Expand All @@ -197,13 +195,6 @@ const getColumnsWithCellRenderer = (cellRenderer) => [
{ label: "", dataKey: "actualQuantity", cellRenderer },
];

const getColumnsWithHeaderFormatter = (headerFormatter) => [
{ label: "Date", dataKey: "date" },
{ label: "Expected Quantity", dataKey: "expectedQuantity" },
{ label: "Actual Quantity", dataKey: "actualQuantity" },
{ label: "Add record", dataKey: "c4", headerFormatter },
];

const footerRowData = [
{
date: "Total",
Expand Down Expand Up @@ -280,10 +271,6 @@ export const WithData = () => (
/>
);

WithData.story = {
name: "with data",
};

export const WithNoData = () => (
<Table
columns={columns}
Expand All @@ -293,10 +280,6 @@ export const WithNoData = () => (
/>
);

WithNoData.story = {
name: "with no data",
};

export const WithStickyHeader = () => (
<Box mt="x4">
<Table
Expand All @@ -311,10 +294,6 @@ export const WithStickyHeader = () => (
</Box>
);

WithStickyHeader.story = {
name: "with sticky header",
};

export const WithLotsOfRowsAndColumns = () => (
<Table
columns={mockColumns}
Expand All @@ -325,50 +304,58 @@ export const WithLotsOfRowsAndColumns = () => (
/>
);

WithLotsOfRowsAndColumns.story = {
name: "with lots of rows and columns",
};

export const WithCustomColumnWidths = () => <Table columns={columnsWithWidths} rows={rowDataWithWidths} />;

WithCustomColumnWidths.story = {
name: "with custom column widths",
};

export const WithACustomCellComponent = () => (
<Table columns={getColumnsWithCellRenderer(dropdownCellRenderer)} rows={rowData} />
);

WithACustomCellComponent.story = {
name: "with a custom cell component",
};

export const WithCellAlignment = () => <Table columns={columnsWithAlignment} rows={rowData} />;

WithCellAlignment.story = {
name: "with cell alignment",
};

export const WithACellFormatter = () => <Table columns={columnsWithFormatter} rows={rowData} />;

WithACellFormatter.story = {
name: "with a cell formatter",
};

export const WithACustomColumnLabelComponent = () => (
<Table columns={getColumnsWithHeaderFormatter(buttonRenderer)} rows={rowData} />
<Table
columns={[
{ label: "Date", dataKey: "date" },
{ label: "Expected Quantity", dataKey: "expectedQuantity" },
{ label: "Actual Quantity", dataKey: "actualQuantity" },
{
label: "Add record",
dataKey: "c4",
headerFormatter: ({ label }) => <Button onClick={action("button clicked")}>{label}</Button>,
},
]}
rows={rowData}
/>
);

WithACustomColumnLabelComponent.story = {
name: "with a custom column label component",
};
export const WithMetadata = () => (
<Table
columns={[
{ label: "Date", dataKey: "date" },
{ label: "Expected Quantity", dataKey: "expectedQuantity" },
{ label: "Actual Quantity", dataKey: "actualQuantity" },
{
label: "Add record",
dataKey: "c4",
metadata: { helpText: "Allows adding a new record" },
headerFormatter: ({ label, metadata }) => (
<Flex flexDirection="column">
<Text>{label}</Text>
<Text fontSize="small" fontWeight="medium" color="midGrey">
{metadata.helpText}
</Text>
</Flex>
),
},
]}
rows={rowData}
/>
);

export const WithFullWidthSection = () => <Table columns={columns} rows={rowDataWithSections} />;

WithFullWidthSection.story = {
name: "with full width section",
};

export const WithAFooter = () => (
<>
<Table
Expand All @@ -389,11 +376,6 @@ export const WithAFooter = () => (
</>
);

WithAFooter.story = {
name: "with a footer",
};
/* eslint-enable react/prop-types */

const TableWithBorderedRows = styled(Table)`
border-collapse: collapse;
Expand Down
46 changes: 24 additions & 22 deletions src/Table/BaseTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import TableBody from "./TableBody";
import TableFoot from "./TableFoot";
import { rowsPropType, RowType, Columns } from "./Table.types";

export type BaseTableProps = {
columns: Columns;
export type BaseTableProps<ColumnMetaData> = {
columns: Columns<ColumnMetaData>;
rows: RowType[];
noRowsContent?: string;
keyField?: string;
Expand All @@ -33,7 +33,7 @@ const StyledTable = styled.table<any>(space, {
position: "relative",
});

const BaseTable: React.FC<BaseTableProps> = ({
function BaseTable<ColumnMetaData>({
columns,
rows,
noRowsContent = "No records have been created for this table.",
Expand All @@ -48,25 +48,27 @@ const BaseTable: React.FC<BaseTableProps> = ({
onRowMouseEnter = () => {},
onRowMouseLeave = () => {},
...props
}) => (
<StyledTable id={id} className={className} {...props}>
<TableHead columns={columns} compact={compact} sticky={stickyHeader} />
<TableBody
columns={columns}
rows={rows}
keyField={keyField}
noRowsContent={noRowsContent}
loading={loading}
rowHovers={rowHovers}
compact={compact}
onRowMouseLeave={onRowMouseLeave}
onRowMouseEnter={onRowMouseEnter}
/>
{footerRows && (
<TableFoot columns={columns} rows={footerRows} keyField={keyField} loading={loading} compact={compact} />
)}
</StyledTable>
);
}: BaseTableProps<ColumnMetaData>) {
return (
<StyledTable id={id} className={className} {...props}>
<TableHead columns={columns} compact={compact} sticky={stickyHeader} />
<TableBody
columns={columns}
rows={rows}
keyField={keyField}
noRowsContent={noRowsContent}
loading={loading}
rowHovers={rowHovers}
compact={compact}
onRowMouseLeave={onRowMouseLeave}
onRowMouseEnter={onRowMouseEnter}
/>
{footerRows && (
<TableFoot columns={columns} rows={footerRows} keyField={keyField} loading={loading} compact={compact} />
)}
</StyledTable>
);
}

BaseTable.propTypes = {
...propTypes.space,
Expand Down
6 changes: 3 additions & 3 deletions src/Table/StatefulTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import BaseTable, { BaseTableProps } from "./BaseTable";
import { addExpandableControl } from "./addExpandableControl";
import { addSelectableControl } from "./addSelectableControl";

export type StatefulTableProps = BaseTableProps & {
export type StatefulTableProps<ColumnMetaData> = BaseTableProps<ColumnMetaData> & {
selectedRows?: string[];
onRowSelectionChange?: (...args: any[]) => any;
onRowExpansionChange?: (...args: any[]) => any;
Expand Down Expand Up @@ -36,7 +36,7 @@ type StatefulTableState = {
currentPage: number;
paginatedRows: any;
};
class StatefulTable extends Component<StatefulTableProps, StatefulTableState> {
class StatefulTable<ColumnMetaData> extends Component<StatefulTableProps<ColumnMetaData>, StatefulTableState> {
static defaultProps = {
...BaseTable.defaultProps,
hasSelectableRows: false,
Expand Down Expand Up @@ -208,7 +208,7 @@ class StatefulTable extends Component<StatefulTableProps, StatefulTableState> {
onRowExpansionChange: this.onExpandRow,
expandedRows,
};
const props: StatefulTableProps = {
const props: StatefulTableProps<ColumnMetaData> = {
...this.props,
rows: this.rowsByPageSelector(currentPage) || [],
...(hasSelectableRows && selectionConfig),
Expand Down
15 changes: 8 additions & 7 deletions src/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import SortingColumnHeader from "./SortingColumnHeader";
import { StatefulTableProps } from "./StatefulTable";
import { ColumnType, RowType, CellInfoType } from "./Table.types";

export type TableProps = StatefulTableProps;
export type TableColumnType = ColumnType;
export type TableProps<ColumnMetadata> = StatefulTableProps<ColumnMetadata>;
export type TableColumnType<ColumnMetadata> = ColumnType<ColumnMetadata>;
export type TableRowType = RowType;
export type TableCellInfoType = CellInfoType;
export type TableCellInfoType<ColumnMetadata> = CellInfoType<ColumnMetadata>;

const Table = ({
function Table<ColumnMetadata>({
hasSelectableRows,
rowsPerPage,
hasExpandableRows,
Expand All @@ -23,9 +23,9 @@ const Table = ({
paginationCss,
paginationProps,
...props
}: TableProps) =>
hasSelectableRows || rowsPerPage || hasExpandableRows ? (
<StatefulTable
}: TableProps<ColumnMetadata>) {
return hasSelectableRows || rowsPerPage || hasExpandableRows ? (
<StatefulTable<ColumnMetadata>
hasExpandableRows={hasExpandableRows}
hasSelectableRows={hasSelectableRows}
onRowExpansionChange={onRowExpansionChange}
Expand All @@ -42,6 +42,7 @@ const Table = ({
) : (
<BaseTable {...props} />
);
}
Table.SortingHeader = SortingColumnHeader;

export default Table;
Loading

0 comments on commit d18b814

Please sign in to comment.