TanStackTableHeadSort.test.tsx124 lines · main
1import {
2 flexRender,
3 getCoreRowModel,
4 getSortedRowModel,
5 useReactTable,
6 type ColumnDef,
7 type SortingState,
8} from '@tanstack/react-table'
9import { render, screen } from '@testing-library/react'
10import userEvent from '@testing-library/user-event'
11import { useState } from 'react'
12import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
13import { describe, expect, it } from 'vitest'
14
15import { TanStackTableHeadSort } from './TanStackTableHeadSort'
16
17type Row = {
18 name: string
19 amount: number
20}
21
22const data: Row[] = [
23 { name: 'Bravo', amount: 200 },
24 { name: 'Alpha', amount: 100 },
25]
26
27const columns: ColumnDef<Row>[] = [
28 {
29 accessorKey: 'name',
30 header: ({ column }) => <TanStackTableHeadSort column={column}>Name</TanStackTableHeadSort>,
31 cell: ({ row }) => row.getValue('name'),
32 },
33 {
34 accessorKey: 'amount',
35 enableSorting: false,
36 header: ({ column }) => <TanStackTableHeadSort column={column}>Amount</TanStackTableHeadSort>,
37 cell: ({ row }) => row.getValue('amount'),
38 },
39]
40
41const classNameColumns: ColumnDef<Row>[] = [
42 {
43 accessorKey: 'name',
44 header: ({ column }) => (
45 <TanStackTableHeadSort column={column} className="justify-end">
46 Name
47 </TanStackTableHeadSort>
48 ),
49 cell: ({ row }) => row.getValue('name'),
50 },
51]
52
53const TestTable = ({ tableColumns = columns }: { tableColumns?: ColumnDef<Row>[] }) => {
54 const [sorting, setSorting] = useState<SortingState>([])
55 const table = useReactTable({
56 data,
57 columns: tableColumns,
58 state: { sorting },
59 onSortingChange: setSorting,
60 getCoreRowModel: getCoreRowModel(),
61 getSortedRowModel: getSortedRowModel(),
62 })
63
64 return (
65 <Table>
66 <TableHeader>
67 {table.getHeaderGroups().map((headerGroup) => (
68 <TableRow key={headerGroup.id}>
69 {headerGroup.headers.map((header) => (
70 <TableHead key={header.id}>
71 {header.isPlaceholder
72 ? null
73 : flexRender(header.column.columnDef.header, header.getContext())}
74 </TableHead>
75 ))}
76 </TableRow>
77 ))}
78 </TableHeader>
79 <TableBody>
80 {table.getRowModel().rows.map((row) => (
81 <TableRow key={row.id}>
82 {row.getVisibleCells().map((cell) => (
83 <TableCell key={cell.id}>
84 {flexRender(cell.column.columnDef.cell, cell.getContext())}
85 </TableCell>
86 ))}
87 </TableRow>
88 ))}
89 </TableBody>
90 </Table>
91 )
92}
93
94describe('TanStackTableHeadSort', () => {
95 it('cycles unsorted, ascending, descending, and cleared', async () => {
96 const user = userEvent.setup()
97
98 render(<TestTable />)
99
100 expect(screen.getAllByRole('row')[1]).toHaveTextContent('Bravo')
101
102 await user.click(screen.getByRole('button', { name: 'Name' }))
103 expect(screen.getAllByRole('row')[1]).toHaveTextContent('Alpha')
104
105 await user.click(screen.getByRole('button', { name: 'Name' }))
106 expect(screen.getAllByRole('row')[1]).toHaveTextContent('Bravo')
107
108 await user.click(screen.getByRole('button', { name: 'Name' }))
109 expect(screen.getAllByRole('row')[1]).toHaveTextContent('Bravo')
110 })
111
112 it('renders non-sortable columns as plain content', () => {
113 render(<TestTable />)
114
115 expect(screen.getByText('Amount')).toBeInTheDocument()
116 expect(screen.queryByRole('button', { name: 'Amount' })).not.toBeInTheDocument()
117 })
118
119 it('passes className through to the rendered sort control', () => {
120 render(<TestTable tableColumns={classNameColumns} />)
121
122 expect(screen.getByRole('button', { name: 'Name' })).toHaveClass('justify-end')
123 })
124})