mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-18 22:33:50 +02:00
add test
This commit is contained in:
@@ -264,6 +264,13 @@
|
||||
} from '../pagination';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../table';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
|
||||
import {
|
||||
getColStyle,
|
||||
getToggleableColumns,
|
||||
isReorderable as isReorderableHelper,
|
||||
isSpacer,
|
||||
resolveHeaderLabel
|
||||
} from './dataTableHelpers.js';
|
||||
import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuTrigger } from '../context-menu';
|
||||
|
||||
import DataTableEmpty from './DataTableEmpty.vue';
|
||||
@@ -396,20 +403,7 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
const isSpacer = (col) => col?.id === '__spacer';
|
||||
|
||||
const isStretch = (col) => {
|
||||
return !!col?.columnDef?.meta?.stretch;
|
||||
};
|
||||
|
||||
const isReorderable = (header) => {
|
||||
const col = header?.column;
|
||||
if (!col) return false;
|
||||
if (isSpacer(col)) return false;
|
||||
if (getPinnedState(col)) return false;
|
||||
if (col.columnDef?.meta?.disableReorder) return false;
|
||||
return true;
|
||||
};
|
||||
const isReorderable = (header) => isReorderableHelper(header, getPinnedState);
|
||||
|
||||
const reorderableIndex = (headers, actualIndex) => {
|
||||
let sortableIdx = 0;
|
||||
@@ -463,33 +457,9 @@
|
||||
|
||||
const toggleableColumns = computed(() => {
|
||||
const cols = props.table?.getAllLeafColumns?.() ?? [];
|
||||
return cols.filter((col) => {
|
||||
if (isSpacer(col)) return false;
|
||||
if (isStretch(col)) return false;
|
||||
if (col.columnDef?.meta?.disableVisibilityToggle) return false;
|
||||
if (!col.columnDef?.meta?.label) return false;
|
||||
return true;
|
||||
});
|
||||
return getToggleableColumns(cols);
|
||||
});
|
||||
|
||||
const resolveHeaderLabel = (col) => {
|
||||
const label = col?.columnDef?.meta?.label;
|
||||
if (typeof label === 'function') return label();
|
||||
return label ?? col?.id ?? '';
|
||||
};
|
||||
|
||||
const getColStyle = (col) => {
|
||||
if (isSpacer(col)) return { width: '0px' };
|
||||
|
||||
if (isStretch(col)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const size = col?.getSize?.();
|
||||
if (!Number.isFinite(size)) return null;
|
||||
return { width: `${size}px` };
|
||||
};
|
||||
|
||||
const getHeaderClass = (header) => {
|
||||
const columnDef = header?.column?.columnDef;
|
||||
const meta = columnDef?.meta ?? {};
|
||||
|
||||
184
src/components/ui/data-table/__tests__/dataTableHelpers.test.js
Normal file
184
src/components/ui/data-table/__tests__/dataTableHelpers.test.js
Normal file
@@ -0,0 +1,184 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
getColStyle,
|
||||
getToggleableColumns,
|
||||
isReorderable,
|
||||
isSpacer,
|
||||
isStretch,
|
||||
resolveHeaderLabel
|
||||
} from '../dataTableHelpers';
|
||||
|
||||
// Helper to create a mock TanStack column instance
|
||||
const mockCol = (id, meta = {}, overrides = {}) => ({
|
||||
id,
|
||||
columnDef: { meta },
|
||||
...overrides
|
||||
});
|
||||
|
||||
describe('isSpacer', () => {
|
||||
it('returns true for __spacer column', () => {
|
||||
expect(isSpacer({ id: '__spacer' })).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for regular column', () => {
|
||||
expect(isSpacer({ id: 'name' })).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for null/undefined', () => {
|
||||
expect(isSpacer(null)).toBe(false);
|
||||
expect(isSpacer(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isStretch', () => {
|
||||
it('returns true when meta.stretch is true', () => {
|
||||
expect(isStretch(mockCol('detail', { stretch: true }))).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when meta.stretch is absent', () => {
|
||||
expect(isStretch(mockCol('name'))).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for null column', () => {
|
||||
expect(isStretch(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveHeaderLabel', () => {
|
||||
it('returns string label from meta', () => {
|
||||
expect(resolveHeaderLabel(mockCol('name', { label: 'Name' }))).toBe(
|
||||
'Name'
|
||||
);
|
||||
});
|
||||
|
||||
it('calls function label and returns result', () => {
|
||||
const col = mockCol('name', { label: () => 'Translated Name' });
|
||||
expect(resolveHeaderLabel(col)).toBe('Translated Name');
|
||||
});
|
||||
|
||||
it('falls back to column id when no label', () => {
|
||||
expect(resolveHeaderLabel(mockCol('displayName'))).toBe('displayName');
|
||||
});
|
||||
|
||||
it('returns empty string for null column', () => {
|
||||
expect(resolveHeaderLabel(null)).toBe('');
|
||||
});
|
||||
|
||||
it('returns empty string for undefined column', () => {
|
||||
expect(resolveHeaderLabel(undefined)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getToggleableColumns', () => {
|
||||
it('includes columns with meta.label', () => {
|
||||
const cols = [mockCol('name', { label: 'Name' })];
|
||||
expect(getToggleableColumns(cols)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('excludes spacer columns', () => {
|
||||
const cols = [
|
||||
mockCol('name', { label: 'Name' }),
|
||||
{ id: '__spacer', columnDef: { meta: { label: 'Spacer' } } }
|
||||
];
|
||||
expect(getToggleableColumns(cols)).toHaveLength(1);
|
||||
expect(getToggleableColumns(cols)[0].id).toBe('name');
|
||||
});
|
||||
|
||||
it('excludes stretch columns', () => {
|
||||
const cols = [
|
||||
mockCol('name', { label: 'Name' }),
|
||||
mockCol('detail', { stretch: true, label: 'Detail' })
|
||||
];
|
||||
expect(getToggleableColumns(cols)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('excludes columns with disableVisibilityToggle', () => {
|
||||
const cols = [
|
||||
mockCol('name', { label: 'Name' }),
|
||||
mockCol('hidden', {
|
||||
label: 'Hidden',
|
||||
disableVisibilityToggle: true
|
||||
})
|
||||
];
|
||||
expect(getToggleableColumns(cols)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('excludes columns without meta.label', () => {
|
||||
const cols = [
|
||||
mockCol('name', { label: 'Name' }),
|
||||
mockCol('icon'),
|
||||
mockCol('expand', {})
|
||||
];
|
||||
expect(getToggleableColumns(cols)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('returns empty array for non-array input', () => {
|
||||
expect(getToggleableColumns(null)).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty array when all columns are excluded', () => {
|
||||
const cols = [
|
||||
{ id: '__spacer', columnDef: { meta: {} } },
|
||||
mockCol('icon')
|
||||
];
|
||||
expect(getToggleableColumns(cols)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getColStyle', () => {
|
||||
it('returns width 0px for spacer column', () => {
|
||||
expect(getColStyle({ id: '__spacer' })).toEqual({ width: '0px' });
|
||||
});
|
||||
|
||||
it('returns null for stretch column', () => {
|
||||
expect(getColStyle(mockCol('detail', { stretch: true }))).toBeNull();
|
||||
});
|
||||
|
||||
it('returns width from getSize()', () => {
|
||||
const col = { ...mockCol('name'), getSize: () => 200 };
|
||||
expect(getColStyle(col)).toEqual({ width: '200px' });
|
||||
});
|
||||
|
||||
it('returns null when getSize returns non-finite', () => {
|
||||
const col = { ...mockCol('name'), getSize: () => NaN };
|
||||
expect(getColStyle(col)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when getSize is missing', () => {
|
||||
expect(getColStyle(mockCol('name'))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isReorderable', () => {
|
||||
const noPinning = () => false;
|
||||
|
||||
it('returns true for normal column', () => {
|
||||
const header = { column: mockCol('name') };
|
||||
expect(isReorderable(header, noPinning)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for spacer column', () => {
|
||||
const header = { column: { id: '__spacer', columnDef: { meta: {} } } };
|
||||
expect(isReorderable(header, noPinning)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for pinned column', () => {
|
||||
const header = { column: mockCol('name') };
|
||||
const isPinned = () => true;
|
||||
expect(isReorderable(header, isPinned)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for columns with disableReorder', () => {
|
||||
const header = { column: mockCol('name', { disableReorder: true }) };
|
||||
expect(isReorderable(header, noPinning)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for null header', () => {
|
||||
expect(isReorderable(null, noPinning)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for header without column', () => {
|
||||
expect(isReorderable({}, noPinning)).toBe(false);
|
||||
});
|
||||
});
|
||||
77
src/components/ui/data-table/dataTableHelpers.js
Normal file
77
src/components/ui/data-table/dataTableHelpers.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Pure helper functions for DataTableLayout.
|
||||
* Extracted for testability.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {object} col - TanStack column instance
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isSpacer(col) {
|
||||
return col?.id === '__spacer';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} col - TanStack column instance
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isStretch(col) {
|
||||
return !!col?.columnDef?.meta?.stretch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a column's display label for the visibility menu.
|
||||
* Supports both string and function labels (for lazy i18n).
|
||||
* @param {object} col - TanStack column instance
|
||||
* @returns {string}
|
||||
*/
|
||||
export function resolveHeaderLabel(col) {
|
||||
const label = col?.columnDef?.meta?.label;
|
||||
if (typeof label === 'function') return label();
|
||||
return label ?? col?.id ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters columns to determine which are toggleable in the visibility menu.
|
||||
* @param {Array} cols - Array of TanStack column instances
|
||||
* @returns {Array}
|
||||
*/
|
||||
export function getToggleableColumns(cols) {
|
||||
if (!Array.isArray(cols)) return [];
|
||||
return cols.filter((col) => {
|
||||
if (isSpacer(col)) return false;
|
||||
if (isStretch(col)) return false;
|
||||
if (col.columnDef?.meta?.disableVisibilityToggle) return false;
|
||||
if (!col.columnDef?.meta?.label) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the style object for a column's <col> element.
|
||||
* @param {object} col - TanStack column instance
|
||||
* @returns {object|null}
|
||||
*/
|
||||
export function getColStyle(col) {
|
||||
if (isSpacer(col)) return { width: '0px' };
|
||||
if (isStretch(col)) return null;
|
||||
|
||||
const size = col?.getSize?.();
|
||||
if (!Number.isFinite(size)) return null;
|
||||
return { width: `${size}px` };
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a header can be reordered via drag-and-drop.
|
||||
* @param {object} header - TanStack header instance
|
||||
* @param {function} getPinnedState - function to check if column is pinned
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isReorderable(header, getPinnedState) {
|
||||
const col = header?.column;
|
||||
if (!col) return false;
|
||||
if (isSpacer(col)) return false;
|
||||
if (getPinnedState?.(col)) return false;
|
||||
if (col.columnDef?.meta?.disableReorder) return false;
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user