[OT] Spread TData and TValue

This commit is contained in:
Santeri Rajamäki
2026-07-16 17:36:00 +03:00
parent 2a89f37984
commit 75d3269282
8 changed files with 53 additions and 45 deletions
@@ -4,14 +4,13 @@ import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableContainer from '@mui/material/TableContainer'
import TableHead from '@mui/material/TableHead'
import { type Table as TableType } from '@tanstack/react-table'
import type { RowData, Table as TableType } from '@tanstack/react-table'
import { ReactNode } from 'react'
import { OodiTableDataRow } from '@/components/OodiTable/components/Cell'
import { OodiTableHeaderGroup } from '@/components/OodiTable/components/Header'
import { OodiTablePagination } from '@/components/OodiTable/components/Pagination'
export const OodiTableContainer = <OTData,>({
export const OodiTableContainer = <OTData extends RowData>({
table,
toolbarContent,
isExportView,
@@ -2,12 +2,12 @@ import './styles.css'
import TableCell from '@mui/material/TableCell'
import TableRow from '@mui/material/TableRow'
import type { Row } from '@tanstack/react-table'
import type { Row, RowData } from '@tanstack/react-table'
import { flexRender } from '@/components/OodiTable/components/util'
import { getCommonPinningStyles } from '@/components/OodiTable/styles'
export const OodiTableDataRow = <OTData,>(row: Row<OTData>, aggregate = false) => (
export const OodiTableDataRow = <OTData extends RowData>(row: Row<OTData>, aggregate = false) => (
<TableRow key={row.id}>
{row.getVisibleCells().map(cell => {
const { size, maxSize } = cell.column.columnDef
@@ -1,7 +1,7 @@
import { styled, SxProps, Theme } from '@mui/material/styles'
import TableCell from '@mui/material/TableCell'
import TableRow from '@mui/material/TableRow'
import type { Header, HeaderGroup } from '@tanstack/react-table'
import type { Header, HeaderGroup, RowData } from '@tanstack/react-table'
import { useRef } from 'react'
import { OodiTableSortIcons, OtSortIconWrapper } from '@/components/OodiTable/components/SortIcons'
@@ -30,11 +30,11 @@ const OtVerticalContentWrapper = styled('div')({
margin: '0 auto',
})
const OodiTableHeaderSinglton = <OTData,>({
const OodiTableHeaderSinglton = <OTData extends RowData, OTValue = unknown>({
header,
isVerticalHeader,
}: {
header: Header<OTData, unknown>
header: Header<OTData, OTValue>
isVerticalHeader: boolean
}) => {
const base = useRef(null)
@@ -75,7 +75,10 @@ const OodiTableHeaderSinglton = <OTData,>({
)
}
export const OodiTableHeaderGroup = <OTData,>(headerGroup: HeaderGroup<OTData>, verticalHeaders: string[]) => (
export const OodiTableHeaderGroup = <OTData extends RowData>(
headerGroup: HeaderGroup<OTData>,
verticalHeaders: string[]
) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => {
if (header.depth - header.column.depth > 1) return null
@@ -1,34 +1,35 @@
import { Cell, CellContext, Column, Row, Table, TableFeature } from '@tanstack/react-table'
import type { Cell, CellContext, Column, Row, RowData, Table, TableFeature } from '@tanstack/react-table'
/**
* This doesn't implement all the features of Row, so be aware of that
*/
/* eslint-disable-line import-x/no-unused-modules */ export interface AggregationRow<TData> extends Row<TData> {
/* eslint-disable-line import-x/no-unused-modules */ export interface AggregationRow<TData extends RowData>
extends Row<TData> {
getIsAggregationRow: () => true
}
type AggregationRowValueFn<TData, TValue = unknown> = (ctx: {
type AggregationRowValueFn<TData extends RowData, TValue = unknown> = (ctx: {
table: Table<TData>
rowId: string
column: Column<TData, TValue>
}) => TValue
interface AggregationRowCellDef<TData, TValue = unknown> {
interface AggregationRowCellDef<TData extends RowData, TValue = unknown> {
id: string
value: TValue | AggregationRowValueFn<TData, TValue>
}
export type AggregationRowsInput<TData, TValue = unknown> =
export type AggregationRowsInput<TData extends RowData, TValue = unknown> =
| AggregationRowCellDef<TData, TValue>[]
| ((ctx: { table: Table<TData>; column: Column<TData> }) => AggregationRowCellDef<TData, TValue>[])
export interface AggregationRowModel<TData> {
export interface AggregationRowModel<TData extends RowData> {
rows: AggregationRow<TData>[]
flatRows: AggregationRow<TData>[]
rowsById: Record<string, AggregationRow<TData>>
}
const resolveAggregationRowsArray = <TData, TValue>(
const resolveAggregationRowsArray = <TData extends RowData, TValue>(
table: Table<TData>,
column: Column<TData, TValue>
): AggregationRowCellDef<TData, TValue>[] => {
@@ -37,7 +38,7 @@ const resolveAggregationRowsArray = <TData, TValue>(
return typeof input === 'function' ? (input({ table, column }) ?? []) : input
}
const collectAggregationRowIds = <TData>(table: Table<TData>): string[] => {
const collectAggregationRowIds = <TData extends RowData>(table: Table<TData>): string[] => {
const seen: string[] = []
const set = new Set<string>()
@@ -54,26 +55,28 @@ const collectAggregationRowIds = <TData>(table: Table<TData>): string[] => {
return seen
}
const resolveAggregationCellValue = <TData, TValue>(
const resolveAggregationCellValue = <TData extends RowData, TValue>(
table: Table<TData>,
column: Column<TData, unknown>,
column: Column<TData, TValue>,
aggRowId: string
): TValue | undefined => {
const defs = resolveAggregationRowsArray(table, column)
const def = defs.find(def => def.id === aggRowId)
if (!def) return undefined
const { value } = def
if (typeof value === 'function') {
return (value as AggregationRowValueFn<TData>)({
return (value as AggregationRowValueFn<TData, TValue>)({
table,
rowId: aggRowId,
column,
}) as TValue
})
}
return value as TValue
return value
}
const buildAggregationRowModel = <TData>(table: Table<TData>): AggregationRowModel<TData> => {
const buildAggregationRowModel = <TData extends RowData>(table: Table<TData>): AggregationRowModel<TData> => {
const aggRowIds = collectAggregationRowIds(table)
if (!aggRowIds.length) {
@@ -81,7 +84,7 @@ const buildAggregationRowModel = <TData>(table: Table<TData>): AggregationRowMod
rows: [],
flatRows: [],
rowsById: {},
} as unknown as AggregationRowModel<TData>
}
}
const visibleLeafColumns =
@@ -96,13 +99,13 @@ const buildAggregationRowModel = <TData>(table: Table<TData>): AggregationRowMod
id: aggRowId,
index,
depth: 0,
original: undefined as any,
original: undefined as TData,
parentId: undefined,
_valuesCache: {},
_uniqueValuesCache: {},
_groupingValuesCache: {},
getValue: <TValue>(columnId: string): TValue => {
const column = table.getColumn(columnId)
const column = table.getColumn(columnId) as Column<TData, TValue> | undefined
return column
? (resolveAggregationCellValue<TData, TValue>(table, column, aggRowId) as TValue)
: (undefined as TValue)
@@ -11,7 +11,7 @@ declare module '@tanstack/react-table' {
interface TableState extends VerticalHeaders {}
interface TableState extends Zebrastripes {}
interface ColumnDefBase<TData, TValue = unknown> {
interface ColumnDefBase<TData extends RowData, TValue> {
/**
* Accepts two kinds of inputs:
*
@@ -28,7 +28,7 @@ declare module '@tanstack/react-table' {
aggregationRows?: AggregationRowsInput<TData, TValue>
}
interface Table<TData> {
interface Table<TData extends RowData> {
/**
* Aggregation rows are not included in pagination/sorting.
*/
@@ -1,4 +1,4 @@
import { TableFeature } from '@tanstack/react-table'
import type { TableFeature } from '@tanstack/react-table'
export interface Zebrastripes {
useZebrastripes: boolean
@@ -1,4 +1,4 @@
import type { ColumnDef, TableOptions } from '@tanstack/react-table'
import type { ColumnDef, RowData, Table, TableOptions } from '@tanstack/react-table'
import { useReactTable, getCoreRowModel, getPaginationRowModel, getSortedRowModel } from '@tanstack/react-table'
import { useState, type ReactNode } from 'react'
@@ -19,7 +19,7 @@ import { OodiTableContainer } from '@/components/OodiTable/OodiTable'
* @returns Table instance
*/
export const OodiTable = <TData,>({
export const OodiTable = <TData extends RowData>({
data,
columns,
isExportView,
@@ -36,17 +36,18 @@ export const OodiTable = <TData,>({
}) => {
const [fallbackData] = useState()
// should maybe use a deep merging tool or write own
const { ...config }: Partial<TableOptions<TData>> = options
const table = useReactTable<TData>({
const table: Table<TData> = useReactTable<TData>(
Object.assign<TableOptions<TData>, Partial<TableOptions<TData>>>(
{
_features: [VerticalHeaderFeature, AggregationRowFeature, ZebrastripesFeature],
data: data ?? fallbackData,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
...config,
})
},
options
)
)
return <OodiTableContainer cy={cy} isExportView={isExportView} table={table} toolbarContent={toolbarContent} />
}
@@ -1,7 +1,9 @@
import type { SxProps, Theme } from '@mui/material/styles'
import type { Column } from '@tanstack/react-table'
import type { Column, RowData } from '@tanstack/react-table'
export const getCommonPinningStyles = <TData>(column: Column<TData>): SxProps<Theme> => {
export const getCommonPinningStyles = <TData extends RowData, TValue = unknown>(
column: Column<TData, TValue>
): SxProps<Theme> => {
const isPinned = column.getIsPinned()
return isPinned
? {