mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-17 22:03:50 +02:00
feat: datatable sorting persist
This commit is contained in:
126
src/lib/table/__tests__/useVrcxVueTable.test.js
Normal file
126
src/lib/table/__tests__/useVrcxVueTable.test.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { useVrcxVueTable } from '../useVrcxVueTable';
|
||||
|
||||
function makeColumns(...ids) {
|
||||
return ids.map((id) => ({ id, header: id, accessorKey: id }));
|
||||
}
|
||||
|
||||
describe('useVrcxVueTable persistence', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it('persists sorting to localStorage when sorting changes', async () => {
|
||||
const { sorting } = useVrcxVueTable({
|
||||
data: [],
|
||||
columns: makeColumns('name', 'date'),
|
||||
persistKey: 'test-sort',
|
||||
initialSorting: []
|
||||
});
|
||||
|
||||
sorting.value = [{ id: 'name', desc: false }];
|
||||
|
||||
// Wait for debounce (200ms default)
|
||||
await new Promise((r) => setTimeout(r, 300));
|
||||
|
||||
const stored = JSON.parse(localStorage.getItem('vrcx:table:test-sort'));
|
||||
expect(stored).toBeTruthy();
|
||||
expect(stored.sorting).toEqual([{ id: 'name', desc: false }]);
|
||||
});
|
||||
|
||||
it('restores persisted sorting on init, overriding initialSorting', () => {
|
||||
localStorage.setItem(
|
||||
'vrcx:table:test-restore',
|
||||
JSON.stringify({ sorting: [{ id: 'date', desc: true }] })
|
||||
);
|
||||
|
||||
const { sorting } = useVrcxVueTable({
|
||||
data: [],
|
||||
columns: makeColumns('name', 'date'),
|
||||
persistKey: 'test-restore',
|
||||
initialSorting: [{ id: 'name', desc: false }]
|
||||
});
|
||||
|
||||
expect(sorting.value).toEqual([{ id: 'date', desc: true }]);
|
||||
});
|
||||
|
||||
it('filters out stale sorting entries for removed columns', () => {
|
||||
localStorage.setItem(
|
||||
'vrcx:table:test-stale',
|
||||
JSON.stringify({
|
||||
sorting: [
|
||||
{ id: 'removed_col', desc: false },
|
||||
{ id: 'name', desc: true }
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
const { sorting } = useVrcxVueTable({
|
||||
data: [],
|
||||
columns: makeColumns('name', 'date'),
|
||||
persistKey: 'test-stale',
|
||||
initialSorting: []
|
||||
});
|
||||
|
||||
// Stale entry should have been loaded but will be filtered on next persist write
|
||||
// On init, the raw persisted array is loaded as-is
|
||||
expect(sorting.value).toContainEqual({ id: 'name', desc: true });
|
||||
});
|
||||
|
||||
it('does not persist sorting when persistSorting is false', async () => {
|
||||
const { sorting } = useVrcxVueTable({
|
||||
data: [],
|
||||
columns: makeColumns('name', 'date'),
|
||||
persistKey: 'test-no-persist-sort',
|
||||
persistSorting: false,
|
||||
initialSorting: []
|
||||
});
|
||||
|
||||
sorting.value = [{ id: 'name', desc: false }];
|
||||
|
||||
await new Promise((r) => setTimeout(r, 300));
|
||||
|
||||
const stored = JSON.parse(
|
||||
localStorage.getItem('vrcx:table:test-no-persist-sort')
|
||||
);
|
||||
// Should be null or not contain sorting
|
||||
expect(stored?.sorting).toBeUndefined();
|
||||
});
|
||||
|
||||
it('still persists columnSizing alongside sorting', async () => {
|
||||
const { columnSizing, sorting } = useVrcxVueTable({
|
||||
data: [],
|
||||
columns: makeColumns('name', 'date'),
|
||||
persistKey: 'test-both',
|
||||
initialSorting: []
|
||||
});
|
||||
|
||||
columnSizing.value = { name: 200 };
|
||||
|
||||
// Wait for columnSizing debounce to fire first
|
||||
await new Promise((r) => setTimeout(r, 300));
|
||||
|
||||
sorting.value = [{ id: 'date', desc: true }];
|
||||
|
||||
// Wait for sorting debounce to fire
|
||||
await new Promise((r) => setTimeout(r, 300));
|
||||
|
||||
const stored = JSON.parse(localStorage.getItem('vrcx:table:test-both'));
|
||||
expect(stored).toBeTruthy();
|
||||
// Both keys should be present since writePersisted merges patches
|
||||
expect('columnSizing' in stored).toBe(true);
|
||||
expect(stored.sorting).toEqual([{ id: 'date', desc: true }]);
|
||||
});
|
||||
|
||||
it('uses initialSorting when no persisted data exists', () => {
|
||||
const { sorting } = useVrcxVueTable({
|
||||
data: [],
|
||||
columns: makeColumns('name', 'date'),
|
||||
persistKey: 'test-initial',
|
||||
initialSorting: [{ id: 'date', desc: true }]
|
||||
});
|
||||
|
||||
expect(sorting.value).toEqual([{ id: 'date', desc: true }]);
|
||||
});
|
||||
});
|
||||
@@ -44,6 +44,14 @@ function filterSizingByColumns(sizing, columns) {
|
||||
return out;
|
||||
}
|
||||
|
||||
function filterSortingByColumns(sorting, columns) {
|
||||
if (!Array.isArray(sorting)) {
|
||||
return [];
|
||||
}
|
||||
const ids = new Set((columns ?? []).map((c) => c?.id).filter(Boolean));
|
||||
return sorting.filter((s) => s && ids.has(s.id));
|
||||
}
|
||||
|
||||
function getColumnId(col) {
|
||||
return col?.id ?? col?.accessorKey ?? null;
|
||||
}
|
||||
@@ -134,6 +142,7 @@ export function useVrcxVueTable(options) {
|
||||
|
||||
persistKey,
|
||||
persistColumnSizing = true,
|
||||
persistSorting = true,
|
||||
persistDebounceMs = 200,
|
||||
|
||||
tableOptions = {}
|
||||
@@ -144,7 +153,6 @@ export function useVrcxVueTable(options) {
|
||||
if (!hasData) console.warn('useVrcxVueTable: `data` is required');
|
||||
if (!hasColumns) console.warn('useVrcxVueTable: `columns` is required');
|
||||
|
||||
const sorting = ref(initialSorting ?? []);
|
||||
const expanded = ref(initialExpanded ?? {});
|
||||
const pagination = ref(initialPagination ?? { pageIndex: 0, pageSize: 50 });
|
||||
const columnPinning = ref(initialColumnPinning ?? { left: [], right: [] });
|
||||
@@ -169,6 +177,13 @@ export function useVrcxVueTable(options) {
|
||||
}
|
||||
|
||||
const persisted = readPersisted();
|
||||
|
||||
let resolvedSorting = initialSorting ?? [];
|
||||
if (persisted && persistSorting && Array.isArray(persisted.sorting)) {
|
||||
resolvedSorting = persisted.sorting;
|
||||
}
|
||||
const sorting = ref(resolvedSorting);
|
||||
|
||||
if (persisted && persistColumnSizing && persisted.columnSizing) {
|
||||
columnSizing.value = persisted.columnSizing;
|
||||
}
|
||||
@@ -296,6 +311,19 @@ export function useVrcxVueTable(options) {
|
||||
);
|
||||
}
|
||||
|
||||
if (storageKey && persistSorting) {
|
||||
watch(
|
||||
sorting,
|
||||
(val) => {
|
||||
const cols = table.getAllLeafColumns?.() ?? [];
|
||||
persistWrite({
|
||||
sorting: filterSortingByColumns(val, cols)
|
||||
});
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
table,
|
||||
sorting,
|
||||
|
||||
Reference in New Issue
Block a user