diff --git a/src/app/base/components/GenericTable/GenericTable.test.tsx b/src/app/base/components/GenericTable/GenericTable.test.tsx index 45c1cc3867..ded178d575 100644 --- a/src/app/base/components/GenericTable/GenericTable.test.tsx +++ b/src/app/base/components/GenericTable/GenericTable.test.tsx @@ -67,7 +67,6 @@ describe("GenericTable", () => { const mockFilterCells = vi.fn(() => true); const mockFilterHeaders = vi.fn(() => true); - const mockGetRowId = vi.fn((row) => row.id.toString()); it("renders table with headers and rows", () => { render( @@ -76,7 +75,6 @@ describe("GenericTable", () => { data={data} filterCells={mockFilterCells} filterHeaders={mockFilterHeaders} - getRowId={mockGetRowId} rowSelection={{}} setRowSelection={vi.fn} /> @@ -96,7 +94,6 @@ describe("GenericTable", () => { data={[]} filterCells={mockFilterCells} filterHeaders={mockFilterHeaders} - getRowId={mockGetRowId} noData={No data} rowSelection={{}} setRowSelection={vi.fn} @@ -113,7 +110,6 @@ describe("GenericTable", () => { data={data} filterCells={mockFilterCells} filterHeaders={mockFilterHeaders} - getRowId={mockGetRowId} rowSelection={{}} setRowSelection={vi.fn} /> diff --git a/src/app/base/components/GenericTable/GenericTable.tsx b/src/app/base/components/GenericTable/GenericTable.tsx index 31182b802f..2bbfe4a662 100644 --- a/src/app/base/components/GenericTable/GenericTable.tsx +++ b/src/app/base/components/GenericTable/GenericTable.tsx @@ -1,59 +1,61 @@ -import type { Dispatch, ReactNode, SetStateAction } from "react"; -import { useMemo, useState } from "react"; +import { + type Dispatch, + type ReactNode, + type SetStateAction, + useMemo, + useState, +} from "react"; import { DynamicTable } from "@canonical/maas-react-components"; -import { Button } from "@canonical/react-components"; import type { Column, + Row, ColumnDef, ColumnSort, - ExpandedState, GroupingState, + ExpandedState, + SortingState, Header, - Row, RowSelectionState, - SortingState, } from "@tanstack/react-table"; import { + flexRender, getCoreRowModel, getExpandedRowModel, getGroupedRowModel, useReactTable, - flexRender, } from "@tanstack/react-table"; import classNames from "classnames"; +import TableCheckbox from "@/app/base/components/GenericTable/TableCheckbox"; +import TableHeader from "@/app/base/components/GenericTable/TableHeader"; + import "./_index.scss"; -import SortingIndicator from "./SortingIndicator"; type GenericTableProps = { - ariaLabel?: string; + canSelect?: boolean; columns: ColumnDef>[]; data: T[]; - filterCells: (row: Row, column: Column) => boolean; - filterHeaders: (header: Header) => boolean; - getRowId: ( - originalRow: T, - index: number, - parent?: Row | undefined - ) => string; + filterCells?: (row: Row, column: Column) => boolean; + filterHeaders?: (header: Header) => boolean; groupBy?: string[]; noData?: ReactNode; + pin?: { value: string; isTop: boolean }[]; sortBy?: ColumnSort[]; - rowSelection: RowSelectionState; + rowSelection?: RowSelectionState; setRowSelection?: Dispatch>; }; const GenericTable = ({ - ariaLabel, + canSelect = false, columns, data, - filterCells, - filterHeaders, - getRowId, + filterCells = () => true, + filterHeaders = () => true, groupBy, - sortBy, noData, + pin, + sortBy, rowSelection, setRowSelection, }: GenericTableProps) => { @@ -61,8 +63,62 @@ const GenericTable = ({ const [expanded, setExpanded] = useState(true); const [sorting, setSorting] = useState(sortBy ?? []); - const sortedData = useMemo(() => { + if (canSelect) { + columns = [ + { + id: "select", + accessorKey: "id", + enableSorting: false, + header: "", + cell: ({ row }) => + !row.getIsGrouped() ? : null, + }, + ...columns, + ]; + + if (groupBy) { + columns = [ + { + id: "group-select", + accessorKey: "id", + enableSorting: false, + header: ({ table }) => , + cell: ({ row }) => + row.getIsGrouped() ? : null, + }, + ...columns, + ]; + } + } + + data = useMemo(() => { return [...data].sort((a, b) => { + if (pin && pin.length > 0 && grouping.length > 0) { + for (const { value, isTop } of pin) { + const groupId = grouping[0]; + const aValue = a[groupId as keyof typeof a]; + const bValue = b[groupId as keyof typeof b]; + + if (aValue === value && bValue !== value) { + return isTop ? -1 : 1; + } + if (bValue === value && aValue !== value) { + return isTop ? 1 : -1; + } + } + } + + for (const groupId of grouping) { + const aGroupValue = a[groupId as keyof typeof a]; + const bGroupValue = b[groupId as keyof typeof b]; + if (aGroupValue < bGroupValue) { + return -1; + } + if (aGroupValue > bGroupValue) { + return 1; + } + } + for (const { id, desc } of sorting) { const aValue = a[id as keyof typeof a]; const bValue = b[id as keyof typeof b]; @@ -75,10 +131,10 @@ const GenericTable = ({ } return 0; }); - }, [data, sorting]); + }, [data, sorting, grouping, pin]); const table = useReactTable({ - data: sortedData, + data, columns, state: { grouping, @@ -101,40 +157,15 @@ const GenericTable = ({ groupedColumnMode: false, enableRowSelection: true, enableMultiRowSelection: true, - getRowId, }); return ( - + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.filter(filterHeaders).map((header) => ( - - {header.column.getCanSort() ? ( - - ) : ( - flexRender( - header.column.columnDef.header, - header.getContext() - ) - )} - + ))} ))} diff --git a/src/app/base/components/GenericTable/TableCheckbox/TableCheckbox.tsx b/src/app/base/components/GenericTable/TableCheckbox/TableCheckbox.tsx index 6f3cad8a42..75777497a3 100644 --- a/src/app/base/components/GenericTable/TableCheckbox/TableCheckbox.tsx +++ b/src/app/base/components/GenericTable/TableCheckbox/TableCheckbox.tsx @@ -24,7 +24,7 @@ const TableAllCheckbox = ({ table, ...props }: TableCheckboxProps) => { } return ( -