I am building a table using the Tanstack Table v.8. I've added the expanding rows functionality but I stumbled when trying to implement a toggle button which expands/collapses all rows.
I can't figure out why the column header doesn't render a function, as in the tanstack example here: https://tanstack.com/table/v8/docs/examples/react/expanding.
it doesn't even render a simple test function like () => <div>foo</div>
.
I'm a beginner with JS/React so any help would be greatly appreciated.
"use client";
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
getExpandedRowModel,
useReactTable,
createColumnHelper,
} from "@tanstack/react-table";
import Image from "next/image";
import { useState, useMemo, useRef } from "react";
import useSWR from "swr";
import JobsFilterBox from "./JobsFilterBox";
const fetcher = (...args) => fetch(...args).then((res) => res.json());
const columnHelper = createColumnHelper();
const JobsTable = () => {
const columns = useMemo(
() => [
columnHelper.accessor("expander", {
id: "expander",
size: 20,
enableSorting: false,
header: () => (
<>
<button onClick={table.getToggleAllRowsExpandedHandler()}>
{table.getIsAllRowsExpanded() ? "-" : "+"}
</button>
</>
),
cell: (info) => {
return (
<div className="relative">
{info.row.getCanExpand() && (
<button onClick={info.row.getToggleExpandedHandler()}>
<Image
src="/table_expander.svg"
fill={true}
alt="table expander"
className={` ${
info.row.getIsExpanded() ? "rotate-180 " : "rotate-0 "
}`}
/>
</button>
)}
{}
</div>
);
},
}),
columnHelper.accessor("Serial", {
header: "Serial",
cell: (props) => <p>{props.getValue()}</p>,
size: 80,
}),
// rest of the columns similar to above Serial
],
[],
);
const [columnFilters, setColumnFilters] = useState([]);
const [expanded, setExpanded] = useState(false);
const {
data: jobsData,
error: jobsError,
isLoading: jobsIsLoading,
} = useSWR("http://localhost:9600/Job/jobs", fetcher);
const table = useReactTable({
data: jobsData,
columns,
state: {
columnFilters,
expanded,
},
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
getSortedRowModel: getSortedRowModel(),
onExpandedChange: setExpanded,
getSubRows: (row) => row.Tasks,
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
initialState: {
pagination: {
pageSize: 27,
},
},
columnResizeMode: "onChange",
});
if (jobsError) return <div>failed to load</div>;
if (jobsIsLoading) return <div>loading...</div>;
return (
<div className="relative grid h-screen w-full grid-cols-[0.5fr_1fr] grid-rows-[40px_700px_80px] gap-y-1">
<JobsFilterBox
className="col-[1_/_3] row-[1_/_2]"
columnFilters={columnFilters}
setColumnFilters={setColumnFilters}
/>
<div className="col-[1_/_3] row-[2_/_3] overflow-x-scroll">
<table
className={
"border-l border-t border-gray-300 bg-white text-left text-xs"
}
style={{ width: table.getTotalSize() }}
>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr className="relative flex" key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th
className={
"relative flex items-center justify-center border-b border-r border-gray-300 bg-[#2170ba] p-2 text-center text-gray-100"
}
style={{ width: header.getSize() }}
key={header.id}
>
{header.column.columnDef.header}
{header.column.getCanSort() && (
<div className="relative ml-2 h-3 w-3 cursor-pointer">
<Image
fill={true}
src="../sort_icon.svg"
alt="sort icon"
onClick={header.column.getToggleSortingHandler()}
/>
</div>
)}
<div
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={`${
header.column.getIsResizing()
? "bg-orange-600 opacity-100"
: ""
} absolute right-0 top-0 h-full w-[5px] cursor-col-resize touch-none select-none bg-blue-900 opacity-0 hover:opacity-100`}
></div>
</th>
))}
</tr>
))}
</thead>
<tbody className="">
{table.getRowModel().rows.map((row) => (
<tr
className={` relative flex h-6 ${
row.depth > 0
? "bg-[#afd6f4] text-gray-700"
: "bg-[#93c8f0] text-gray-700"
} ${
row.depth === 1 && row.id.includes(".0")
? "border-t border-gray-400"
: ""
} `}
key={row.id}
>
{row.getVisibleCells().map((cell) => (
<td
className={`truncate border-b border-r border-gray-300 ${
cell.column.id === "State"
? getStatusColor(row.original.Status)
: ""
} `}
style={{ width: cell.column.getSize() }}
key={cell.id}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
<div className="col-span-2 col-start-1 row-[3_/_4] flex flex-[1_0] flex-row flex-nowrap">
<div className="">
<button
className="mr-2 w-fit shrink-0"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{"<"}
</button>
</div>
<div className="">
<p className="w-fit text-sm text-gray-900">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</p>
</div>
<div className="">
<button
className="ml-2 w-fit"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{">"}
</button>
</div>
</div>
</div>
);
};
I replaced this line:
{header.column.columnDef.header}
With this:
<span>{flexRender(header.column.columnDef.header, header.getContext())}</span>
And now it works.