mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 20:35:07 +03:00
## What kind of change does this PR introduce? Component update. ## What is the current behaviour? TanStack tables in the repo are split between the shared `TableHeadSort` primitive and the older Studio-local `DataTableColumnHeader` helper, which makes the sorting UI and integration path inconsistent. If you were to just use `DataTableColumnHeader` in `ui-patterns/Table`, you’d get a very different visual result to the `TableHeadSort` UI you see in most other tables. ## What is the new behaviour? Adds a shared `TanStackTableHeadSort` adapter in `ui-patterns/Table`, backed by the existing `TableHeadSort` primitive, and switches the webhook table plus the design-system TanStack demo to that canonical path. `DataTableColumnHeader` stays as a deprecated wrapper for now, Studio gets a lint guard to block new imports of it, and the table docs now point TanStack tables at the shared adapter explicitly. ## To test Check out column sorting on the Platform Webhook endpoint deliveries table.
125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import {
|
|
flexRender,
|
|
getCoreRowModel,
|
|
getSortedRowModel,
|
|
useReactTable,
|
|
type ColumnDef,
|
|
type SortingState,
|
|
} from '@tanstack/react-table'
|
|
import { render, screen } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import { useState } from 'react'
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { TanStackTableHeadSort } from './TanStackTableHeadSort'
|
|
|
|
type Row = {
|
|
name: string
|
|
amount: number
|
|
}
|
|
|
|
const data: Row[] = [
|
|
{ name: 'Bravo', amount: 200 },
|
|
{ name: 'Alpha', amount: 100 },
|
|
]
|
|
|
|
const columns: ColumnDef<Row>[] = [
|
|
{
|
|
accessorKey: 'name',
|
|
header: ({ column }) => <TanStackTableHeadSort column={column}>Name</TanStackTableHeadSort>,
|
|
cell: ({ row }) => row.getValue('name'),
|
|
},
|
|
{
|
|
accessorKey: 'amount',
|
|
enableSorting: false,
|
|
header: ({ column }) => <TanStackTableHeadSort column={column}>Amount</TanStackTableHeadSort>,
|
|
cell: ({ row }) => row.getValue('amount'),
|
|
},
|
|
]
|
|
|
|
const classNameColumns: ColumnDef<Row>[] = [
|
|
{
|
|
accessorKey: 'name',
|
|
header: ({ column }) => (
|
|
<TanStackTableHeadSort column={column} className="justify-end">
|
|
Name
|
|
</TanStackTableHeadSort>
|
|
),
|
|
cell: ({ row }) => row.getValue('name'),
|
|
},
|
|
]
|
|
|
|
const TestTable = ({ tableColumns = columns }: { tableColumns?: ColumnDef<Row>[] }) => {
|
|
const [sorting, setSorting] = useState<SortingState>([])
|
|
const table = useReactTable({
|
|
data,
|
|
columns: tableColumns,
|
|
state: { sorting },
|
|
onSortingChange: setSorting,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
})
|
|
|
|
return (
|
|
<Table>
|
|
<TableHeader>
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<TableRow key={headerGroup.id}>
|
|
{headerGroup.headers.map((header) => (
|
|
<TableHead key={header.id}>
|
|
{header.isPlaceholder
|
|
? null
|
|
: flexRender(header.column.columnDef.header, header.getContext())}
|
|
</TableHead>
|
|
))}
|
|
</TableRow>
|
|
))}
|
|
</TableHeader>
|
|
<TableBody>
|
|
{table.getRowModel().rows.map((row) => (
|
|
<TableRow key={row.id}>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<TableCell key={cell.id}>
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</TableCell>
|
|
))}
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
)
|
|
}
|
|
|
|
describe('TanStackTableHeadSort', () => {
|
|
it('cycles unsorted, ascending, descending, and cleared', async () => {
|
|
const user = userEvent.setup()
|
|
|
|
render(<TestTable />)
|
|
|
|
expect(screen.getAllByRole('row')[1]).toHaveTextContent('Bravo')
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Name' }))
|
|
expect(screen.getAllByRole('row')[1]).toHaveTextContent('Alpha')
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Name' }))
|
|
expect(screen.getAllByRole('row')[1]).toHaveTextContent('Bravo')
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Name' }))
|
|
expect(screen.getAllByRole('row')[1]).toHaveTextContent('Bravo')
|
|
})
|
|
|
|
it('renders non-sortable columns as plain content', () => {
|
|
render(<TestTable />)
|
|
|
|
expect(screen.getByText('Amount')).toBeInTheDocument()
|
|
expect(screen.queryByRole('button', { name: 'Amount' })).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('passes className through to the rendered sort control', () => {
|
|
render(<TestTable tableColumns={classNameColumns} />)
|
|
|
|
expect(screen.getByRole('button', { name: 'Name' })).toHaveClass('justify-end')
|
|
})
|
|
})
|