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

feat!: option to add row count on tables (#284) #285

Merged
merged 3 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { TableCell as MTableCell } from "@mui/material";
import { TableCell } from "@mui/material";
import { flexRender, Row, RowData, Table } from "@tanstack/react-table";
import React, { Fragment } from "react";
import { getTableCellPadding } from "../../../../../Table/components/TableCell/common/utils";
import {
getTableCellAlign,
getTableCellPadding,
} from "../../../../../Table/components/TableCell/common/utils";
import { TableRow } from "../../../../../Table/components/TableRow/tableRow.styles";
import { TableView } from "../../table";

Expand Down Expand Up @@ -31,13 +34,14 @@ export const TableRows = <T extends RowData>({
if (cell.getIsAggregated()) return null; // Display of aggregated cells is currently not supported.
if (cell.getIsPlaceholder()) return null; // Display of placeholder cells is currently not supported.
return (
<MTableCell
<TableCell
key={cell.id}
align={getTableCellAlign(cell.column)}
padding={getTableCellPadding(cell.column.id)}
size={tableCellSize}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</MTableCell>
</TableCell>
);
})}
</TableRow>
Expand Down
9 changes: 7 additions & 2 deletions src/components/Detail/components/Table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import {
useBreakpointHelper,
} from "../../../../hooks/useBreakpointHelper";
import { TABLET } from "../../../../theme/common/breakpoints";
import { COLUMN_DEF } from "../../../Table/common/columnDef";
import { ROW_DIRECTION } from "../../../Table/common/entities";
import { TableHead } from "../../../Table/components/TableHead/tableHead";
import { ROW_POSITION } from "../../../Table/features/RowPosition/constants";
import { ROW_PREVIEW } from "../../../Table/features/RowPreview/constants";
import { GridTable } from "../../../Table/table.styles";
import { generateColumnDefinitions } from "./common/utils";
Expand Down Expand Up @@ -56,8 +58,11 @@ export const Table = <T extends RowData>({
const { stickyHeader = false } = table || {};
const { sx: tableContainerSx } = tableContainer || {};
const tableInstance = useReactTable({
_features: [ROW_PREVIEW],
columns: generateColumnDefinitions(columns),
_features: [ROW_POSITION, ROW_PREVIEW],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New custom feature ROW_POSITION.

columns: generateColumnDefinitions([
COLUMN_DEF.ROW_POSITION as ColumnDef<T>,
...columns,
]),
data: items,
enableSorting: false,
getCoreRowModel: getCoreRowModel(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { FormControl as MFormControl } from "@mui/material";
import { mediaTabletUp } from "../../../../styles/common/mixins/breakpoints";
import {
inkMain,
smokeLightest,
smokeMain,
} from "../../../../styles/common/mixins/colors";
import { inkMain, smokeMain } from "../../../../styles/common/mixins/colors";
import { textBodyLarge500 } from "../../../../styles/common/mixins/fonts";
import { ThemeProps } from "../../../../theme/theme";
import {
Expand Down Expand Up @@ -75,8 +71,6 @@ export const TableFormControl = styled(FormControl)`

.MuiTable-root {
th {
background-color: ${smokeLightest};

.MuiFormControlLabel-label {
font: inherit;
}
Expand Down
19 changes: 19 additions & 0 deletions src/components/Table/common/columnDef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ColumnDef, RowData } from "@tanstack/react-table";
import { TABLE_CELL_PROPS } from "../../../styles/common/mui/tableCell";
import { ACCESSOR_KEYS } from "../../TableCreator/common/constants";
import { RowPositionCell } from "../components/TableCell/components/RowPositionCell/rowPositionCell";

export const COLUMN_DEF: Record<string, ColumnDef<RowData>> = {
ROW_POSITION: {
cell: RowPositionCell,
enableHiding: false,
enableSorting: false,
header: "",
id: ACCESSOR_KEYS.ROW_POSITION,
meta: {
align: TABLE_CELL_PROPS.ALIGN.RIGHT,
header: "",
width: "max-content",
},
},
};
3 changes: 1 addition & 2 deletions src/components/Table/common/gridTable.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ export const GridTable = styled(MTable, {
background-color: ${white};
}

td,
th {
td {
background-color: inherit;
}

Expand Down
27 changes: 22 additions & 5 deletions src/components/Table/components/TableCell/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { TableCellProps as MTableCellProps } from "@mui/material";
import { CoreCell, CoreHeader, RowData } from "@tanstack/react-table";
import { TableCellProps } from "@mui/material";
import { Column, CoreCell, CoreHeader, RowData } from "@tanstack/react-table";
import { TABLE_CELL_PROPS } from "../../../../../styles/common/mui/tableCell";
import { ACCESSOR_KEYS } from "../../../../TableCreator/common/constants";

/**
* Returns table cell alignment based on the cell.
* @param column - Column.
* @returns table cell alignment.
*/
export function getTableCellAlign<T extends RowData, TValue>(
column: Column<T, TValue>
): TableCellProps["align"] {
return column.columnDef.meta?.align;
}

/**
* Returns table cell padding based on the cell ID.
* @param id - Cell ID.
* @returns table cell padding.
*/
export function getTableCellPadding<T extends RowData, TValue>(
id: CoreHeader<T, TValue>["id"] | CoreCell<T, TValue>["id"]
): MTableCellProps["padding"] {
if (id === ACCESSOR_KEYS.SELECT) {
return "checkbox";
): TableCellProps["padding"] {
switch (id) {
case ACCESSOR_KEYS.ROW_POSITION:
return TABLE_CELL_PROPS.PADDING.NORMAL;
case ACCESSOR_KEYS.SELECT:
return TABLE_CELL_PROPS.PADDING.CHECKBOX;
default:
return TABLE_CELL_PROPS.PADDING.NORMAL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TypographyOwnProps } from "@mui/material";
import { VARIANT } from "../../../../../../styles/common/mui/typography";

export const TYPOGRAPHY_PROPS: Partial<TypographyOwnProps> = {
sx: { marginRight: -2, paddingLeft: 2 },
variant: VARIANT.INHERIT,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Typography } from "@mui/material";
import { CellContext, RowData } from "@tanstack/react-table";
import React from "react";
import { BaseComponentProps } from "../../../../../types";
import { TYPOGRAPHY_PROPS } from "./constants";

export const RowPositionCell = <TData extends RowData, TValue>({
className,
...cellContext
}: BaseComponentProps & CellContext<TData, TValue>): JSX.Element => {
return (
<Typography {...TYPOGRAPHY_PROPS} className={className} component="div">
{cellContext.row.getRowPosition()}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

RowPosition is now a custom feature, and we now have getRowPostiion on the cellContext.row.

</Typography>
);
};
12 changes: 8 additions & 4 deletions src/components/Table/components/TableHead/tableHead.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import {
TableCell as MTableCell,
TableHead as MTableHead,
TableRow as MTableRow,
TableCell,
TableSortLabel,
} from "@mui/material";
import { flexRender, RowData, Table } from "@tanstack/react-table";
import React, { Fragment } from "react";
import { ROW_DIRECTION } from "../../common/entities";
import { getTableSortLabelProps } from "../../common/utils";
import { getTableCellPadding } from "../TableCell/common/utils";
import {
getTableCellAlign,
getTableCellPadding,
} from "../TableCell/common/utils";

export interface TableHeadProps<T extends RowData> {
rowDirection: ROW_DIRECTION;
Expand All @@ -34,8 +37,9 @@ export const TableHead = <T extends RowData>({
},
} = header;
return header.column.getIsGrouped() ? null : (
<MTableCell
<TableCell
key={header.id}
align={getTableCellAlign(header.column)}
padding={getTableCellPadding(header.id)}
>
{header.column.getCanSort() && enableSortingInteraction ? (
Expand All @@ -53,7 +57,7 @@ export const TableHead = <T extends RowData>({
header.getContext()
)
)}
</MTableCell>
</TableCell>
);
})}
</MTableRow>
Expand Down
12 changes: 8 additions & 4 deletions src/components/Table/components/TableRows/tableRows.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { TableCell as MTableCell } from "@mui/material";
import { TableCell } from "@mui/material";
import { flexRender, Row, RowData, Table } from "@tanstack/react-table";
import { Virtualizer } from "@tanstack/react-virtual";
import React, { Fragment } from "react";
import { getTableCellPadding } from "../TableCell/common/utils";
import {
getTableCellAlign,
getTableCellPadding,
} from "../TableCell/common/utils";
import { TableRow } from "../TableRow/tableRow.styles";

export interface TableRowsProps<T extends RowData> {
Expand Down Expand Up @@ -30,12 +33,13 @@ export const TableRows = <T extends RowData>({
ref={virtualizer.measureElement}
>
{row.getVisibleCells().map((cell) => (
<MTableCell
<TableCell
key={cell.id}
align={getTableCellAlign(cell.column)}
padding={getTableCellPadding(cell.column.id)}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</MTableCell>
</TableCell>
))}
</TableRow>
);
Expand Down
34 changes: 34 additions & 0 deletions src/components/Table/features/RowPosition/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
Cell,
Column,
Row,
RowData,
RowModel,
Table,
TableFeature,
TableState,
} from "@tanstack/react-table";
import { InitialTableState } from "@tanstack/table-core/src/types";
import { getRowModel, getRowPosition, initInitialState } from "./utils";

export const ROW_POSITION: TableFeature = {
createCell: <T extends RowData, TValue>(
Copy link
Contributor Author

@frano-m frano-m Nov 25, 2024

Choose a reason for hiding this comment

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

We extend cellContext.row with the function getRowPosition. We will call this function from the RowPositionCell component. The value returned will be the row position.

cell: Cell<T, TValue>,
column: Column<T>,
row: Row<T>,
table: Table<T>
): void => {
row.getRowPosition = (): number => {
return getRowPosition(row.id, table);
};
},
createTable: <T extends RowData>(table: Table<T>): void => {
const originalGetRowModel = table.getRowModel.bind(table);
table.getRowModel = (): RowModel<T> => {
return getRowModel(table, originalGetRowModel);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We update the getRowModel; each row in the getRowModel.rowsById will be updated to include the getRowPosition function. Doing this in the feature, allows us to efficiently retrieve the getRowPosition function for the given row.

};
},
getInitialState: (initialState?: InitialTableState): Partial<TableState> => {
return initInitialState(initialState);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The default initial state will be to hide the row position column.

},
};
3 changes: 3 additions & 0 deletions src/components/Table/features/RowPosition/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface RowPositionRow {
getRowPosition: () => number;
}
80 changes: 80 additions & 0 deletions src/components/Table/features/RowPosition/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
InitialTableState,
RowData,
RowModel,
Table,
TableState,
} from "@tanstack/react-table";
import { ACCESSOR_KEYS } from "../../../TableCreator/common/constants";
import { DEFAULT_PAGINATION } from "../constants";

/**
* Returns row model, with getter for row position.
* @param table - Table.
* @param getRowModel - Table getRowModel function.
* @returns row model.
*/
export function getRowModel<T extends RowData>(
table: Table<T>,
getRowModel: Table<T>[`getRowModel`]
): RowModel<T> {
const rowModel = getRowModel();
rowModel.rows.forEach(({ id }, i) => {
rowModel.rowsById[id].getRowPosition = (): number =>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding the function getRowPosition onto rowModel.rowsById[id] where rowsById is part of TanStack's rowModel API, which maps each row to its id.

calculateRowPosition(table, i);
});
return rowModel;
}

/**
* Returns the position of the row in the table.
* @param rowId - Row ID.
* @param table - Table.
* @returns row position.
*/
export function getRowPosition<T extends RowData>(
rowId: string,
table: Table<T>
): number {
const { getRowModel } = table;
const { rowsById } = getRowModel();
return rowsById[rowId].getRowPosition();
}

/**
* Calculates the position of the row in the table.
* @param table - Table.
* @param index - Row index.
* @returns row position.
*/
function calculateRowPosition<T extends RowData>(
table: Table<T>,
index: number
): number {
const { getState } = table;
const {
pagination: { pageIndex, pageSize },
} = getState();
return pageIndex * pageSize + (index + 1);
}

/**
* Returns the initial table state.
* @param initialState - Initial state.
* @returns initial state.
*/
export function initInitialState(
initialState?: InitialTableState
): Partial<TableState> {
return {
...initialState,
columnVisibility: {
[ACCESSOR_KEYS.ROW_POSITION]: false,
...initialState?.columnVisibility,
},
pagination: {
...DEFAULT_PAGINATION,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Custom feature initial state expects pagination to be defined pageIndex and pageSize. These values are initial state only, and will always be updated by table state as per usual.

...initialState?.pagination,
},
};
}
10 changes: 10 additions & 0 deletions src/components/Table/features/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PaginationState } from "@tanstack/react-table";

/**
* Default TanStack pagination state.
* See https://tanstack.com/table/latest/docs/guide/custom-features#getinitialstate.
*/
export const DEFAULT_PAGINATION: PaginationState = {
pageIndex: 0,
pageSize: 10,
};
3 changes: 2 additions & 1 deletion src/components/Table/features/entities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RowData } from "@tanstack/react-table";
import { RowPositionRow } from "./RowPosition/types";
import {
RowPreviewInstance,
RowPreviewOptions,
Expand All @@ -9,5 +10,5 @@ import {
export type CustomFeatureInitialTableState = Partial<RowPreviewTableState>;
export type CustomFeatureInstance<T extends RowData> = RowPreviewInstance<T>;
export type CustomFeatureOptions = RowPreviewOptions;
export type CustomFeatureRow = RowPreviewRow;
export type CustomFeatureRow = RowPositionRow & RowPreviewRow;
export type CustomFeatureTableState = RowPreviewTableState;
Loading