Harden Soma table pagination and pinning

active-uix
dev 5 months ago
parent d7e3626580
commit 108c7ceb95

@ -148,6 +148,32 @@ describe('createTable', () => {
}
});
it('normalizes invalid pagination state', () => {
const { table, cleanup } = withTable<Person>({
data: () => people,
columns,
pagination: { enabled: true },
initialPagination: { pageIndex: -4, pageSize: 0 }
});
try {
expect(table.pagination).toEqual({ pageIndex: 0, pageSize: 1 });
expect(table.pageCount).toBe(4);
expect(table.rows.map((row) => row.original.name)).toEqual(['Ada']);
table.setPageSize(2.8);
flushSync();
expect(table.pagination).toEqual({ pageIndex: 0, pageSize: 2 });
expect(table.pageCount).toBe(2);
table.setPageIndex(Number.POSITIVE_INFINITY);
flushSync();
expect(table.pagination.pageIndex).toBe(0);
} finally {
cleanup();
}
});
it('passes the source row index to accessor functions after sorting and pagination', () => {
const indexedColumns: ColumnDef<Person>[] = [
{
@ -190,6 +216,11 @@ describe('createTable', () => {
expect(table.headers.map((header) => header.id)).toEqual(['name', 'status', 'score', 'age']);
expect(table.getPinnedOffset('name')).toBe(0);
table.pinColumn('status', 'left');
flushSync();
expect(table.headers.map((header) => header.id)).toEqual(['name', 'status', 'score', 'age']);
expect(table.getIsColumnPinned('status')).toBe(false);
table.pinColumn('score', 'left');
flushSync();
expect(table.headers.map((header) => header.id)).toEqual(['name', 'score', 'status', 'age']);

@ -220,13 +220,24 @@ function applyGlobalFilter<TData>(
// ── Pagination logic ─────────────────────────────────────────────────────────
function normalizePageSize(size: number): number {
if (!Number.isFinite(size)) return 1;
return Math.max(1, Math.floor(size));
}
function normalizePageIndex(index: number): number {
if (!Number.isFinite(index)) return 0;
return Math.max(0, Math.floor(index));
}
function applyPagination<TData>(
rows: IndexedRow<TData>[],
pagination: PaginationState | undefined
): IndexedRow<TData>[] {
if (!pagination) return rows;
const start = pagination.pageIndex * pagination.pageSize;
return rows.slice(start, start + pagination.pageSize);
const pageSize = normalizePageSize(pagination.pageSize);
const start = normalizePageIndex(pagination.pageIndex) * pageSize;
return rows.slice(start, start + pageSize);
}
// ── Table row/cell models ────────────────────────────────────────────────────
@ -429,12 +440,12 @@ export function createTable<TData>(config: TableConfig<TData>): TableInstance<TD
let globalFilter = $state<string>(config.initialGlobalFilter ?? '');
let columnVisibility = $state<ColumnVisibilityState>(config.initialColumnVisibility ?? {});
let selection = $state<RowSelectionState>(config.initialSelection ?? {});
let pagination = $state<PaginationState>(
config.initialPagination ?? {
pageIndex: 0,
pageSize: config.pagination?.pageSize ?? 10
}
);
let pagination = $state<PaginationState>({
pageIndex: normalizePageIndex(config.initialPagination?.pageIndex ?? 0),
pageSize: normalizePageSize(
config.initialPagination?.pageSize ?? config.pagination?.pageSize ?? 10
)
});
let columnPinning = $state<ColumnPinningState>(config.initialColumnPinning ?? {});
let columnSizing = $state<ColumnSizingState>(config.initialColumnSizing ?? {});
let rowDetailOpen = $state<RowDetailState>(config.initialRowDetailOpen ?? {});
@ -460,7 +471,7 @@ export function createTable<TData>(config: TableConfig<TData>): TableInstance<TD
const pageCount = $derived.by(() => {
if (!config.pagination?.enabled) return 1;
return Math.max(1, Math.ceil(totalRowCount / pagination.pageSize));
return Math.max(1, Math.ceil(totalRowCount / normalizePageSize(pagination.pageSize)));
});
const paginatedRows = $derived.by(() => {
@ -712,13 +723,13 @@ export function createTable<TData>(config: TableConfig<TData>): TableInstance<TD
const canNextPage = $derived.by(() => pagination.pageIndex < pageCount - 1);
function setPageIndex(index: number) {
const clamped = Math.max(0, Math.min(index, pageCount - 1));
const clamped = Math.max(0, Math.min(normalizePageIndex(index), pageCount - 1));
pagination = { ...pagination, pageIndex: clamped };
config.onPaginationChange?.(pagination);
}
function setPageSize(size: number) {
pagination = { pageIndex: 0, pageSize: size };
pagination = { pageIndex: 0, pageSize: normalizePageSize(size) };
config.onPaginationChange?.(pagination);
}
@ -739,7 +750,7 @@ export function createTable<TData>(config: TableConfig<TData>): TableInstance<TD
function pinColumn(columnId: string, position: ColumnPinningPosition) {
const col = resolvedColumns.find((c) => c.id === columnId);
if (!col || col.def.enablePinning === false) return;
if (!col || col.def.enablePinning !== true) return;
const left = (columnPinning.left ?? []).filter((id) => id !== columnId);
const right = (columnPinning.right ?? []).filter((id) => id !== columnId);
if (position === 'left') {

Loading…
Cancel
Save

Powered by TurnKey Linux.