I am trying to build a data table where each row can be expanded/collapsed. Clicking on the arrow on the far left, expands the coloured content as show in the image below. This is working nicely, but the content of the expanded section is only as wide as the first column. How can I make the expanded section as wide as the entire table (indicated by the red arrows)?
I am using Shadcn-UI (https://ui.shadcn.com/docs/components/data-table).
Here is the code:
//page.tsx
import { DataTable } from "./data-table";
import { Row, columns } from "./columns";
export default async function CollapsibleTablePage() {
const data: [Row] = [
{
column1: "Hi from outer column 1 and row 1",
column2: "Hi from outer column 2 and row 1",
collapsibleContent: "Hi from collapsible content and row 1",
},
{
column1: "Hi from outer column 1 and row 2",
column2: "Hi from outer column 2 and row 2",
collapsibleContent: "Hi from collapsible content and row 2",
},
{
column1: "Hi from outer column 1 and row 3",
column2: "Hi from outer column 2 and row 3",
collapsibleContent: "Hi from collapsible content and row 3",
},
];
return (
<DataTable columns={columns} data={data} />
);
}
//columns.tsx
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { ChevronDown, Copy } from "lucide-react";
import { Button } from "@/components/shadcn/ui/button";
import { CollapsibleTrigger } from "@/components/shadcn/ui/collapsible";
export type Row = {
column1: string;
column2: string;
collapsibleContent: string;
};
export const columns: ColumnDef<Row>[] = [
{
accessorKey: "column1",
header: "column1",
cell: ({ row }) => {
return (
<div className="flex items-center">
<CollapsibleTrigger>
<Button variant="ghost">
<ChevronDown className="h-4 w-4" />
</Button>
{row.getValue("column1")}
</CollapsibleTrigger>
</div>
);
},
},
{
accessorKey: "column2",
header: "column2",
},
];
//data-table.tsx
"use client";
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/shadcn/ui/table";
import {
Collapsible,
CollapsibleContent,
} from "@/components/shadcn/ui/collapsible";
import React from "react";
import { Row } from "./columns";
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(),
});
var CollapsibleRowContent = ({ row }: { row: Row }) => (
<div className="p-20">{row.collapsibleContent}</div>
);
return (
<div>
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<Collapsible key={row.id} asChild>
<>
<TableRow>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
<CollapsibleContent className="bg-slate-700">
<CollapsibleRowContent row={row.original} />
</CollapsibleContent>
</>
</Collapsible>
))}
</TableBody>
</Table>
</div>
);
}
Since it is in a <table>
, we can use the colspan
attribute on a <td>
element to span multiple columns.
First, we override the element rendered by <CollapsibleContent>
to be a <tr>
using the asChild
prop:
<CollapsibleContent className="bg-slate-700" asChild>
<tr>
<CollapsibleRowContent row={row.original} />
</tr>
</CollapsibleContent>
And then render the <td>
in <CollapsibleRowContent>
with the colSpan
prop that translates to the colspan
attribute:
var CollapsibleRowContent = ({ row }: { row: Row }) => (
<td colSpan={2}>
<div className="p-20">{row.collapsibleContent}</div>
</td>
);
See it working in this Stackblitz project.