Mixing shadcn/ui data-table and sheet components together in NextJs

2.3k Views Asked by At

It's my first time using the shadcn/ui library and I wanted to implement it in my new NextJs app, I created a data-table component (link: text), and I wanted to add a sheet component (link: text) which I wanted it to show when the user clicks on a row. I tried several attempts but every time It renders like this: as you can see all the data becomes inside the first column

here is my implementation:

"use client";

import {
    ColumnDef,
    flexRender,
    getCoreRowModel,
    useReactTable,
} from "@tanstack/react-table";

import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from "@/components/ui/table";

import {
    Sheet,
    SheetContent,
    SheetDescription,
    SheetHeader,
    SheetTitle,
    SheetTrigger,
} from "@/components/ui/sheet";

interface DataTableProps<TData, TValue> {
    columns: ColumnDef<TData, TValue>[];
    data: TData[];
}

export function DataTable<TData, TValue>({
    columns,
    data,
}: DataTableProps<TData, TValue>) {
    const table = useReactTable({
        data,
        columns,
        getCoreRowModel: getCoreRowModel(),
    });

    return (
        <div className="rounded-md border">
            <Table>
                <TableHeader>
                    {table.getHeaderGroups().map((headerGroup) => (
                        <TableRow key={headerGroup.id} className="w-32">
                            {headerGroup.headers.map((header) => {
                                return (
                                    <TableHead key={header.id} className="w-32">
                                        {header.isPlaceholder
                                            ? null
                                            : flexRender(
                                                  header.column.columnDef
                                                      .header,
                                                  header.getContext()
                                              )}
                                    </TableHead>
                                );
                            })}
                        </TableRow>
                    ))}
                </TableHeader>

                <TableBody>
                    {table.getRowModel().rows?.length ? (
                        table.getRowModel().rows.map((row) => (
                            <Sheet key={row.id}>
                                <SheetTrigger key={row.id} asChild={true}>
                                    <TableRow
                                        key={row.id}
                                        data-state={
                                            row.getIsSelected() && "selected"
                                        }
                                    >
                                        {row.getVisibleCells().map((cell) => (
                                            <TableCell
                                                key={cell.id}
                                                className="w-fit"
                                            >
                                                {flexRender(
                                                    cell.column.columnDef.cell,
                                                    cell.getContext()
                                                )}
                                            </TableCell>
                                        ))}
                                    </TableRow>
                                </SheetTrigger>
                                <SheetContent>
                                    <SheetHeader>
                                        <SheetTitle>
                                            Are you sure absolutely sure?
                                        </SheetTitle>
                                        <SheetDescription>
                                            This action cannot be undone. This
                                            will permanently delete your account
                                            and remove your data from our
                                            servers.
                                        </SheetDescription>
                                    </SheetHeader>
                                </SheetContent>
                            </Sheet>
                        ))
                    ) : (
                        <TableRow>
                            <TableCell
                                colSpan={columns.length}
                                className="h-24 text-center"
                            >
                                No results.
                            </TableCell>
                        </TableRow>
                    )}
                </TableBody>
            </Table>
        </div>
    );
}

I even tried to make the sheet into a function component that takes props but it didn't work either, If you have any ideas or suggestions please help.

1

There are 1 best solutions below

0
On

I faced a similar issue for one of my projects recently. While I did not understand how to solve it directly, I found a workaround that makes it run well. What I did was to define a <Sheet /> component in the parent element where the Data Table is being called, and you add a rowInfo state variable that changes the value to the current row click, which you can handle through the onClick parameter of your table rows.

DataTable.tsx

interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
  initialPageSize?: number;
  onRowClick?: (row: TData) => void;
  exportButton?: boolean;
  pagination?: boolean;
}

export function DataTable<TData, TValue>({
  columns,
  data,
  initialPageSize = 10,
  onRowClick,
  exportButton = true,
  pagination = true,
}: DataTableProps<TData, TValue>) {
  const [sorting, setSorting] = useState<SortingState>([]);
  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);

  const table = useReactTable({
    data,
    columns,
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    state: {
      sorting,
      columnFilters,
    },
    initialState: {
      pagination: {
        pageSize: initialPageSize,
      },
    },
  });

... existing table code ...

   </TableHeader>
          <TableBody>
            {table.getRowModel().rows?.length ? (
              table.getRowModel().rows.map((row) => (
                <TableRow
                  key={row.id}
                  data-state={row.getIsSelected() && 'selected'}
                  onClick={() => onRowClick && onRowClick(row.original)}
                  className="cursor-pointer">

Then, the onRowClick() can be set to the state handler:

page.tsx

const handleRowClick = (row: RowType) => {
    setIsSheetOpen(true);
    setRowDetails(row);
    console.log(row);
  };

Hope this helps!