diff --git a/packages/table-core/tests/helpers/generateTestTable.ts b/packages/table-core/tests/helpers/generateTestTable.ts index 942ba68f02..a61edb36a4 100644 --- a/packages/table-core/tests/helpers/generateTestTable.ts +++ b/packages/table-core/tests/helpers/generateTestTable.ts @@ -6,7 +6,12 @@ import type { Person } from '../fixtures/data/types' export function generateTestTableWithData( lengths: Array | number = 10, - options?: Omit, 'data' | 'columns'>, + options?: Omit< + TableOptions, + 'data' | 'columns' | '_features' + > & { + _features?: TableFeatures + }, ) { const lengthsArray = Array.isArray(lengths) ? lengths : [lengths] const data = generateTestData(...lengthsArray) diff --git a/packages/table-core/tests/helpers/testUtils.ts b/packages/table-core/tests/helpers/testUtils.ts index c55cdb3174..697fc753ac 100644 --- a/packages/table-core/tests/helpers/testUtils.ts +++ b/packages/table-core/tests/helpers/testUtils.ts @@ -1,5 +1,4 @@ import type { vi } from 'vitest' -import type { RowPinningState } from '../../src' import type { Person } from '../fixtures/data/types' export const createArrayOfNumbers = (length: number) => { @@ -13,10 +12,7 @@ export const getPeopleIds = ( return people.map((person, index) => (usePersonId ? person.id : `${index}`)) } -export function getUpdaterResult( - mock: ReturnType, - input: RowPinningState, -) { +export function getUpdaterResult(mock: ReturnType, input: any) { const updaterFn = mock.mock.calls[0]?.[0] return updaterFn?.(input) } diff --git a/packages/table-core/tests/unit/features/column-ordering/columnOrderingFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-ordering/columnOrderingFeature.utils.test.ts new file mode 100644 index 0000000000..b6e378e614 --- /dev/null +++ b/packages/table-core/tests/unit/features/column-ordering/columnOrderingFeature.utils.test.ts @@ -0,0 +1,171 @@ +import { describe, expect, it, vi } from 'vitest' +import { generateTestTableWithData } from '../../../helpers/generateTestTable' +import { + column_getIndex, + column_getIsFirstColumn, + column_getIsLastColumn, + getDefaultColumnOrderState, + orderColumns, + table_getOrderColumnsFn, + table_resetColumnOrder, + table_setColumnOrder, +} from '../../../../src/features/column-ordering/columnOrderingFeature.utils' +import type { TableFeatures } from '../../../../src' + +describe('getDefaultColumnOrderState', () => { + it('should return an empty array', () => { + expect(getDefaultColumnOrderState()).toEqual([]) + }) +}) + +describe('column_getIndex', () => { + it('should return correct index for a column', () => { + const table = generateTestTableWithData(3) + const column = table.getAllLeafColumns()[1]! + + expect(column_getIndex(column)).toBe(1) + }) + + it('should return -1 for non-existent column', () => { + const table = generateTestTableWithData(3) + const column = { + ...table.getAllLeafColumns()[0], + id: 'non-existent', + table, + } as any + + expect(column_getIndex(column)).toBe(-1) + }) +}) + +describe('column_getIsFirstColumn', () => { + it('should return true for first column', () => { + const table = generateTestTableWithData(3) + const firstColumn = table.getAllLeafColumns()[0]! + + expect(column_getIsFirstColumn(firstColumn)).toBe(true) + }) + + it('should return false for non-first column', () => { + const table = generateTestTableWithData(3) + const secondColumn = table.getAllLeafColumns()[1]! + + expect(column_getIsFirstColumn(secondColumn)).toBe(false) + }) +}) + +describe('column_getIsLastColumn', () => { + it('should return true for last column', () => { + const table = generateTestTableWithData(3) + const columns = table.getAllLeafColumns() + const lastColumn = columns[columns.length - 1]! + + expect(column_getIsLastColumn(lastColumn)).toBe(true) + }) + + it('should return false for non-last column', () => { + const table = generateTestTableWithData(3) + const firstColumn = table.getAllLeafColumns()[0]! + + expect(column_getIsLastColumn(firstColumn)).toBe(false) + }) +}) + +describe('table_setColumnOrder', () => { + it('should call onColumnOrderChange with updater', () => { + const onColumnOrderChange = vi.fn() + const table = generateTestTableWithData(3, { + onColumnOrderChange, + }) + const newOrder = ['col1', 'col2'] + + table_setColumnOrder(table, newOrder) + + expect(onColumnOrderChange).toHaveBeenCalledWith(newOrder) + }) +}) + +describe('table_resetColumnOrder', () => { + it('should reset to empty array when defaultState is true', () => { + const onColumnOrderChange = vi.fn() + const table = generateTestTableWithData(3, { + onColumnOrderChange, + }) + + table_resetColumnOrder(table, true) + + expect(onColumnOrderChange).toHaveBeenCalledWith([]) + }) + + it('should reset to initialState when defaultState is false', () => { + const initialColumnOrder = ['col1', 'col2'] + const onColumnOrderChange = vi.fn() + const table = generateTestTableWithData(3, { + onColumnOrderChange, + initialState: { columnOrder: initialColumnOrder }, + }) + + table_resetColumnOrder(table, false) + + expect(onColumnOrderChange).toHaveBeenCalledWith(initialColumnOrder) + }) +}) + +describe('table_getOrderColumnsFn', () => { + it('should return original columns when no column order is specified', () => { + const table = generateTestTableWithData(3) + const columns = table.getAllLeafColumns() + const orderFn = table_getOrderColumnsFn(table) + + expect(orderFn(columns)).toEqual(columns) + }) + + it('should reorder columns according to columnOrder', () => { + const table = generateTestTableWithData(3, { + state: { + columnOrder: ['lastName', 'firstName'], + }, + }) + const columns = table.getAllLeafColumns() + const orderFn = table_getOrderColumnsFn(table) + const orderedColumns = orderFn(columns) + + expect(orderedColumns[0]?.id).toBe('lastName') + expect(orderedColumns[1]?.id).toBe('firstName') + }) +}) + +describe('orderColumns', () => { + it('should return original columns when no grouping is present', () => { + const table = generateTestTableWithData(3) + const columns = table.getAllLeafColumns() + + expect(orderColumns(table, columns)).toEqual(columns) + }) + + it('should remove grouped columns when groupedColumnMode is "remove"', () => { + const table = generateTestTableWithData(3, { + state: { + grouping: ['firstName'], + }, + groupedColumnMode: 'remove', + }) + const columns = table.getAllLeafColumns() + const orderedColumns = orderColumns(table, columns) + + expect(orderedColumns.find((col) => col.id === 'firstName')).toBeUndefined() + }) + + it('should move grouped columns to start when groupedColumnMode is "reorder"', () => { + const table = generateTestTableWithData(3, { + state: { + grouping: ['lastName'], + }, + groupedColumnMode: 'reorder', + }) + const columns = table.getAllLeafColumns() + const orderedColumns = orderColumns(table, columns) + + expect(orderedColumns[0]?.id).toBe('lastName') + }) +}) diff --git a/packages/table-core/tests/unit/features/column-pinning/columnPinningFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-pinning/columnPinningFeature.utils.test.ts new file mode 100644 index 0000000000..004b5a47fb --- /dev/null +++ b/packages/table-core/tests/unit/features/column-pinning/columnPinningFeature.utils.test.ts @@ -0,0 +1,726 @@ +import { describe, expect, it, vi } from 'vitest' +import { + column_getCanPin, + column_getIsPinned, + column_getPinnedIndex, + column_pin, + getDefaultColumnPinningState, + row_getCenterVisibleCells, + row_getLeftVisibleCells, + row_getRightVisibleCells, + table_getCenterFlatHeaders, + table_getCenterFooterGroups, + table_getCenterHeaderGroups, + table_getCenterLeafColumns, + table_getIsSomeColumnsPinned, + table_getLeftFlatHeaders, + table_getLeftFooterGroups, + table_getLeftHeaderGroups, + table_getLeftLeafColumns, + table_getPinnedLeafColumns, + table_getPinnedVisibleLeafColumns, + table_getRightFlatHeaders, + table_getRightFooterGroups, + table_getRightHeaderGroups, + table_getRightLeafColumns, + table_getVisibleLeafColumns, + table_resetColumnPinning, + table_setColumnPinning, +} from '../../../../src' +import { generateTestTableWithData } from '../../../helpers/generateTestTable' +import { getUpdaterResult } from '../../../helpers/testUtils' + +describe('getDefaultColumnPinningState', () => { + it('should return default column pinning state', () => { + const result = getDefaultColumnPinningState() + expect(result).toEqual({ + left: [], + right: [], + }) + }) +}) + +describe('column_pin', () => { + it('should pin column to the left', () => { + const onColumnPinningChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnPinningChange, + state: { + columnPinning: { + left: [], + right: [], + }, + }, + }) + const column = table.getAllColumns()[0]! + + column_pin(column, 'left') + + const result = getUpdaterResult(onColumnPinningChange, { + left: [], + right: [], + }) + + expect(result).toEqual({ + left: [column.id], + right: [], + }) + }) + + it('should pin column to the right', () => { + const onColumnPinningChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnPinningChange, + state: { + columnPinning: { + left: [], + right: [], + }, + }, + }) + const column = table.getAllColumns()[0]! + + column_pin(column, 'right') + + const result = getUpdaterResult(onColumnPinningChange, { + left: [], + right: [], + }) + + expect(result).toEqual({ + left: [], + right: [column.id], + }) + }) + + it('should unpin column when false is passed', () => { + const onColumnPinningChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnPinningChange, + state: { + columnPinning: { + left: ['id'], + right: [], + }, + }, + }) + const column = table.getColumn('id')! + + column_pin(column, false) + + const result = getUpdaterResult(onColumnPinningChange, { + left: ['id'], + right: [], + }) + + expect(result).toEqual({ + left: [], + right: [], + }) + }) +}) + +describe('column_getCanPin', () => { + it('should return true when column pinning is enabled', () => { + const table = generateTestTableWithData(1) + const column = table.getAllColumns()[0] + + const result = column_getCanPin(column as any) + + expect(result).toBe(true) + }) + + it('should return false when column pinning is disabled globally', () => { + const table = generateTestTableWithData(1, { + enableColumnPinning: false, + }) + const column = table.getAllColumns()[0] + + const result = column_getCanPin(column as any) + + expect(result).toBe(false) + }) + + it('should return false when column pinning is disabled for specific column', () => { + const table = generateTestTableWithData(1) + const column = { + ...table.getAllColumns()[0], + columnDef: { enablePinning: false }, + table: table, + getLeafColumns: () => [ + { + ...table.getAllColumns()[0], + columnDef: { enablePinning: false }, + }, + ], + } + + const result = column_getCanPin(column as any) + + expect(result).toBe(false) + }) +}) + +describe('column_getIsPinned', () => { + it('should return left when column is pinned left', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + const column = table.getColumn('firstName')! + + const result = column_getIsPinned(column) + + expect(result).toBe('left') + }) + + it('should return right when column is pinned right', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['firstName'], + }, + }, + }) + const column = table.getColumn('firstName')! + + const result = column_getIsPinned(column) + + expect(result).toBe('right') + }) + + it('should return false when column is not pinned', () => { + const table = generateTestTableWithData(1) + const column = table.getColumn('firstName')! + + const result = column_getIsPinned(column) + + expect(result).toBe(false) + }) +}) + +describe('table_setColumnPinning', () => { + it('should call onColumnPinningChange with updater', () => { + const onColumnPinningChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnPinningChange, + }) + + table_setColumnPinning(table as any, { + left: ['firstName'], + right: [], + }) + + expect(onColumnPinningChange).toHaveBeenCalledWith({ + left: ['firstName'], + right: [], + }) + }) +}) + +describe('table_resetColumnPinning', () => { + it('should reset to default state when defaultState is true', () => { + const onColumnPinningChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnPinningChange, + }) + + table_resetColumnPinning(table as any, true) + + expect(onColumnPinningChange).toHaveBeenCalledWith({ + left: [], + right: [], + }) + }) + + it('should reset to initial state when defaultState is false', () => { + const onColumnPinningChange = vi.fn() + const initialState = { + columnPinning: { + left: ['firstName'], + right: [], + }, + } + const table = generateTestTableWithData(1, { + onColumnPinningChange, + initialState, + }) + + table_resetColumnPinning(table as any, false) + + expect(onColumnPinningChange).toHaveBeenCalledWith({ + left: ['firstName'], + right: [], + }) + }) +}) + +describe('table_getIsSomeColumnsPinned', () => { + it('should return true when columns are pinned left', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + const result = table_getIsSomeColumnsPinned(table as any) + + expect(result).toBe(true) + }) + + it('should return true when columns are pinned right', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['firstName'], + }, + }, + }) + + const result = table_getIsSomeColumnsPinned(table as any) + + expect(result).toBe(true) + }) + + it('should return false when no columns are pinned', () => { + const table = generateTestTableWithData(1) + + const result = table_getIsSomeColumnsPinned(table as any) + + expect(result).toBe(false) + }) + + it('should check specific position when position parameter is provided', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + expect(table_getIsSomeColumnsPinned(table as any, 'left')).toBe(true) + expect(table_getIsSomeColumnsPinned(table as any, 'right')).toBe(false) + }) +}) + +describe('column_getPinnedIndex', () => { + it('should return index of pinned column', () => { + const table = generateTestTableWithData(2, { + state: { + columnPinning: { + left: ['firstName', 'lastName'], + right: [], + }, + }, + }) + const column = table.getColumn('lastName')! + + const result = column_getPinnedIndex(column) + + expect(result).toBe(1) + }) + + it('should return 0 when column is not pinned', () => { + const table = generateTestTableWithData(1) + const column = table.getColumn('firstName')! + + const result = column_getPinnedIndex(column) + + expect(result).toBe(0) + }) +}) + +describe('row_getCenterVisibleCells', () => { + it('should return only unpinned visible cells', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + const row = table.getRowModel().rows[0]! + + const centerCells = row_getCenterVisibleCells(row) + + expect(centerCells.map((cell) => cell.column.id)).not.toContain('firstName') + expect(centerCells.map((cell) => cell.column.id)).not.toContain('lastName') + expect(centerCells.length).toBeGreaterThan(0) + }) +}) + +describe('row_getLeftVisibleCells', () => { + it('should return only left pinned cells', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + const row = table.getRowModel().rows[0]! + + const leftCells = row_getLeftVisibleCells(row) + + expect(leftCells).toHaveLength(1) + expect(leftCells[0]?.column.id).toBe('firstName') + }) + + it('should return empty array when no columns are pinned left', () => { + const table = generateTestTableWithData(1) + const row = table.getRowModel().rows[0]! + + const leftCells = row_getLeftVisibleCells(row) + + expect(leftCells).toHaveLength(0) + }) +}) + +describe('row_getRightVisibleCells', () => { + it('should return only right pinned cells', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + const row = table.getRowModel().rows[0]! + + const rightCells = row_getRightVisibleCells(row) + + expect(rightCells).toHaveLength(1) + expect(rightCells[0]?.column.id).toBe('lastName') + }) + + it('should return empty array when no columns are pinned right', () => { + const table = generateTestTableWithData(1) + const row = table.getRowModel().rows[0]! + + const rightCells = row_getRightVisibleCells(row) + + expect(rightCells).toHaveLength(0) + }) +}) + +describe('table_getLeftHeaderGroups', () => { + it('should return header groups for left pinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + const headerGroups = table_getLeftHeaderGroups(table as any) + + expect(headerGroups[0]?.headers[0]?.column.id).toBe('firstName') + }) +}) + +describe('table_getRightHeaderGroups', () => { + it('should return header groups for right pinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['lastName'], + }, + }, + }) + + const headerGroups = table_getRightHeaderGroups(table as any) + + expect(headerGroups[0]?.headers[0]?.column.id).toBe('lastName') + }) +}) + +describe('table_getCenterHeaderGroups', () => { + it('should return header groups for unpinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + + const headerGroups = table_getCenterHeaderGroups(table as any) + const centerColumnIds = headerGroups[0]?.headers.map( + (header) => header.column.id, + ) + + expect(centerColumnIds).not.toContain('firstName') + expect(centerColumnIds).not.toContain('lastName') + expect(headerGroups[0]?.headers.length).toBeGreaterThan(0) + }) +}) + +describe('table_getLeftLeafColumns', () => { + it('should return left pinned leaf columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + const leafColumns = table_getLeftLeafColumns(table as any) + + expect(leafColumns).toHaveLength(1) + expect(leafColumns[0]?.id).toBe('firstName') + }) +}) + +describe('table_getRightLeafColumns', () => { + it('should return right pinned leaf columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['lastName'], + }, + }, + }) + + const leafColumns = table_getRightLeafColumns(table as any) + + expect(leafColumns).toHaveLength(1) + expect(leafColumns[0]?.id).toBe('lastName') + }) +}) + +describe('table_getCenterLeafColumns', () => { + it('should return unpinned leaf columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + + const leafColumns = table_getCenterLeafColumns(table as any) + const centerColumnIds = leafColumns.map((col) => col.id) + + expect(centerColumnIds).not.toContain('firstName') + expect(centerColumnIds).not.toContain('lastName') + expect(leafColumns.length).toBeGreaterThan(0) + }) +}) + +describe('table_getPinnedLeafColumns', () => { + it('should return left pinned leaf columns when position is left', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + const leafColumns = table_getPinnedLeafColumns(table as any, 'left') + + expect(leafColumns).toHaveLength(1) + expect(leafColumns[0]?.id).toBe('firstName') + }) + + it('should return right pinned leaf columns when position is right', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['lastName'], + }, + }, + }) + + const leafColumns = table_getPinnedLeafColumns(table as any, 'right') + + expect(leafColumns).toHaveLength(1) + expect(leafColumns[0]?.id).toBe('lastName') + }) + + it('should return center leaf columns when position is center', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + + const leafColumns = table_getPinnedLeafColumns(table as any, 'center') + + expect(leafColumns.length).toBeGreaterThan(0) + expect(leafColumns.map((col) => col.id)).not.toContain('firstName') + expect(leafColumns.map((col) => col.id)).not.toContain('lastName') + }) +}) + +describe('table_getPinnedVisibleLeafColumns', () => { + it('should return visible leaf columns for specified position', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + columnVisibility: { + age: false, + }, + }, + }) + + const leftColumns = table_getPinnedVisibleLeafColumns(table as any, 'left') + const rightColumns = table_getPinnedVisibleLeafColumns(table, 'right') + const centerColumns = table_getPinnedVisibleLeafColumns(table, 'center') + + expect(leftColumns[0]?.id).toBe('firstName') + expect(rightColumns[0]?.id).toBe('lastName') + expect(centerColumns.map((col) => col.id)).not.toContain('age') + }) + + it('should return all visible leaf columns when no position specified', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + age: false, + }, + }, + }) + + const leafColumns = table_getPinnedVisibleLeafColumns(table as any) + + expect(leafColumns.map((col) => col.id)).not.toContain('age') + expect(leafColumns.length).toBe( + table_getVisibleLeafColumns(table as any).length, + ) + }) +}) + +describe('table_getFooterGroups', () => { + it('should return footer groups for left pinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + const footerGroups = table_getLeftFooterGroups(table as any) + + expect(footerGroups[0]?.headers[0]?.column.id).toBe('firstName') + }) + + it('should return footer groups for right pinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['lastName'], + }, + }, + }) + + const footerGroups = table_getRightFooterGroups(table as any) + + expect(footerGroups[0]?.headers[0]?.column.id).toBe('lastName') + }) + + it('should return footer groups for center columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + + const footerGroups = table_getCenterFooterGroups(table as any) + const centerColumnIds = footerGroups[0]?.headers.map( + (header) => header.column.id, + ) + + expect(centerColumnIds).not.toContain('firstName') + expect(centerColumnIds).not.toContain('lastName') + expect(footerGroups[0]?.headers.length).toBeGreaterThan(0) + }) +}) + +describe('table_getFlatHeaders', () => { + it('should return flat headers for left pinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: [], + }, + }, + }) + + const flatHeaders = table_getLeftFlatHeaders(table as any) + + expect(flatHeaders).toHaveLength(1) + expect(flatHeaders[0]?.column.id).toBe('firstName') + }) + + it('should return flat headers for right pinned columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: [], + right: ['lastName'], + }, + }, + }) + + const flatHeaders = table_getRightFlatHeaders(table as any) + + expect(flatHeaders).toHaveLength(1) + expect(flatHeaders[0]?.column.id).toBe('lastName') + }) + + it('should return flat headers for center columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnPinning: { + left: ['firstName'], + right: ['lastName'], + }, + }, + }) + + const flatHeaders = table_getCenterFlatHeaders(table as any) + const centerColumnIds = flatHeaders.map((header) => header.column.id) + + expect(centerColumnIds).not.toContain('firstName') + expect(centerColumnIds).not.toContain('lastName') + expect(flatHeaders.length).toBeGreaterThan(0) + }) +}) diff --git a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts new file mode 100644 index 0000000000..560e0e42ad --- /dev/null +++ b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts @@ -0,0 +1,345 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { + column_getCanResize, + column_getIsResizing, + getDefaultColumnResizingState, + header_getResizeHandler, + isTouchStartEvent, + passiveEventSupported, + table_resetHeaderSizeInfo, + table_setColumnResizing, +} from '../../../../src' +import { generateTestTableWithData } from '../../../helpers/generateTestTable' + +// Add type for the features we need +type TestFeatures = { + columnResizingFeature: {} + columnSizingFeature: {} +} + +// Helper function to create a properly structured test header +function createTestResizeHeader(table: any, overrides = {}) { + const baseColumn = { + ...table.getAllColumns()[0], + id: 'firstName', + columnDef: { + enableResizing: true, + }, + table, + getLeafColumns: () => [ + { + ...table.getAllColumns()[0], + id: 'firstName', + columnDef: { + enableResizing: true, + }, + }, + ], + } + + return { + column: baseColumn, + getLeafHeaders: () => [ + { + column: baseColumn, + getSize: () => 100, + subHeaders: [], + }, + ], + subHeaders: [], + getSize: () => 100, + ...overrides, + } +} + +describe('getDefaultColumnResizingState', () => { + it('should return default column resizing state', () => { + const result = getDefaultColumnResizingState() + expect(result).toEqual({ + startOffset: null, + startSize: null, + deltaOffset: null, + deltaPercentage: null, + isResizingColumn: false, + columnSizingStart: [], + }) + }) +}) + +describe('column_getCanResize', () => { + it('should return true when column resizing is enabled', () => { + const table = generateTestTableWithData(1) + const column = { + ...table.getAllColumns()[0], + columnDef: {}, + table, + } + + const result = column_getCanResize(column as any) + + expect(result).toBe(true) + }) + + it('should return false when column resizing is disabled globally', () => { + const table = generateTestTableWithData(1, { + enableColumnResizing: false, + }) + const column = { + ...table.getAllColumns()[0], + columnDef: {}, + table, + } + + const result = column_getCanResize(column as any) + + expect(result).toBe(false) + }) + + it('should return false when column resizing is disabled for specific column', () => { + const table = generateTestTableWithData(1) + const column = { + ...table.getAllColumns()[0], + columnDef: { enableResizing: false }, + table, + } + + const result = column_getCanResize(column as any) + + expect(result).toBe(false) + }) +}) + +describe('column_getIsResizing', () => { + it('should return true when column is being resized', () => { + const table = generateTestTableWithData(1, { + state: { + columnResizing: { + isResizingColumn: 'firstName', + columnSizingStart: [], + deltaOffset: null, + deltaPercentage: null, + startOffset: null, + startSize: null, + }, + }, + }) + const column = { + ...table.getAllColumns()[0], + id: 'firstName', + table, + } + + const result = column_getIsResizing(column as any) + + expect(result).toBe(true) + }) + + it('should return false when column is not being resized', () => { + const table = generateTestTableWithData(1) + const column = { + ...table.getAllColumns()[0], + table, + } + + const result = column_getIsResizing(column as any) + + expect(result).toBe(false) + }) +}) + +describe('table_setColumnResizing', () => { + it('should call onColumnResizingChange with updater', () => { + const onColumnResizingChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnResizingChange, + }) + + const newState = { + startOffset: 100, + startSize: 200, + deltaOffset: 50, + deltaPercentage: 0.25, + isResizingColumn: 'firstName', + columnSizingStart: [], + } + + table_setColumnResizing(table, newState) + + expect(onColumnResizingChange).toHaveBeenCalledWith(newState) + }) +}) + +describe('table_resetHeaderSizeInfo', () => { + it('should reset to default state when defaultState is true', () => { + const onColumnResizingChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnResizingChange, + }) + + table_resetHeaderSizeInfo(table, true) + + expect(onColumnResizingChange).toHaveBeenCalledWith( + getDefaultColumnResizingState(), + ) + }) + + it('should reset to initial state when defaultState is false', () => { + const initialState = { + columnResizing: { + startOffset: 100, + startSize: 200, + deltaOffset: 50, + deltaPercentage: 0.25, + isResizingColumn: 'firstName', + columnSizingStart: [], + }, + } + const onColumnResizingChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnResizingChange, + initialState, + }) + + table_resetHeaderSizeInfo(table, false) + + expect(onColumnResizingChange).toHaveBeenCalledWith( + initialState.columnResizing, + ) + }) +}) + +describe('isTouchStartEvent', () => { + it('should return true for touch start events', () => { + const event = { type: 'touchstart' } + + const result = isTouchStartEvent(event) + + expect(result).toBe(true) + }) + + it('should return false for non-touch start events', () => { + const event = { type: 'mousedown' } + + const result = isTouchStartEvent(event) + + expect(result).toBe(false) + }) +}) + +describe('header_getResizeHandler', () => { + beforeEach(() => { + vi.restoreAllMocks() + }) + + it('should return a function', () => { + const table = generateTestTableWithData(1, { + state: { + columnSizing: {}, + }, + }) + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any) + expect(typeof handler).toBe('function') + }) + + it('should not resize when column resizing is disabled', () => { + const table = generateTestTableWithData(1, { + enableColumnResizing: false, + }) + const onColumnResizingChange = vi.fn() + table.options.onColumnResizingChange = onColumnResizingChange + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any) + handler({ type: 'mousedown', clientX: 100 }) + + expect(onColumnResizingChange).not.toHaveBeenCalled() + }) + + it('should ignore multi-touch events', () => { + const table = generateTestTableWithData(1) + const onColumnResizingChange = vi.fn() + table.options.onColumnResizingChange = onColumnResizingChange + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any) + handler({ + type: 'touchstart', + touches: [{ clientX: 100 }, { clientX: 200 }], + }) + + expect(onColumnResizingChange).not.toHaveBeenCalled() + }) + + it('should update immediately in onChange mode', () => { + const table = generateTestTableWithData(1, { + columnResizeMode: 'onChange', + }) + const onColumnSizingChange = vi.fn() + table.options.onColumnSizingChange = onColumnSizingChange + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any) + handler({ type: 'mousedown', clientX: 100 }) + + // Simulate mouse move + const moveEvent = new MouseEvent('mousemove', { clientX: 150 }) + document.dispatchEvent(moveEvent) + + expect(onColumnSizingChange).toHaveBeenCalled() + }) + + it('should cleanup event listeners on mouse up', () => { + const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener') + const table = generateTestTableWithData(1) + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 100 }) + + // Clear the spy calls from setup + removeEventListenerSpy.mockClear() + + // Simulate mouse up + const upEvent = new MouseEvent('mouseup', { clientX: 150 }) + document.dispatchEvent(upEvent) + + // Should remove mousemove and mouseup listeners + expect(removeEventListenerSpy).toHaveBeenCalledTimes(4) + expect(removeEventListenerSpy).toHaveBeenCalledWith( + 'mousemove', + expect.any(Function), + ) + expect(removeEventListenerSpy).toHaveBeenCalledWith( + 'mouseup', + expect.any(Function), + ) + + removeEventListenerSpy.mockRestore() + }) +}) + +describe('passiveEventSupported', () => { + it('should return boolean indicating passive event support', () => { + const result = passiveEventSupported() + expect(typeof result).toBe('boolean') + }) + + it('should cache the result of passive support check', () => { + const firstResult = passiveEventSupported() + const secondResult = passiveEventSupported() + expect(firstResult).toBe(secondResult) + }) + + it('should handle errors during support check', () => { + const addEventListenerSpy = vi.spyOn(window, 'addEventListener') + addEventListenerSpy.mockImplementation(() => { + throw new Error('Test error') + }) + + const result = passiveEventSupported() + expect(result).toBe(false) + + addEventListenerSpy.mockRestore() + }) +}) diff --git a/packages/table-core/tests/unit/features/column-visibility/columnVisibilityFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-visibility/columnVisibilityFeature.utils.test.ts new file mode 100644 index 0000000000..916ec45da4 --- /dev/null +++ b/packages/table-core/tests/unit/features/column-visibility/columnVisibilityFeature.utils.test.ts @@ -0,0 +1,400 @@ +import { describe, expect, it, vi } from 'vitest' +import { + column_getCanHide, + column_getIsVisible, + column_getToggleVisibilityHandler, + column_toggleVisibility, + getDefaultColumnVisibilityState, + row_getAllVisibleCells, + row_getVisibleCells, + table_getIsAllColumnsVisible, + table_getIsSomeColumnsVisible, + table_getToggleAllColumnsVisibilityHandler, + table_getVisibleFlatColumns, + table_getVisibleLeafColumns, + table_resetColumnVisibility, + table_setColumnVisibility, + table_toggleAllColumnsVisible, +} from '../../../../src' +import { generateTestTableWithData } from '../../../helpers/generateTestTable' +import { getUpdaterResult } from '../../../helpers/testUtils' + +type TestFeatures = { + columnVisibilityFeature: {} +} + +describe('getDefaultColumnVisibilityState', () => { + it('should return empty object', () => { + const result = getDefaultColumnVisibilityState() + expect(result).toEqual({}) + }) +}) + +describe('column_getIsVisible', () => { + it('should return true by default', () => { + const table = generateTestTableWithData(1) + const column = table.getAllColumns()[0]! + + const result = column_getIsVisible(column) + + expect(result).toBe(true) + }) + + it('should return false when column is hidden', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + firstName: false, + }, + }, + }) + const column = { + ...table.getAllColumns()[0]!, + id: 'firstName', + table, + } + + const result = column_getIsVisible(column as any) + + expect(result).toBe(false) + }) + + it('should return true if any child column is visible', () => { + const table = generateTestTableWithData(1) + const parentColumn = { + ...table.getAllColumns()[0]!, + columns: [ + { ...table.getAllColumns()[0]!, id: 'child1' }, + { ...table.getAllColumns()[1]!, id: 'child2' }, + ], + } + + const result = column_getIsVisible(parentColumn as any) + + expect(result).toBe(true) + }) +}) + +describe('column_getCanHide', () => { + it('should return true by default', () => { + const table = generateTestTableWithData(1) + const column = table.getAllColumns()[0]! + + const result = column_getCanHide(column) + + expect(result).toBe(true) + }) + + it('should return false when hiding is disabled globally', () => { + const table = generateTestTableWithData(1, { + enableHiding: false, + }) + const column = table.getAllColumns()[0]! + + const result = column_getCanHide(column) + + expect(result).toBe(false) + }) + + it('should return false when hiding is disabled for column', () => { + const table = generateTestTableWithData(1) + const column = { + ...table.getAllColumns()[0]!, + columnDef: { enableHiding: false }, + } + + const result = column_getCanHide(column as any) + + expect(result).toBe(false) + }) +}) + +describe('column_toggleVisibility', () => { + it('should toggle column visibility', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + const column = { + ...table.getAllColumns()[0]!, + id: 'firstName', + table, + } + + column_toggleVisibility(column as any) + + const result = getUpdaterResult(onColumnVisibilityChange, {}) + expect(result).toEqual({ firstName: false }) + }) + + it('should set specific visibility when provided', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + const column = { + ...table.getAllColumns()[0]!, + id: 'firstName', + table, + } + + column_toggleVisibility(column as any, true) + + const result = getUpdaterResult(onColumnVisibilityChange, {}) + expect(result).toEqual({ firstName: true }) + }) + + it('should not toggle when column cannot be hidden', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + enableHiding: false, + onColumnVisibilityChange, + }) + const column = { + ...table.getAllColumns()[0]!, + id: 'firstName', + table, + } + + column_toggleVisibility(column as any) + + expect(onColumnVisibilityChange).not.toHaveBeenCalled() + }) +}) + +describe('column_getToggleVisibilityHandler', () => { + it('should return handler that toggles visibility based on checkbox state', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + const column = { + ...table.getAllColumns()[0]!, + id: 'firstName', + table, + } + const handler = column_getToggleVisibilityHandler(column as any) + + handler({ target: { checked: true } } as any) + + const result = getUpdaterResult(onColumnVisibilityChange, {}) + expect(result).toEqual({ firstName: true }) + }) +}) + +describe('row_getAllVisibleCells', () => { + it('should return only visible cells', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + firstName: false, + }, + }, + }) + const row = table.getRowModel().rows[0]! + + const visibleCells = row_getAllVisibleCells(row) + const visibleColumnIds = visibleCells.map((cell) => cell.column.id) + + expect(visibleColumnIds).not.toContain('firstName') + expect(visibleCells.length).toBe(row.getAllCells().length - 1) + }) +}) + +describe('row_getVisibleCells', () => { + it('should combine left, center and right cells', () => { + const leftCells = [{ id: 'left' }] + const centerCells = [{ id: 'center' }] + const rightCells = [{ id: 'right' }] + + const result = row_getVisibleCells( + leftCells as any, + centerCells as any, + rightCells as any, + ) + + expect(result).toEqual([...leftCells, ...centerCells, ...rightCells]) + }) +}) + +describe('table_getVisibleFlatColumns', () => { + it('should return only visible flat columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + firstName: false, + }, + }, + }) + + const visibleColumns = table_getVisibleFlatColumns(table) + const visibleColumnIds = visibleColumns.map((col) => col.id) + + expect(visibleColumnIds).not.toContain('firstName') + expect(visibleColumns.length).toBe(table.getAllFlatColumns().length - 1) + }) +}) + +describe('table_getVisibleLeafColumns', () => { + it('should return only visible leaf columns', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + firstName: false, + }, + }, + }) + + const visibleColumns = table_getVisibleLeafColumns(table) + const visibleColumnIds = visibleColumns.map((col) => col.id) + + expect(visibleColumnIds).not.toContain('firstName') + expect(visibleColumns.length).toBe(table.getAllLeafColumns().length - 1) + }) +}) + +describe('table_setColumnVisibility', () => { + it('should call onColumnVisibilityChange with updater', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + + table_setColumnVisibility(table, { firstName: false }) + + expect(onColumnVisibilityChange).toHaveBeenCalledWith({ firstName: false }) + }) +}) + +describe('table_resetColumnVisibility', () => { + it('should reset to empty state when defaultState is true', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + + table_resetColumnVisibility(table, true) + + expect(onColumnVisibilityChange).toHaveBeenCalledWith({}) + }) + + it('should reset to initial state when defaultState is false', () => { + const initialState = { columnVisibility: { firstName: false } } + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + initialState, + onColumnVisibilityChange, + }) + + table_resetColumnVisibility(table, false) + + expect(onColumnVisibilityChange).toHaveBeenCalledWith({ firstName: false }) + }) +}) + +describe('table_toggleAllColumnsVisible', () => { + it('should show all columns when value is true', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + + table_toggleAllColumnsVisible(table, true) + + expect(onColumnVisibilityChange).toHaveBeenCalled() + const result = onColumnVisibilityChange.mock.calls[0]?.[0] + const allColumnIds = table.getAllLeafColumns().map((col) => col.id) + expect(Object.entries(result)).toEqual(allColumnIds.map((id) => [id, true])) + }) + + it('should hide all columns that can be hidden when value is false', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + + table_toggleAllColumnsVisible(table, false) + + expect(onColumnVisibilityChange).toHaveBeenCalled() + const result = onColumnVisibilityChange.mock.calls[0]?.[0] + const allColumnIds = table.getAllLeafColumns().map((col) => col.id) + expect(Object.entries(result)).toEqual( + allColumnIds.map((id) => [id, false]), + ) + }) +}) + +describe('table_getIsAllColumnsVisible', () => { + it('should return true when all columns are visible', () => { + const table = generateTestTableWithData(1) + + const result = table_getIsAllColumnsVisible(table) + + expect(result).toBe(true) + }) + + it('should return false when some columns are hidden', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + firstName: false, + }, + }, + }) + + const result = table_getIsAllColumnsVisible(table) + + expect(result).toBe(false) + }) +}) + +describe('table_getIsSomeColumnsVisible', () => { + it('should return true when some columns are visible', () => { + const table = generateTestTableWithData(1, { + state: { + columnVisibility: { + firstName: false, + }, + }, + }) + + const result = table_getIsSomeColumnsVisible(table) + + expect(result).toBe(true) + }) + + it('should return false when no columns are visible', () => { + const table = generateTestTableWithData(1) + const allColumnIds = table.getAllLeafColumns().map((col) => col.id) + const hideAllColumns = Object.fromEntries( + allColumnIds.map((id) => [id, false]), + ) + + const tableWithHiddenColumns = generateTestTableWithData(1, { + state: { + columnVisibility: hideAllColumns, + }, + }) + + const result = table_getIsSomeColumnsVisible(tableWithHiddenColumns) + + expect(result).toBe(false) + }) +}) + +describe('table_getToggleAllColumnsVisibilityHandler', () => { + it('should return handler that toggles all columns visibility based on checkbox state', () => { + const onColumnVisibilityChange = vi.fn() + const table = generateTestTableWithData(1, { + onColumnVisibilityChange, + }) + const handler = table_getToggleAllColumnsVisibilityHandler(table) + + handler({ target: { checked: true } } as any) + + expect(onColumnVisibilityChange).toHaveBeenCalled() + const result = onColumnVisibilityChange.mock.calls[0]?.[0] + const allColumnIds = table.getAllLeafColumns().map((col) => col.id) + expect(Object.entries(result)).toEqual(allColumnIds.map((id) => [id, true])) + }) +})