Reputation: 568
We are migrating our tables from v7 to v8. And I'm kinda got a problem with cells conditional styling. So basically what I want to do is, based on a status (which is coming to table data) I need to add a specific className to every cell in a row.
In v7 we used this: https://react-table-v7.tanstack.com/docs/examples/data-driven-classes-and-styles
But in v8 I can't find anything like that....
So far I tried to use meta
in column definitions https://tanstack.com/table/v8/docs/api/core/column-def#meta where I can set some values to className property, and use it in my JSX like this:
className={cell.column.columnDef.meta?.className}
But problem is anything I can set to meta are static values. For my case I need to set specific className based on my status value. And seems like in meta we can't access any cell props...
const driverFormatter = ({ row }) => {
const { status } = row.original;
return <span>{status}</span>;
};
const columns: ColumnDef<any,any>[] = [
{
accessorKey: "customerName",
header: "Customer"
},
{
accessorKey: "driver",
header: "Driver",
enableSorting: false,
cell: driverFormatter,
meta: {
className: "disabled",
},
},
...
So is there are any way of achieving that using v8???
Thank you!
Upvotes: 9
Views: 20875
Reputation: 1
go to DataTable, get value of an individual cell and style the cell based on its value
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
cell.getValue() === "Paid" ?
<TableCell key={cell.id} className="text-nowrap text-green-600">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell> :
cell.getValue() === "Not paid" ?
<TableCell key={cell.id} className="text-nowrap text-red-600">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell> :
<TableCell key={cell.id} className="text-nowrap">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
Upvotes: 0
Reputation: 93
Tech Stack: Next.js v13 Javascript Tanstack v8
Hey I was stuck with same but I found an easy but costly fix: I will share my code try it out : Notes: using getValue() function to get value of every cell in table body, and that value is a arg to another function named getRolesCellClassName()
import AdminStyle from "../src/styles/admin.module.css";
import React, { useEffect, useState, useMemo } from "react";
import {
useReactTable,
getCoreRowModel,
flexRender,
} from "@tanstack/react-table";
const BasicTable = () => {
const [users, setUsers] = useState([]);
const fetchUsers = async () => {
const response = await fetch("http://localhost:9002/user").catch((err) =>
console.log(err)
);
if (response) {
const usersData = await response.json();
const transformedUsers = usersData.flatMap(function(us) {
return us.roles.map(function(role) {
return { ...us, roles: role };
});
});
console.log(usersData);
console.log(transformedUsers);
setUsers(transformedUsers);
}
};
useEffect(() => {
fetchUsers();
}, []);
const data = useMemo(() => users, [users]);
// =========================Returning dynamic style==========================
const getRolesCellClassName = (value) => {
console.log(value)
if (value === "admin") {
return `${AdminStyle.adminRoleBadge} bg-green-500 text-white`;
} else if (value === "user") {
return `${AdminStyle.userRole} bg-blue-500 text-white`;
}
// Add more conditions for other roles if needed
return ""; // Default CSS class if no specific condition is met
};
// /**@type import('@tanstack/react-table').ColumnDef<any> */
const columns = [
{
header: "Name",
accessorFn: (row) => `${row.first_name} ${row.last_name}`,
// Adds classes to this particular column but not to header of that column
columnClassName: "font-medium text-sm "
},
{
header: "Email",
accessorKey: "email",
// Adds classes to this particular column but not headers
columnClassName: AdminStyle.emailColumn
},
{
header: "Roles",
accessorKey: "roles",
cellClassName: (cell) => getRolesCellClassName(cell.value), // Apply the cell class dynamically
},
{
header: "Actions",
cell: (row) => {
return (
<button
onClick={(row)=>{alert('message' + row.getValue.email)}}
>
Edit
{row.getValue()}
</button>
);
},
accessorKey: "actions",
},
];
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<table className="w-full ">
<thead className={AdminStyle.borderBtm}>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id} >
{headerGroup.headers.map((header) => (
<th
key={header.id}
className={`${AdminStyle} border-btm text-left gap-3 px-6 py-2 `}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr
key={row.id}
className={` ${AdminStyle.borderBtm} w-full table-hea border-btm box-border items-center`}
>
{row.getVisibleCells().map((cell) => (
<td
key={cell.id}
className={`${AdminStyle} pl-6 gap-3 py-6 ${cell.column.columnDef.columnClassName} `}
>
{/* To get the actual value
{console.log(cell.getValue())} */}
{/* ================================Conditionally add
styles=============================== */}
<span className={`${getRolesCellClassName(cell.getValue())}`}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</span>
</td>
))}
</tr>
))}
</tbody>
</table>
);
};
export default BasicTable;
Upvotes: 0
Reputation: 91
First Method - Through column definition meta key:
We can get the detailed context of each cell in V8 through meta, like
{
accessorKey: 'section',
cell: (info) => info.getValue(),
footer: (props) => props.column.id,
meta: {
getCellContext: (context: CellContext<Person, unknown>) => {
if (context.row.index === 0) {
// console.log(context)
return {
style: { fontWeight: 'bold', minWidth: '30%', textTransform: 'uppercase' },
className: 'bold',
}
}
},
},
},
To get dynamic context like this, we need to include additional props to ColumnMeta.
declare module '@tanstack/react-table' {
interface ColumnMeta<TData, TValue> {
// Your additional properties here
getCellContext: (context: CellContext<TData, TValue>) => TableCellProps | void
}
}
Then we can use this func inside the loop
<TableBody>
{table
.getRowModel()
.rows.slice(0, 20)
.map((row) => {
return (
<TableRow key={row.id} {...getRowProps(row)}>
{(isSplit ? row.getCenterVisibleCells() : row.getVisibleCells()).map((cell) => {
let hasMeta = cell.getContext().cell.column.columnDef.meta
return (
<TableCell
key={cell.id}
{...(hasMeta && { ...hasMeta.getCellContext(cell.getContext()) })}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
)
})}
</TableRow>
)
})}
</TableBody>
By this, you can add styles, className,, etc. To each *** TABLE CELL*** based on the value.
But you can't style the TABLE ROW with the above method. For that
Second Method - Through callback function:
Define two functions for rows and cells
// Func to provide props to table row
const getRowProps = (context: Row<Person>): TableRowProps | void => {
console.log(context)
if (context.original.section.includes('section')) {
return { className: 'sectionHeader', sx: (theme) => ({ background: theme.palette.primary.light }) }
}
}
// Func to provide props to table cell
const getCellProps = (context: CellContext<Person, unknown>): TableCellProps | void => {
if (context.row.index === 0) {
// console.log(context)
return {
style: { fontWeight: 'bold', minWidth: '30%', textTransform: 'uppercase' },
className: 'bold',
}
}
}
And just destructure the functions
<TableBody>
{table
.getRowModel()
.rows.slice(0, 20)
.map((row) => {
return (
<TableRow key={row.id} {...getRowProps(row)}> //<- here
{row.getLeftVisibleCells().map((cell) => {
return (
<TableCell
key={cell.id}
{...getCellProps(cell.getContext())} // <-here
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
)
})}
</TableRow>
)
})}
</TableBody>
Upvotes: 9
Reputation: 39
I also encountered the same problem and as you mentioned there is separate example in V7 .
and for v8 there is not enough documentation or correct example
Here is link to my implementation where I was able to add different column color in v8. Hope this helps. If you any further doubt let me know
Upvotes: 3