Skip to content

Commit

Permalink
Custom sorter fn now parameterizes getValue() return values in ad…
Browse files Browse the repository at this point in the history
…dition to row objects
  • Loading branch information
pheuter committed Jul 16, 2024
1 parent 633884b commit 4ba9232
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/funny-crabs-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@careswitch/svelte-data-table': minor
---

Custom `sorter` fn now parameterizes `getValue()` return values in addition to row objects
96 changes: 81 additions & 15 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,6 @@ describe('DataTable', () => {
expect(table.getSortState('age')).toBeNull();
});

it('should handle sorting with custom sorter function', () => {
const customColumns = columns.map((col) =>
col.id === 'name'
? {
...col,
sorter: (a, b) => b.name.localeCompare(a.name) // Reverse alphabetical order
}
: col
) satisfies ColumnDef<(typeof sampleData)[0]>[];
const table = new DataTable({ data: sampleData, columns: customColumns });
table.toggleSort('name');
expect(table.rows[0].name).toBe('Eve');
expect(table.rows[4].name).toBe('Alice');
});

it('should maintain sort state when applying filters', () => {
const table = new DataTable({ data: sampleData, columns });
table.toggleSort('age');
Expand Down Expand Up @@ -167,6 +152,87 @@ describe('DataTable', () => {
});
});

describe('Enhanced Sorting', () => {
const sampleData = [
{ id: 1, name: 'Alice', age: 30, score: 85 },
{ id: 2, name: 'Bob', age: 25, score: 92 },
{ id: 3, name: 'Charlie', age: 35, score: 78 },
{ id: 4, name: 'David', age: 28, score: 88 },
{ id: 5, name: 'Eve', age: 32, score: 95 }
];

const columns: ColumnDef<(typeof sampleData)[0]>[] = [
{ id: 'id', key: 'id', name: 'ID', sortable: true },
{ id: 'name', key: 'name', name: 'Name', sortable: true },
{ id: 'age', key: 'age', name: 'Age', sortable: true },
{ id: 'score', key: 'score', name: 'Score', sortable: true },
{
id: 'complexSort',
key: 'score',
name: 'Complex Sort',
sortable: true,
getValue: (row) => row.score,
sorter: (a, b, rowA, rowB) => {
// Sort by score, but if scores are equal, sort by age
if (a === b) {
return rowA.age - rowB.age;
}
return b - a; // Descending order of scores
}
}
];

it('should sort using custom sorter with access to full row data', () => {
const table = new DataTable({ data: sampleData, columns });
table.toggleSort('complexSort');
expect(table.rows[0].name).toBe('Eve'); // Highest score
expect(table.rows[1].name).toBe('Bob'); // Second highest score
expect(table.rows[2].name).toBe('David'); // Third highest score
expect(table.rows[3].name).toBe('Alice'); // Equal score with Charlie, but younger
expect(table.rows[4].name).toBe('Charlie'); // Equal score with Alice, but older
});

it('should handle custom sorter with reverse direction', () => {
const table = new DataTable({ data: sampleData, columns });
table.toggleSort('complexSort');
table.toggleSort('complexSort'); // Toggle twice for descending order
expect(table.rows[0].name).toBe('Charlie');
expect(table.rows[1].name).toBe('Alice');
expect(table.rows[2].name).toBe('David');
expect(table.rows[3].name).toBe('Bob');
expect(table.rows[4].name).toBe('Eve');
});

it('should use custom sorter with initial sort', () => {
const table = new DataTable({
data: sampleData,
columns,
initialSort: 'complexSort',
initialSortDirection: 'desc'
});
expect(table.rows[0].name).toBe('Charlie');
expect(table.rows[4].name).toBe('Eve');
});

it('should maintain custom sort when applying filters', () => {
const table = new DataTable({ data: sampleData, columns });
table.toggleSort('complexSort');
table.setFilter('age', [30, 32, 35]); // Filter out Bob and David
expect(table.rows).toHaveLength(3);
expect(table.rows[0].name).toBe('Eve');
expect(table.rows[1].name).toBe('Alice');
expect(table.rows[2].name).toBe('Charlie');
});

it('should handle custom sorter with all equal primary values', () => {
const equalScoreData = sampleData.map((row) => ({ ...row, score: 90 }));
const table = new DataTable({ data: equalScoreData, columns });
table.toggleSort('complexSort');
expect(table.rows[0].name).toBe('Bob'); // Youngest
expect(table.rows[4].name).toBe('Charlie'); // Oldest
});
});

describe('Filtering', () => {
it('should apply single column filter', () => {
const table = new DataTable({ data: sampleData, columns });
Expand Down
14 changes: 9 additions & 5 deletions src/lib/DataTable.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type ValueGetter<T, V> = (row: T) => V;
type Sorter<T> = (a: T, b: T) => number;
type Sorter<T, V> = (a: V, b: V, rowA: T, rowB: T) => number;
type Filter<T, V> = (value: V, filterValue: V, row: T) => boolean;

export interface ColumnDef<T, V = any> {
Expand All @@ -8,7 +8,7 @@ export interface ColumnDef<T, V = any> {
name: string;
sortable?: boolean;
getValue?: ValueGetter<T, V>;
sorter?: Sorter<T>;
sorter?: Sorter<T, V>;
filter?: Filter<T, V>;
}

Expand Down Expand Up @@ -127,11 +127,15 @@ export class DataTable<T> {
if (columnId && direction) {
const colDef = this.#getColumnDef(columnId);
this.#sortedData = [...this.#filteredData].sort((a, b) => {
if (colDef && colDef.sorter) {
return direction === 'asc' ? colDef.sorter(a, b) : colDef.sorter(b, a);
}
const aVal = this.#getValue(a, columnId);
const bVal = this.#getValue(b, columnId);

if (colDef && colDef.sorter) {
return direction === 'asc'
? colDef.sorter(aVal, bVal, a, b)
: colDef.sorter(bVal, aVal, b, a);
}

if (aVal < bVal) return direction === 'asc' ? -1 : 1;
if (aVal > bVal) return direction === 'asc' ? 1 : -1;
return 0;
Expand Down

0 comments on commit 4ba9232

Please sign in to comment.