Grouping
Row grouping allows users to collapse rows into hierarchical groups based on column values. Click the group icon in a column header to group by that column. Grouped rows display a count of their subRows and can be expanded to show the underlying data. Use an aggregationFn on columns like "Visits" to aggregate values (e.g., sum) when rows are grouped.
First Name | Last Name | Visits | Account Status | Role |
|---|
{
"grouping": []
}
import {useMemo, useState} from "react"
import {Combine, Ungroup} from "lucide-react"
import {
type ColumnDef,
getCoreRowModel,
getExpandedRowModel,
getFilteredRowModel,
getGroupedRowModel,
getPaginationRowModel,
type GroupingState,
} from "@qualcomm-ui/core/table"
import {Button} from "@qualcomm-ui/react/button"
import {Pagination} from "@qualcomm-ui/react/pagination"
import {ProgressRing} from "@qualcomm-ui/react/progress-ring"
import {
flexRender,
Table,
useReactTable,
useTablePagination,
} from "@qualcomm-ui/react/table"
import {CodeHighlight} from "@qualcomm-ui/react-mdx/code-highlight"
import {type User, useUserData} from "./use-data"
export function GroupingDemo() {
const {data = [], isFetching, refetch} = useUserData(10000)
// always memoize your data and columns
const columns: ColumnDef<User>[] = useMemo(
() => [
{
accessorKey: "firstName",
cell: (info) => info.getValue(),
header: "First Name",
},
{
accessorFn: (row) => row.lastName,
cell: (info) => info.getValue(),
header: "Last Name",
id: "lastName",
},
{
accessorKey: "visitCount",
aggregationFn: "sum",
header: "Visits",
},
{
accessorKey: "accountStatus",
header: "Account Status",
},
{
accessorKey: "role",
header: "Role",
id: "role",
},
],
[],
)
const refreshData = () => refetch()
const [grouping, setGrouping] = useState<GroupingState>([])
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getGroupedRowModel: data.length ? getGroupedRowModel() : undefined,
getPaginationRowModel: getPaginationRowModel(),
onGroupingChange: setGrouping,
state: {
grouping,
},
})
const paginationProps = useTablePagination(table)
return (
<div className="flex w-full flex-col gap-4 p-2">
<Table.Root>
<Table.ActionBar>
<Button onClick={() => void refreshData()} variant="outline">
Refresh Data
</Button>
{isFetching ? <ProgressRing size="xs" /> : null}
</Table.ActionBar>
<Table.ScrollContainer>
<Table.Table>
<Table.Header>
{table.getHeaderGroups().map((headerGroup) => (
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<Table.HeaderCell
key={header.id}
colSpan={header.colSpan}
style={{width: header.getSize()}}
>
{header.isPlaceholder ? null : (
<div className="flex items-center gap-1">
{header.column.getCanGroup() ? (
// If the header can be grouped, add a toggle
<Table.ColumnHeaderAction
icon={
header.column.getIsGrouped()
? Ungroup
: Combine
}
onClick={header.column.getToggleGroupingHandler()}
title={
header.column.getIsGrouped()
? "Ungroup"
: "Group"
}
/>
) : null}{" "}
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</div>
)}
</Table.HeaderCell>
)
})}
</Table.Row>
))}
</Table.Header>
<Table.Body>
{table.getRowModel().rows.map((row) => {
return (
<Table.Row key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<Table.Cell key={cell.id} cell={cell}>
{cell.getIsGrouped() ? (
// If it's a grouped cell, add an expander and row
// count
<div className="inline-flex items-center gap-2">
{row.getCanExpand() ? (
<Table.RowExpandButton row={row} />
) : null}
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}{" "}
({row.subRows.length})
</div>
) : cell.getIsAggregated() ? (
// If the cell is aggregated, use the Aggregated
// renderer for cell
flexRender(
cell.column.columnDef.aggregatedCell ??
cell.column.columnDef.cell,
cell.getContext(),
)
) : cell.getIsPlaceholder() ? null : ( // For cells with repeated values, render null
// Otherwise, just render the regular cell
flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)
)}
</Table.Cell>
)
})}
</Table.Row>
)
})}
</Table.Body>
</Table.Table>
</Table.ScrollContainer>
<Table.Pagination {...paginationProps}>
<Pagination.PageMetadata>
{({count, pageEnd, pageStart}) => (
<>
{pageStart}-{pageEnd} of {count} results
</>
)}
</Pagination.PageMetadata>
<Pagination.PageButtons />
</Table.Pagination>
</Table.Root>
<CodeHighlight
className="w-fit"
code={JSON.stringify({grouping}, null, 2)}
disableCopy
language="json"
/>
</div>
)
}