From c4b1c7fef84193552ae0d03b23d8298f22be1220 Mon Sep 17 00:00:00 2001 From: Corey Martin Date: Mon, 4 Nov 2024 15:11:29 -0800 Subject: [PATCH] [ui] Upgrade react-table to v8 (#13219) GitOrigin-RevId: 65955114b5d293e84d15b8e62122ba6f3160fc10 --- packages/ui/package.json | 3 +- .../DataManagerTable/DataManagerTable.tsx | 36 ++--- .../components/DataManagerTable/filters.ts | 4 +- packages/ui/src/components/Table/Table.tsx | 127 ++++++++++-------- 4 files changed, 92 insertions(+), 78 deletions(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index 39d035d8..f082c158 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -62,6 +62,7 @@ "@rollup/plugin-url": "^8.0.2", "@simbathesailor/use-what-changed": "^2.0.0", "@svgr/core": "^8.1.0", + "@tanstack/react-table": "^8.20.5", "@wojtekmaj/react-daterange-picker": "^5.5.0", "@wojtekmaj/react-datetimerange-picker": "^5.5.0", "@zxing/browser": "^0.1.1", @@ -81,7 +82,6 @@ "react-dom": "^18.1.0", "react-router-dom": "6.11.2", "react-select": "^5.4.0", - "react-table": "^7.8.0", "react-tooltip": "^5.10.1", "uuid": "^9.0.0" }, @@ -97,7 +97,6 @@ "@testing-library/user-event": "^14.4.3", "@types/jest": "^29.5.3", "@types/prismjs": "^1.26.0", - "@types/react-table": "^7.7.18", "esbuild": "0.19.1", "esbuild-plugin-copy": "^2.1.1", "esbuild-plugin-svgr": "^2.1.0", diff --git a/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx b/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx index 10afc633..e3b28743 100644 --- a/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx +++ b/packages/ui/src/components/DataManagerTable/DataManagerTable.tsx @@ -122,7 +122,7 @@ function initialFilterState>( filters.forEach((filter) => { state = { ...state, - [filter.accessor]: getDefaultFilterState(filter), + [filter.accessorKey]: getDefaultFilterState(filter), }; }); @@ -219,7 +219,7 @@ export function DataManagerTable< return (state: FilterState) => { setFilterStates((prevState) => ({ ...prevState, - [filter.accessor]: state, + [filter.accessorKey]: state, })); }; } @@ -249,14 +249,14 @@ export function DataManagerTable< ); if (validResult) { // Apply the result of the validation for refetching data - appliedFilterStates[filter.accessor] = validResult; + appliedFilterStates[filter.accessorKey] = validResult; // Update UI filter state as a result of applying if needed if (isIdFilterState(filterState)) { const appliedValues = (validResult as IdFilterState).appliedValues; const isApplied = !!appliedValues?.length; updateFilterState(filter)({ - ...filterStates[filter.accessor], + ...filterStates[filter.accessorKey], value: "", appliedValues: appliedValues ? [...appliedValues] : [], isApplied, @@ -298,14 +298,14 @@ export function DataManagerTable< updatedAppliedValues.push(value); const newFilterState = { - ...filterStates[filter.accessor], + ...filterStates[filter.accessorKey], value: "", appliedValues: updatedAppliedValues, isApplied: !!updatedAppliedValues.length, } as StringFilterState; // Apply the result of the validation for refetching data - appliedFilterStates[filter.accessor] = newFilterState; + appliedFilterStates[filter.accessorKey] = newFilterState; // Update UI filter state as a result of applying if needed updateFilterState(filter)(newFilterState); } else if (filterState.appliedValues?.length === 0) { @@ -316,14 +316,14 @@ export function DataManagerTable< } else if (isEnumFilterState(filterState)) { if (filterState.value) { const newFilterState = { - ...filterStates[filter.accessor], + ...filterStates[filter.accessorKey], value: "", appliedValues: filterState.appliedValues, isApplied: !!filterState.appliedValues?.length, } as EnumFilterState; // Apply the result of the validation for refetching data - appliedFilterStates[filter.accessor] = newFilterState; + appliedFilterStates[filter.accessorKey] = newFilterState; // Update UI filter state as a result of applying if needed updateFilterState(filter)(newFilterState); } else if (filterState.appliedValues?.length === 0) { @@ -477,10 +477,10 @@ export function DataManagerTable< updateFilterState={(state) => { setFilterStates((prevState) => ({ ...prevState, - [filter.accessor]: state, + [filter.accessorKey]: state, })); }} - state={filterStates[filter.accessor] as DateFilterState} + state={filterStates[filter.accessorKey] as DateFilterState} /> ); @@ -491,13 +491,13 @@ export function DataManagerTable< updateFilterState={(state) => { setFilterStates((prevState) => ({ ...prevState, - [filter.accessor]: state, + [filter.accessorKey]: state, })); }} options={filter.enumValues} label={filter.label} placeholder={filter.placeholder} - state={filterStates[filter.accessor] as EnumFilterState} + state={filterStates[filter.accessorKey] as EnumFilterState} isMulti={filter.isMulti} /> @@ -509,12 +509,12 @@ export function DataManagerTable< updateFilterState={(state) => { setFilterStates((prevState) => ({ ...prevState, - [filter.accessor]: state, + [filter.accessorKey]: state, })); }} label={filter.label} placeholder={filter.placeholder} - state={filterStates[filter.accessor] as StringFilterState} + state={filterStates[filter.accessorKey] as StringFilterState} /> ); @@ -525,12 +525,12 @@ export function DataManagerTable< updateFilterState={(state) => { setFilterStates((prevState) => ({ ...prevState, - [filter.accessor]: state, + [filter.accessorKey]: state, })); }} label={filter.label} placeholder={filter.placeholder} - state={filterStates[filter.accessor] as IdFilterState} + state={filterStates[filter.accessorKey] as IdFilterState} /> ); @@ -541,11 +541,11 @@ export function DataManagerTable< updateFilterState={(state) => { setFilterStates((prevState) => ({ ...prevState, - [filter.accessor]: state, + [filter.accessorKey]: state, })); }} label={filter.label} - state={filterStates[filter.accessor] as BooleanFilterState} + state={filterStates[filter.accessorKey] as BooleanFilterState} /> ); diff --git a/packages/ui/src/components/DataManagerTable/filters.ts b/packages/ui/src/components/DataManagerTable/filters.ts index 100ba560..0f408199 100644 --- a/packages/ui/src/components/DataManagerTable/filters.ts +++ b/packages/ui/src/components/DataManagerTable/filters.ts @@ -1,8 +1,8 @@ interface FilterBase> { type: FilterType; label: string; - // This is the accessor for the column in the data - accessor: keyof T; + // This is the accessorKey for the column in the data + accessorKey: keyof T; value?: string | boolean; // Placeholder for any string filters placeholder?: string; diff --git a/packages/ui/src/components/Table/Table.tsx b/packages/ui/src/components/Table/Table.tsx index 49814f45..7ff52428 100644 --- a/packages/ui/src/components/Table/Table.tsx +++ b/packages/ui/src/components/Table/Table.tsx @@ -1,10 +1,19 @@ import { css } from "@emotion/react"; import styled from "@emotion/styled"; +import { + flexRender, + getCoreRowModel, + getSortedRowModel, + useReactTable, + type CellContext, + type ColumnSort, + type HeaderContext, + type Row, +} from "@tanstack/react-table"; import { isObject } from "lodash-es"; import type { KeyboardEvent, MouseEvent, ReactNode } from "react"; -import { Fragment, useCallback, useMemo } from "react"; -import { useTable, type Row } from "react-table"; +import { Fragment, useCallback, useMemo, useState } from "react"; import { useClipboard } from "../../hooks/useClipboard.js"; import { Link, @@ -32,7 +41,12 @@ import { Loading } from "../Loading.js"; export type TableColumnHeaderInfo = string | { name: string; tooltip?: string }; -export type TableCell = string | LinkCell | ClipboardCell | ReactNode; +export type TableCell = + | string + | LinkCell + | ClipboardCell + | MultilineCell + | ReactNode; type ObjectCell = { text: string; @@ -80,8 +94,8 @@ const isMultilineCell = (value: unknown): value is MultilineCell => { }; interface Column> { - Header: TableColumnHeaderInfo; - accessor: keyof T; + header: TableColumnHeaderInfo; + accessorKey: keyof T; } export type TableProps> = { @@ -104,6 +118,7 @@ export function Table>({ clipboardCallbacks, }: TableProps) { const navigate = useNavigate(); + const [sorting, setSorting] = useState([]); const { canWriteToClipboard, writeTextToClipboard } = useClipboard(clipboardCallbacks); @@ -119,22 +134,24 @@ export function Table>({ () => columns.map((column) => ({ ...column, - Header: - typeof column.Header === "string" ? ( - column.Header + header: (context: HeaderContext) => + typeof column.header === "string" ? ( + column.header ) : (
- {column.Header.name} + {column.header.name}
), - accessor: column.accessor.toString(), - Cell: ({ value }: { value: unknown }) => { - let content = value as ReactNode; + accessorKey: column.accessorKey.toString(), + cell: (context: CellContext) => { + const value = context.getValue(); + + let content: ReactNode = null; let icon = null; if (isObjectCell(value)) { icon = value.icon ? ( @@ -155,6 +172,8 @@ export function Table>({ ); } + } else { + content = value; } if (isLinkAndClipboardCell(value)) { @@ -265,10 +284,17 @@ export function Table>({ [columns, canWriteToClipboard, onClickCopy, clipboardCallbacks], ); - const tableInstance = useTable({ columns: mappedColumns, data }); - - const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = - tableInstance; + const tableInstance = useReactTable({ + columns: mappedColumns, + data, + state: { + sorting, + }, + onSortingChange: setSorting, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + // debugTable: true + }); function onClickDataRow( event: MouseEvent | KeyboardEvent, @@ -292,40 +318,35 @@ export function Table>({ return ( - + { // Loop over the header rows - headerGroups.map((headerGroup) => ( - // Apply the header row props - - { - // Loop over the headers in each row - headerGroup.headers.map((column) => ( - // Apply the header cell props - - { - // Render the header - column.render("Header") - } + tableInstance.getHeaderGroups().map((headerGroup) => { + return ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} - )) - } - - )) + ))} + + ); + }) } - {/* Apply the table body props */} - + { // Loop over the table rows - rows.map((row) => { - // Prepare the row for display - prepareRow(row); + tableInstance.getRowModel().rows.map((row) => { return ( - // Apply the row props onClickDataRow(event, row)} onKeyDown={(event) => { if (event.key === "Enter") { @@ -334,20 +355,14 @@ export function Table>({ }} tabIndex={0} > - { - // Loop over the rows cells - row.cells.map((cell) => { - // Apply the cell props - return ( - - { - // Render the cell contents - cell.render("Cell") - } - - ); - }) - } + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} ); })