@svgrid/grid 1.1.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SvGrid.svelte +3 -1
- package/dist/SvGridDropdown.svelte +7 -3
- package/dist/cdn/svgrid.js +13504 -0
- package/dist/cdn/svgrid.svelte-external.js +11188 -0
- package/package.json +12 -2
- package/src/SvGrid.svelte +3 -1
- package/src/SvGridDropdown.svelte +7 -3
- package/dist/SvGrid.helpers.test.d.ts +0 -1
- package/dist/SvGrid.helpers.test.js +0 -298
- package/dist/SvGridChart.test.d.ts +0 -1
- package/dist/SvGridChart.test.js +0 -62
- package/dist/a11y.contract.test.d.ts +0 -1
- package/dist/a11y.contract.test.js +0 -39
- package/dist/a11y.test.d.ts +0 -1
- package/dist/a11y.test.js +0 -44
- package/dist/aligned-grids.test.d.ts +0 -1
- package/dist/aligned-grids.test.js +0 -75
- package/dist/build-api.coverage.test.d.ts +0 -20
- package/dist/build-api.coverage.test.js +0 -505
- package/dist/cell-formatting.test.d.ts +0 -1
- package/dist/cell-formatting.test.js +0 -234
- package/dist/cell-render.test.d.ts +0 -1
- package/dist/cell-render.test.js +0 -338
- package/dist/chart-export.test.d.ts +0 -1
- package/dist/chart-export.test.js +0 -302
- package/dist/chart.coverage.test.d.ts +0 -1
- package/dist/chart.coverage.test.js +0 -748
- package/dist/chart.test.d.ts +0 -1
- package/dist/chart.test.js +0 -289
- package/dist/clipboard.test.d.ts +0 -1
- package/dist/clipboard.test.js +0 -700
- package/dist/collaboration.coverage.test.d.ts +0 -1
- package/dist/collaboration.coverage.test.js +0 -200
- package/dist/collaboration.test.d.ts +0 -1
- package/dist/collaboration.test.js +0 -65
- package/dist/column-groups.test.d.ts +0 -1
- package/dist/column-groups.test.js +0 -56
- package/dist/column-types.test.d.ts +0 -1
- package/dist/column-types.test.js +0 -62
- package/dist/columns.test.d.ts +0 -1
- package/dist/columns.test.js +0 -625
- package/dist/conditional-formatting.test.d.ts +0 -1
- package/dist/conditional-formatting.test.js +0 -87
- package/dist/core.coverage.test.d.ts +0 -1
- package/dist/core.coverage.test.js +0 -186
- package/dist/core.performance.test.d.ts +0 -1
- package/dist/core.performance.test.js +0 -29
- package/dist/createGrid.test.d.ts +0 -1
- package/dist/createGrid.test.js +0 -9
- package/dist/editing.test.d.ts +0 -1
- package/dist/editing.test.js +0 -732
- package/dist/editors/cell-editors.coverage.test.d.ts +0 -1
- package/dist/editors/cell-editors.coverage.test.js +0 -139
- package/dist/editors/cell-editors.test.d.ts +0 -1
- package/dist/editors/cell-editors.test.js +0 -75
- package/dist/facet-buckets.test.d.ts +0 -1
- package/dist/facet-buckets.test.js +0 -296
- package/dist/fill-patterns.test.d.ts +0 -1
- package/dist/fill-patterns.test.js +0 -81
- package/dist/filter-operators.test.d.ts +0 -1
- package/dist/filter-operators.test.js +0 -135
- package/dist/filtering/excel-filters.test.d.ts +0 -1
- package/dist/filtering/excel-filters.test.js +0 -116
- package/dist/filtering/locale-filter.test.d.ts +0 -13
- package/dist/filtering/locale-filter.test.js +0 -189
- package/dist/flex-render.test.d.ts +0 -1
- package/dist/flex-render.test.js +0 -142
- package/dist/group-aggregate.test.d.ts +0 -1
- package/dist/group-aggregate.test.js +0 -32
- package/dist/hyperformula-adapter.test.d.ts +0 -1
- package/dist/hyperformula-adapter.test.js +0 -205
- package/dist/keyboard-handlers.coverage.test.d.ts +0 -1
- package/dist/keyboard-handlers.coverage.test.js +0 -495
- package/dist/keyboard.test.d.ts +0 -1
- package/dist/keyboard.test.js +0 -27
- package/dist/menus.test.d.ts +0 -1
- package/dist/menus.test.js +0 -560
- package/dist/named-views.coverage.test.d.ts +0 -1
- package/dist/named-views.coverage.test.js +0 -180
- package/dist/named-views.test.d.ts +0 -1
- package/dist/named-views.test.js +0 -57
- package/dist/new-features.test.d.ts +0 -1
- package/dist/new-features.test.js +0 -183
- package/dist/render-component.test.d.ts +0 -1
- package/dist/render-component.test.js +0 -41
- package/dist/row-drag.test.d.ts +0 -1
- package/dist/row-drag.test.js +0 -142
- package/dist/row-resize.test.d.ts +0 -1
- package/dist/row-resize.test.js +0 -329
- package/dist/scroll-sync.test.d.ts +0 -1
- package/dist/scroll-sync.test.js +0 -290
- package/dist/selection.multi-range.test.d.ts +0 -1
- package/dist/selection.multi-range.test.js +0 -55
- package/dist/selection.test.d.ts +0 -1
- package/dist/selection.test.js +0 -647
- package/dist/server-data-source.coverage.test.d.ts +0 -1
- package/dist/server-data-source.coverage.test.js +0 -154
- package/dist/server-data-source.test.d.ts +0 -1
- package/dist/server-data-source.test.js +0 -81
- package/dist/sparkline.test.d.ts +0 -1
- package/dist/sparkline.test.js +0 -52
- package/dist/spreadsheet.test.d.ts +0 -1
- package/dist/spreadsheet.test.js +0 -446
- package/dist/subscribe.test.d.ts +0 -1
- package/dist/subscribe.test.js +0 -93
- package/dist/svgrid.api-extensions.test.d.ts +0 -14
- package/dist/svgrid.api-extensions.test.js +0 -334
- package/dist/svgrid.api.test.d.ts +0 -15
- package/dist/svgrid.api.test.js +0 -169
- package/dist/svgrid.behavior.test.d.ts +0 -15
- package/dist/svgrid.behavior.test.js +0 -625
- package/dist/svgrid.column-reorder.test.d.ts +0 -15
- package/dist/svgrid.column-reorder.test.js +0 -224
- package/dist/svgrid.comments-autocomplete.test.d.ts +0 -1
- package/dist/svgrid.comments-autocomplete.test.js +0 -96
- package/dist/svgrid.context-menu.test.d.ts +0 -1
- package/dist/svgrid.context-menu.test.js +0 -102
- package/dist/svgrid.features.test.d.ts +0 -1
- package/dist/svgrid.features.test.js +0 -129
- package/dist/svgrid.interaction.test.d.ts +0 -9
- package/dist/svgrid.interaction.test.js +0 -349
- package/dist/svgrid.locale-filtering.test.d.ts +0 -15
- package/dist/svgrid.locale-filtering.test.js +0 -261
- package/dist/svgrid.new-features.wrapper.test.d.ts +0 -1
- package/dist/svgrid.new-features.wrapper.test.js +0 -194
- package/dist/svgrid.row-pinning.test.d.ts +0 -14
- package/dist/svgrid.row-pinning.test.js +0 -250
- package/dist/svgrid.set-filter.test.d.ts +0 -17
- package/dist/svgrid.set-filter.test.js +0 -268
- package/dist/svgrid.wrapper.test.d.ts +0 -1
- package/dist/svgrid.wrapper.test.js +0 -59
- package/dist/test-setup.d.ts +0 -1
- package/dist/test-setup.js +0 -35
- package/dist/transaction.test.d.ts +0 -1
- package/dist/transaction.test.js +0 -89
- package/dist/virtualization/column-virtualizer.test.d.ts +0 -1
- package/dist/virtualization/column-virtualizer.test.js +0 -25
- package/dist/virtualization/scroll-scaling.test.d.ts +0 -1
- package/dist/virtualization/scroll-scaling.test.js +0 -127
- package/dist/virtualization/virtualizer.test.d.ts +0 -1
- package/dist/virtualization/virtualizer.test.js +0 -41
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interaction-driven tests against <SvGrid />. Targeting code paths that
|
|
3
|
-
* only run in response to events: keyboard, pointer, scroll, edit lifecycle.
|
|
4
|
-
*
|
|
5
|
-
* Each test mounts a small grid, fires a synthetic event, awaits a tick,
|
|
6
|
-
* then asserts on the api object's snapshot. The goal is breadth: hit as
|
|
7
|
-
* many branches inside SvGrid.svelte as possible.
|
|
8
|
-
*/
|
|
9
|
-
import { describe, expect, it } from 'vitest';
|
|
10
|
-
import { mount, unmount } from 'svelte';
|
|
11
|
-
import SvGrid from './SvGrid.svelte';
|
|
12
|
-
import { columnFilteringFeature, createCoreRowModel, createFilteredRowModel, createPaginatedRowModel, createSortedRowModel, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, } from './index';
|
|
13
|
-
const features = tableFeatures({
|
|
14
|
-
columnFilteringFeature,
|
|
15
|
-
rowSortingFeature,
|
|
16
|
-
rowSelectionFeature,
|
|
17
|
-
rowPaginationFeature,
|
|
18
|
-
});
|
|
19
|
-
const rows = Array.from({ length: 25 }, (_, i) => ({
|
|
20
|
-
id: i + 1,
|
|
21
|
-
name: `Person ${(i + 1).toString().padStart(2, '0')}`,
|
|
22
|
-
team: ['Research', 'Compilers', 'Apollo', 'Kernel'][i % 4],
|
|
23
|
-
salary: 100000 + i * 1000,
|
|
24
|
-
}));
|
|
25
|
-
const cols = [
|
|
26
|
-
{ field: 'name', header: 'Name', width: 220, editorType: 'text' },
|
|
27
|
-
{ field: 'team', header: 'Team', width: 160, editorType: 'text' },
|
|
28
|
-
{ field: 'salary', header: 'Salary', width: 140, editorType: 'number' },
|
|
29
|
-
];
|
|
30
|
-
function mountInteractive(overrides = {}) {
|
|
31
|
-
return new Promise((res, rej) => {
|
|
32
|
-
const target = document.createElement('div');
|
|
33
|
-
document.body.appendChild(target);
|
|
34
|
-
let api = null;
|
|
35
|
-
const app = mount(SvGrid, {
|
|
36
|
-
target,
|
|
37
|
-
props: {
|
|
38
|
-
data: rows,
|
|
39
|
-
columns: cols,
|
|
40
|
-
features,
|
|
41
|
-
_rowModels: {
|
|
42
|
-
coreRowModel: createCoreRowModel(),
|
|
43
|
-
filteredRowModel: createFilteredRowModel(),
|
|
44
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
45
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
46
|
-
},
|
|
47
|
-
rowHeight: 36,
|
|
48
|
-
containerHeight: 480,
|
|
49
|
-
virtualization: false,
|
|
50
|
-
enableInlineEditing: true,
|
|
51
|
-
enableCellSelection: true,
|
|
52
|
-
showPagination: true,
|
|
53
|
-
pageSize: 10,
|
|
54
|
-
showColumnFilters: true,
|
|
55
|
-
showGlobalFilter: true,
|
|
56
|
-
showRowSelection: true,
|
|
57
|
-
onApiReady(received) {
|
|
58
|
-
api = received;
|
|
59
|
-
const grid = target.querySelector('[role="grid"]') ?? target;
|
|
60
|
-
res({
|
|
61
|
-
api,
|
|
62
|
-
target,
|
|
63
|
-
grid,
|
|
64
|
-
destroy: () => {
|
|
65
|
-
unmount(app);
|
|
66
|
-
target.remove();
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
...overrides,
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
queueMicrotask(() => {
|
|
74
|
-
if (!api)
|
|
75
|
-
rej(new Error('onApiReady never fired'));
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
80
|
-
describe('SvGrid interactions - keyboard', () => {
|
|
81
|
-
it('dispatches ArrowRight without throwing', async () => {
|
|
82
|
-
const { grid, destroy } = await mountInteractive();
|
|
83
|
-
try {
|
|
84
|
-
const ev = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true });
|
|
85
|
-
grid.dispatchEvent(ev);
|
|
86
|
-
await tick();
|
|
87
|
-
expect(true).toBe(true);
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
destroy();
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
it('dispatches ArrowDown / ArrowUp / Home / End', async () => {
|
|
94
|
-
const { grid, destroy } = await mountInteractive();
|
|
95
|
-
try {
|
|
96
|
-
for (const key of ['ArrowDown', 'ArrowUp', 'Home', 'End', 'PageDown', 'PageUp']) {
|
|
97
|
-
grid.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));
|
|
98
|
-
await tick();
|
|
99
|
-
}
|
|
100
|
-
expect(true).toBe(true);
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
destroy();
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
it('dispatches Ctrl+Home and Ctrl+End', async () => {
|
|
107
|
-
const { grid, destroy } = await mountInteractive();
|
|
108
|
-
try {
|
|
109
|
-
grid.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', ctrlKey: true, bubbles: true }));
|
|
110
|
-
await tick();
|
|
111
|
-
grid.dispatchEvent(new KeyboardEvent('keydown', { key: 'End', ctrlKey: true, bubbles: true }));
|
|
112
|
-
await tick();
|
|
113
|
-
expect(true).toBe(true);
|
|
114
|
-
}
|
|
115
|
-
finally {
|
|
116
|
-
destroy();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
it('dispatches Escape to close any open menu / cancel edit', async () => {
|
|
120
|
-
const { grid, destroy } = await mountInteractive();
|
|
121
|
-
try {
|
|
122
|
-
grid.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
123
|
-
await tick();
|
|
124
|
-
expect(true).toBe(true);
|
|
125
|
-
}
|
|
126
|
-
finally {
|
|
127
|
-
destroy();
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
it('dispatches Tab and Shift+Tab', async () => {
|
|
131
|
-
const { grid, destroy } = await mountInteractive();
|
|
132
|
-
try {
|
|
133
|
-
grid.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }));
|
|
134
|
-
await tick();
|
|
135
|
-
grid.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true, bubbles: true }));
|
|
136
|
-
await tick();
|
|
137
|
-
expect(true).toBe(true);
|
|
138
|
-
}
|
|
139
|
-
finally {
|
|
140
|
-
destroy();
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
describe('SvGrid interactions - global filter input', () => {
|
|
145
|
-
it('typing in the global filter narrows the displayed rows', async () => {
|
|
146
|
-
const { api, target, destroy } = await mountInteractive();
|
|
147
|
-
try {
|
|
148
|
-
const input = target.querySelector('.sv-grid-global-filter input');
|
|
149
|
-
if (!input) {
|
|
150
|
-
// global filter input wasn't rendered (jsdom quirk) - just verify api still works.
|
|
151
|
-
expect(api.getData().length).toBe(rows.length);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
input.value = 'Person 01';
|
|
155
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
156
|
-
await tick();
|
|
157
|
-
await tick();
|
|
158
|
-
// Some narrowing happened.
|
|
159
|
-
expect(api.getDisplayedRows().length).toBeLessThanOrEqual(rows.length);
|
|
160
|
-
}
|
|
161
|
-
finally {
|
|
162
|
-
destroy();
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
describe('SvGrid interactions - column resize', () => {
|
|
167
|
-
it('starts a column resize via pointerdown on the handle', async () => {
|
|
168
|
-
const { target, destroy } = await mountInteractive();
|
|
169
|
-
try {
|
|
170
|
-
const handle = target.querySelector('.sv-grid-resize-handle');
|
|
171
|
-
if (!handle) {
|
|
172
|
-
// jsdom may not lay out the handle; assertion skipped.
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
handle.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, pointerId: 1, button: 0 }));
|
|
176
|
-
await tick();
|
|
177
|
-
window.dispatchEvent(new PointerEvent('pointermove', { clientX: 200, clientY: 0 }));
|
|
178
|
-
await tick();
|
|
179
|
-
window.dispatchEvent(new PointerEvent('pointerup', { clientX: 200, clientY: 0 }));
|
|
180
|
-
await tick();
|
|
181
|
-
expect(true).toBe(true);
|
|
182
|
-
}
|
|
183
|
-
finally {
|
|
184
|
-
destroy();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
describe('SvGrid interactions - pagination nav', () => {
|
|
189
|
-
it('clicking next/prev/last/first paginator buttons does not throw', async () => {
|
|
190
|
-
const { target, destroy } = await mountInteractive();
|
|
191
|
-
try {
|
|
192
|
-
const buttons = target.querySelectorAll('.sv-grid-pagination-btn');
|
|
193
|
-
for (const btn of Array.from(buttons)) {
|
|
194
|
-
;
|
|
195
|
-
btn.click();
|
|
196
|
-
await tick();
|
|
197
|
-
}
|
|
198
|
-
expect(true).toBe(true);
|
|
199
|
-
}
|
|
200
|
-
finally {
|
|
201
|
-
destroy();
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('SvGrid interactions - sort header click', () => {
|
|
206
|
-
it('clicking a column header toggles the sort indicator', async () => {
|
|
207
|
-
const { api, target, destroy } = await mountInteractive();
|
|
208
|
-
try {
|
|
209
|
-
const headerEl = target.querySelector('[data-svgrid-header-col]');
|
|
210
|
-
if (!headerEl)
|
|
211
|
-
return;
|
|
212
|
-
headerEl.click();
|
|
213
|
-
await tick();
|
|
214
|
-
headerEl.click();
|
|
215
|
-
await tick();
|
|
216
|
-
headerEl.click();
|
|
217
|
-
await tick();
|
|
218
|
-
// No throw means we exercised onHeaderSortClick.
|
|
219
|
-
expect(api.getData().length).toBe(rows.length);
|
|
220
|
-
}
|
|
221
|
-
finally {
|
|
222
|
-
destroy();
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
it('shift-click on a header adds a secondary sort clause', async () => {
|
|
226
|
-
const { target, destroy } = await mountInteractive();
|
|
227
|
-
try {
|
|
228
|
-
const headers = target.querySelectorAll('[data-svgrid-header-col]');
|
|
229
|
-
if (headers.length < 2)
|
|
230
|
-
return;
|
|
231
|
-
headers[0].click();
|
|
232
|
-
await tick();
|
|
233
|
-
headers[1].dispatchEvent(new MouseEvent('click', { bubbles: true, shiftKey: true }));
|
|
234
|
-
await tick();
|
|
235
|
-
expect(true).toBe(true);
|
|
236
|
-
}
|
|
237
|
-
finally {
|
|
238
|
-
destroy();
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
describe('SvGrid interactions - cell pointer events', () => {
|
|
243
|
-
it('pointerdown on a cell starts a selection', async () => {
|
|
244
|
-
const { target, destroy } = await mountInteractive();
|
|
245
|
-
try {
|
|
246
|
-
const cell = target.querySelector('.sv-grid-cell');
|
|
247
|
-
if (!cell)
|
|
248
|
-
return;
|
|
249
|
-
cell.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, pointerId: 1, button: 0 }));
|
|
250
|
-
await tick();
|
|
251
|
-
cell.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, pointerId: 1 }));
|
|
252
|
-
await tick();
|
|
253
|
-
expect(true).toBe(true);
|
|
254
|
-
}
|
|
255
|
-
finally {
|
|
256
|
-
destroy();
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
it('double-click on an editable cell enters edit mode', async () => {
|
|
260
|
-
const { target, destroy } = await mountInteractive();
|
|
261
|
-
try {
|
|
262
|
-
const cell = target.querySelector('.sv-grid-cell');
|
|
263
|
-
if (!cell)
|
|
264
|
-
return;
|
|
265
|
-
cell.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
|
|
266
|
-
await tick();
|
|
267
|
-
// Editor input may or may not be in DOM depending on layout; just
|
|
268
|
-
// exercising the codepath is enough.
|
|
269
|
-
expect(true).toBe(true);
|
|
270
|
-
}
|
|
271
|
-
finally {
|
|
272
|
-
destroy();
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
// issue #23: a mouse drag extends the cell range, but a touch drag must NOT -
|
|
276
|
-
// it should leave the single tapped cell selected so the finger-drag scrolls.
|
|
277
|
-
async function dragSelectCount(pointerType) {
|
|
278
|
-
const { target, destroy } = await mountInteractive();
|
|
279
|
-
try {
|
|
280
|
-
const a = target.querySelector('[data-svgrid-row="0"][data-svgrid-col="0"]');
|
|
281
|
-
const c = target.querySelector('[data-svgrid-row="0"][data-svgrid-col="2"]');
|
|
282
|
-
if (!a || !c)
|
|
283
|
-
return -1; // jsdom didn't lay the cells out; caller skips
|
|
284
|
-
a.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, pointerId: 1, button: 0, pointerType }));
|
|
285
|
-
await tick();
|
|
286
|
-
c.dispatchEvent(new PointerEvent('pointerenter', { bubbles: true, pointerId: 1, pointerType }));
|
|
287
|
-
await tick();
|
|
288
|
-
return target.querySelectorAll('[data-selected-range="true"]').length;
|
|
289
|
-
}
|
|
290
|
-
finally {
|
|
291
|
-
destroy();
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
it('mouse drag extends the selection across cells', async () => {
|
|
295
|
-
const n = await dragSelectCount('mouse');
|
|
296
|
-
if (n < 0)
|
|
297
|
-
return;
|
|
298
|
-
expect(n).toBeGreaterThan(1); // (0,0) -> (0,2) selects the row's 3 cells
|
|
299
|
-
});
|
|
300
|
-
it('touch drag does not start a range selection (issue #23)', async () => {
|
|
301
|
-
const n = await dragSelectCount('touch');
|
|
302
|
-
if (n < 0)
|
|
303
|
-
return;
|
|
304
|
-
expect(n).toBeLessThanOrEqual(1); // only the tapped cell; finger-drag scrolls
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
describe('SvGrid interactions - copy', () => {
|
|
308
|
-
it('Ctrl+C with a cell range does not throw', async () => {
|
|
309
|
-
const { grid, destroy } = await mountInteractive();
|
|
310
|
-
try {
|
|
311
|
-
const cev = new KeyboardEvent('keydown', { key: 'c', ctrlKey: true, bubbles: true });
|
|
312
|
-
grid.dispatchEvent(cev);
|
|
313
|
-
await tick();
|
|
314
|
-
expect(true).toBe(true);
|
|
315
|
-
}
|
|
316
|
-
finally {
|
|
317
|
-
destroy();
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
describe('SvGrid interactions - selection mode', () => {
|
|
322
|
-
it('selecting a row via the API surfaces via getDisplayedRows', async () => {
|
|
323
|
-
const { api, destroy } = await mountInteractive();
|
|
324
|
-
try {
|
|
325
|
-
// Simulate clicking the first row checkbox via the API.
|
|
326
|
-
expect(api.getDisplayedRows().length).toBeGreaterThan(0);
|
|
327
|
-
}
|
|
328
|
-
finally {
|
|
329
|
-
destroy();
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
describe('SvGrid interactions - clearAllFilters round-trip', () => {
|
|
334
|
-
it('setting + clearing global + column filters returns to full set', async () => {
|
|
335
|
-
const { api, destroy } = await mountInteractive();
|
|
336
|
-
try {
|
|
337
|
-
api.setFilter('name', { operator: 'contains', value: 'XYZ-NONE' });
|
|
338
|
-
await tick();
|
|
339
|
-
expect(api.getDisplayedRows().length).toBeLessThanOrEqual(rows.length);
|
|
340
|
-
api.clearAllFilters();
|
|
341
|
-
await tick();
|
|
342
|
-
await tick();
|
|
343
|
-
expect(api.getDisplayedRows().length).toBeGreaterThanOrEqual(1);
|
|
344
|
-
}
|
|
345
|
-
finally {
|
|
346
|
-
destroy();
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the `filterLocale` prop. Mounts a real
|
|
3
|
-
* <SvGrid> with accented data, drives the column-menu filter via
|
|
4
|
-
* `api.setFilter()`, then asserts the visible row count via
|
|
5
|
-
* `api.getDisplayedRows()`.
|
|
6
|
-
*
|
|
7
|
-
* Covers:
|
|
8
|
-
* - Accent-insensitive `contains` operator on the menu filter
|
|
9
|
-
* - Diacritic-stripped global filter (via `setGlobalFilter` not in
|
|
10
|
-
* the public api yet; here we use `api.setFilter('city', ...)`
|
|
11
|
-
* which exercises the same pipeline)
|
|
12
|
-
* - filterLocale prop pass-through (es-ES, de-DE)
|
|
13
|
-
* - Regression: ASCII queries still narrow rows
|
|
14
|
-
*/
|
|
15
|
-
export {};
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for the `filterLocale` prop. Mounts a real
|
|
3
|
-
* <SvGrid> with accented data, drives the column-menu filter via
|
|
4
|
-
* `api.setFilter()`, then asserts the visible row count via
|
|
5
|
-
* `api.getDisplayedRows()`.
|
|
6
|
-
*
|
|
7
|
-
* Covers:
|
|
8
|
-
* - Accent-insensitive `contains` operator on the menu filter
|
|
9
|
-
* - Diacritic-stripped global filter (via `setGlobalFilter` not in
|
|
10
|
-
* the public api yet; here we use `api.setFilter('city', ...)`
|
|
11
|
-
* which exercises the same pipeline)
|
|
12
|
-
* - filterLocale prop pass-through (es-ES, de-DE)
|
|
13
|
-
* - Regression: ASCII queries still narrow rows
|
|
14
|
-
*/
|
|
15
|
-
import { describe, expect, it } from 'vitest';
|
|
16
|
-
import { mount, unmount } from 'svelte';
|
|
17
|
-
import SvGrid from './SvGrid.svelte';
|
|
18
|
-
import { columnFilteringFeature, createCoreRowModel, createFilteredRowModel, createSortedRowModel, rowSortingFeature, sortFns, tableFeatures, } from './index';
|
|
19
|
-
const features = tableFeatures({ columnFilteringFeature, rowSortingFeature });
|
|
20
|
-
const rows = [
|
|
21
|
-
{ id: 1, city: 'München', country: 'Deutschland', region: 'Europe', population: 1510000 },
|
|
22
|
-
{ id: 2, city: 'Köln', country: 'Deutschland', region: 'Europe', population: 1080000 },
|
|
23
|
-
{ id: 3, city: 'Genève', country: 'Suisse', region: 'Europe', population: 203000 },
|
|
24
|
-
{ id: 4, city: 'Montréal', country: 'Canada', region: 'Americas', population: 1780000 },
|
|
25
|
-
{ id: 5, city: 'México', country: 'México', region: 'Americas', population: 9210000 },
|
|
26
|
-
{ id: 6, city: 'São Paulo', country: 'Brasil', region: 'Americas', population: 12330000 },
|
|
27
|
-
{ id: 7, city: 'Tōkyō', country: '日本', region: 'Asia', population: 13960000 },
|
|
28
|
-
{ id: 8, city: 'İstanbul', country: 'Türkiye', region: 'Asia', population: 15840000 },
|
|
29
|
-
{ id: 9, city: 'Málaga', country: 'España', region: 'Europe', population: 580000 },
|
|
30
|
-
{ id: 10, city: 'Berlin', country: 'Deutschland', region: 'Europe', population: 3770000 },
|
|
31
|
-
];
|
|
32
|
-
const cols = [
|
|
33
|
-
{ field: 'city', header: 'City', width: 180, editable: false },
|
|
34
|
-
{ field: 'country', header: 'Country', width: 160, editable: false },
|
|
35
|
-
{ field: 'region', header: 'Region', width: 110, editable: false },
|
|
36
|
-
{ field: 'population', header: 'Population', width: 130, editable: false, align: 'right' },
|
|
37
|
-
];
|
|
38
|
-
function mountGrid(opts = {}) {
|
|
39
|
-
return new Promise((res, rej) => {
|
|
40
|
-
const target = document.createElement('div');
|
|
41
|
-
document.body.appendChild(target);
|
|
42
|
-
let api = null;
|
|
43
|
-
const app = mount(SvGrid, {
|
|
44
|
-
target,
|
|
45
|
-
props: {
|
|
46
|
-
data: rows,
|
|
47
|
-
columns: cols,
|
|
48
|
-
features,
|
|
49
|
-
_rowModels: {
|
|
50
|
-
coreRowModel: createCoreRowModel(),
|
|
51
|
-
filteredRowModel: createFilteredRowModel(),
|
|
52
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
53
|
-
},
|
|
54
|
-
containerHeight: 360,
|
|
55
|
-
virtualization: false,
|
|
56
|
-
columnVirtualization: false,
|
|
57
|
-
showPagination: false,
|
|
58
|
-
showColumnFilters: false,
|
|
59
|
-
showGlobalFilter: false,
|
|
60
|
-
showRowSelection: false,
|
|
61
|
-
filterLocale: opts.filterLocale,
|
|
62
|
-
onApiReady(received) {
|
|
63
|
-
api = received;
|
|
64
|
-
res({
|
|
65
|
-
api,
|
|
66
|
-
destroy: () => { unmount(app); target.remove(); },
|
|
67
|
-
});
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
queueMicrotask(() => { if (!api)
|
|
72
|
-
rej(new Error('onApiReady never fired')); });
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
76
|
-
describe('SvGrid filter - default locale (no prop)', () => {
|
|
77
|
-
it('"munch" matches "München" via contains operator', async () => {
|
|
78
|
-
const { api, destroy } = await mountGrid();
|
|
79
|
-
try {
|
|
80
|
-
api.setFilter('city', { operator: 'contains', value: 'munch' });
|
|
81
|
-
await tick();
|
|
82
|
-
const visible = api.getDisplayedRows();
|
|
83
|
-
expect(visible.map((r) => r.city)).toEqual(['München']);
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
destroy();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
it('"geneve" matches "Genève"', async () => {
|
|
90
|
-
const { api, destroy } = await mountGrid();
|
|
91
|
-
try {
|
|
92
|
-
api.setFilter('city', { operator: 'contains', value: 'geneve' });
|
|
93
|
-
await tick();
|
|
94
|
-
const visible = api.getDisplayedRows();
|
|
95
|
-
expect(visible.map((r) => r.city)).toEqual(['Genève']);
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
destroy();
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
it('"sao paulo" matches "São Paulo"', async () => {
|
|
102
|
-
const { api, destroy } = await mountGrid();
|
|
103
|
-
try {
|
|
104
|
-
api.setFilter('city', { operator: 'contains', value: 'sao paulo' });
|
|
105
|
-
await tick();
|
|
106
|
-
const visible = api.getDisplayedRows();
|
|
107
|
-
expect(visible.map((r) => r.city)).toEqual(['São Paulo']);
|
|
108
|
-
}
|
|
109
|
-
finally {
|
|
110
|
-
destroy();
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
it('"tokyo" matches "Tōkyō"', async () => {
|
|
114
|
-
const { api, destroy } = await mountGrid();
|
|
115
|
-
try {
|
|
116
|
-
api.setFilter('city', { operator: 'contains', value: 'tokyo' });
|
|
117
|
-
await tick();
|
|
118
|
-
const visible = api.getDisplayedRows();
|
|
119
|
-
expect(visible.map((r) => r.city)).toEqual(['Tōkyō']);
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
destroy();
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
it('"istanbul" matches "İstanbul" (dotted-I default folding)', async () => {
|
|
126
|
-
const { api, destroy } = await mountGrid();
|
|
127
|
-
try {
|
|
128
|
-
api.setFilter('city', { operator: 'contains', value: 'istanbul' });
|
|
129
|
-
await tick();
|
|
130
|
-
const visible = api.getDisplayedRows();
|
|
131
|
-
expect(visible.map((r) => r.city)).toEqual(['İstanbul']);
|
|
132
|
-
}
|
|
133
|
-
finally {
|
|
134
|
-
destroy();
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
it('"deutschland" matches all German cities', async () => {
|
|
138
|
-
const { api, destroy } = await mountGrid();
|
|
139
|
-
try {
|
|
140
|
-
api.setFilter('country', { operator: 'contains', value: 'deutschland' });
|
|
141
|
-
await tick();
|
|
142
|
-
const visible = api.getDisplayedRows();
|
|
143
|
-
expect(visible.map((r) => r.city).sort()).toEqual(['Berlin', 'Köln', 'München']);
|
|
144
|
-
}
|
|
145
|
-
finally {
|
|
146
|
-
destroy();
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
describe('SvGrid filter - filterLocale="de-DE"', () => {
|
|
151
|
-
it('explicit German locale - "munchen" still matches "München"', async () => {
|
|
152
|
-
const { api, destroy } = await mountGrid({ filterLocale: 'de-DE' });
|
|
153
|
-
try {
|
|
154
|
-
api.setFilter('city', { operator: 'contains', value: 'munchen' });
|
|
155
|
-
await tick();
|
|
156
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['München']);
|
|
157
|
-
}
|
|
158
|
-
finally {
|
|
159
|
-
destroy();
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
it('"koln" matches "Köln"', async () => {
|
|
163
|
-
const { api, destroy } = await mountGrid({ filterLocale: 'de-DE' });
|
|
164
|
-
try {
|
|
165
|
-
api.setFilter('city', { operator: 'contains', value: 'koln' });
|
|
166
|
-
await tick();
|
|
167
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['Köln']);
|
|
168
|
-
}
|
|
169
|
-
finally {
|
|
170
|
-
destroy();
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
describe('SvGrid filter - filterLocale="es-ES"', () => {
|
|
175
|
-
it('"mexico" matches "México"', async () => {
|
|
176
|
-
const { api, destroy } = await mountGrid({ filterLocale: 'es-ES' });
|
|
177
|
-
try {
|
|
178
|
-
api.setFilter('city', { operator: 'contains', value: 'mexico' });
|
|
179
|
-
await tick();
|
|
180
|
-
const cities = api.getDisplayedRows().map((r) => r.city);
|
|
181
|
-
expect(cities).toContain('México');
|
|
182
|
-
}
|
|
183
|
-
finally {
|
|
184
|
-
destroy();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
it('"malaga" matches "Málaga"', async () => {
|
|
188
|
-
const { api, destroy } = await mountGrid({ filterLocale: 'es-ES' });
|
|
189
|
-
try {
|
|
190
|
-
api.setFilter('city', { operator: 'contains', value: 'malaga' });
|
|
191
|
-
await tick();
|
|
192
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['Málaga']);
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
destroy();
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
describe('SvGrid filter - startsWith + equals operators', () => {
|
|
200
|
-
it('startsWith "mun" matches München', async () => {
|
|
201
|
-
const { api, destroy } = await mountGrid();
|
|
202
|
-
try {
|
|
203
|
-
api.setFilter('city', { operator: 'startsWith', value: 'mun' });
|
|
204
|
-
await tick();
|
|
205
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['München']);
|
|
206
|
-
}
|
|
207
|
-
finally {
|
|
208
|
-
destroy();
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
it('equals "berlin" matches exactly "Berlin" (case-insensitive)', async () => {
|
|
212
|
-
const { api, destroy } = await mountGrid();
|
|
213
|
-
try {
|
|
214
|
-
api.setFilter('city', { operator: 'equals', value: 'berlin' });
|
|
215
|
-
await tick();
|
|
216
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['Berlin']);
|
|
217
|
-
}
|
|
218
|
-
finally {
|
|
219
|
-
destroy();
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
it('equals "kolN" matches "Köln" (case + accent insensitive)', async () => {
|
|
223
|
-
const { api, destroy } = await mountGrid();
|
|
224
|
-
try {
|
|
225
|
-
api.setFilter('city', { operator: 'equals', value: 'kolN' });
|
|
226
|
-
await tick();
|
|
227
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['Köln']);
|
|
228
|
-
}
|
|
229
|
-
finally {
|
|
230
|
-
destroy();
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
describe('SvGrid filter - clear + compose', () => {
|
|
235
|
-
it('clearFilter removes the narrowing', async () => {
|
|
236
|
-
const { api, destroy } = await mountGrid();
|
|
237
|
-
try {
|
|
238
|
-
api.setFilter('city', { operator: 'contains', value: 'munch' });
|
|
239
|
-
await tick();
|
|
240
|
-
expect(api.getDisplayedRows().length).toBe(1);
|
|
241
|
-
api.clearFilter('city');
|
|
242
|
-
await tick();
|
|
243
|
-
expect(api.getDisplayedRows().length).toBe(rows.length);
|
|
244
|
-
}
|
|
245
|
-
finally {
|
|
246
|
-
destroy();
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
it('two column filters compose (AND across columns)', async () => {
|
|
250
|
-
const { api, destroy } = await mountGrid();
|
|
251
|
-
try {
|
|
252
|
-
api.setFilter('country', { operator: 'contains', value: 'deutschland' });
|
|
253
|
-
api.setFilter('city', { operator: 'contains', value: 'mun' });
|
|
254
|
-
await tick();
|
|
255
|
-
expect(api.getDisplayedRows().map((r) => r.city)).toEqual(['München']);
|
|
256
|
-
}
|
|
257
|
-
finally {
|
|
258
|
-
destroy();
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|