TanStack react table v8 style each cell based on the cell value
Asked Answered
A

4

9

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!

Antidisestablishmentarianism answered 19/1, 2023 at 15:26 Comment(0)
C
8

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>
Cartierbresson answered 3/3, 2023 at 5:31 Comment(1)
This is the official way to do it as described in the docs under ColumnDef. This worked for me. I had to define TableCellProps for what I needed. It is not an import for anyone wondering.Consequent
M
3

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

Matthus answered 21/2, 2023 at 7:19 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Pudens
A
0

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;
Acree answered 6/7, 2023 at 13:58 Comment(0)
H
0

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>
Houston answered 3/5 at 11:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.