@svgrid/grid 1.1.2 → 1.2.3
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.css +294 -16
- package/dist/SvGrid.svelte +4 -1
- package/dist/SvGridDropdown.svelte +7 -3
- package/dist/cdn/svgrid.js +13506 -0
- package/dist/cdn/svgrid.svelte-external.js +11190 -0
- package/package.json +12 -2
- package/src/SvGrid.css +294 -16
- package/src/SvGrid.svelte +4 -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,224 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component tests for engine-level column reorder. Mount a real
|
|
3
|
-
* <SvGrid> with `enableColumnReorder={true}`, then exercise both
|
|
4
|
-
* surfaces:
|
|
5
|
-
*
|
|
6
|
-
* - Imperative: api.setColumnOrder() / getColumnOrder() reorders the
|
|
7
|
-
* DOM and fires onColumnOrderChange.
|
|
8
|
-
* - User drag: dispatch dragstart / dragover / drop events on header
|
|
9
|
-
* elements, assert the resulting DOM order matches the drop side.
|
|
10
|
-
* - Drop indicator: the hovered header gets `is-drag-target-before` /
|
|
11
|
-
* `is-drag-target-after` while a drag is in flight.
|
|
12
|
-
* - Headers carry `draggable=true` only when the prop is on.
|
|
13
|
-
* - Pin groups: reorder within a pin group is honored.
|
|
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: 'P-1', first: 'Ada', last: 'Lovelace', team: 'Research', salary: 145000 },
|
|
22
|
-
{ id: 'P-2', first: 'Linus', last: 'Torvalds', team: 'Kernel', salary: 188000 },
|
|
23
|
-
{ id: 'P-3', first: 'Grace', last: 'Hopper', team: 'Compilers', salary: 152000 },
|
|
24
|
-
];
|
|
25
|
-
const cols = [
|
|
26
|
-
{ field: 'id', header: 'ID', width: 100 },
|
|
27
|
-
{ field: 'first', header: 'First', width: 140 },
|
|
28
|
-
{ field: 'last', header: 'Last', width: 140 },
|
|
29
|
-
{ field: 'team', header: 'Team', width: 160 },
|
|
30
|
-
{ field: 'salary', header: 'Salary', width: 130 },
|
|
31
|
-
];
|
|
32
|
-
function mountGrid(opts = {}) {
|
|
33
|
-
return new Promise((res, rej) => {
|
|
34
|
-
const target = document.createElement('div');
|
|
35
|
-
document.body.appendChild(target);
|
|
36
|
-
let api = null;
|
|
37
|
-
const app = mount(SvGrid, {
|
|
38
|
-
target,
|
|
39
|
-
props: {
|
|
40
|
-
data: rows,
|
|
41
|
-
columns: cols,
|
|
42
|
-
features,
|
|
43
|
-
_rowModels: {
|
|
44
|
-
coreRowModel: createCoreRowModel(),
|
|
45
|
-
filteredRowModel: createFilteredRowModel(),
|
|
46
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
47
|
-
},
|
|
48
|
-
containerHeight: 360,
|
|
49
|
-
virtualization: false,
|
|
50
|
-
columnVirtualization: false,
|
|
51
|
-
enableInlineEditing: false,
|
|
52
|
-
showPagination: false,
|
|
53
|
-
showColumnFilters: false,
|
|
54
|
-
showGlobalFilter: false,
|
|
55
|
-
showRowSelection: false,
|
|
56
|
-
enableColumnReorder: opts.enableColumnReorder,
|
|
57
|
-
columnOrder: opts.columnOrder,
|
|
58
|
-
onColumnOrderChange: opts.onColumnOrderChange,
|
|
59
|
-
onApiReady(received) {
|
|
60
|
-
api = received;
|
|
61
|
-
res({
|
|
62
|
-
target,
|
|
63
|
-
api,
|
|
64
|
-
destroy: () => {
|
|
65
|
-
unmount(app);
|
|
66
|
-
target.remove();
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
queueMicrotask(() => { if (!api)
|
|
73
|
-
rej(new Error('onApiReady never fired')); });
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
77
|
-
function headerOrder(target) {
|
|
78
|
-
const ths = target.querySelectorAll('th[data-svgrid-header-col]');
|
|
79
|
-
return Array.from(ths).map((th) => th.dataset.svgridHeaderCol ?? '');
|
|
80
|
-
}
|
|
81
|
-
function header(target, id) {
|
|
82
|
-
// jsdom doesn't ship CSS.escape; our ids are simple alphanumerics so
|
|
83
|
-
// a plain attribute selector is fine.
|
|
84
|
-
const el = target.querySelector(`th[data-svgrid-header-col="${id}"]`);
|
|
85
|
-
if (!el)
|
|
86
|
-
throw new Error(`header not found: ${id}`);
|
|
87
|
-
return el;
|
|
88
|
-
}
|
|
89
|
-
describe('SvGrid column reorder - default off', () => {
|
|
90
|
-
it('does NOT set draggable on headers when the prop is unset', async () => {
|
|
91
|
-
const { target, destroy } = await mountGrid();
|
|
92
|
-
try {
|
|
93
|
-
await tick();
|
|
94
|
-
const draggables = target.querySelectorAll('th[data-svgrid-header-col][draggable="true"]');
|
|
95
|
-
expect(draggables.length).toBe(0);
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
destroy();
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe('SvGrid column reorder - enableColumnReorder=true', () => {
|
|
103
|
-
it('sets draggable=true on every visible header', async () => {
|
|
104
|
-
const { target, destroy } = await mountGrid({ enableColumnReorder: true });
|
|
105
|
-
try {
|
|
106
|
-
await tick();
|
|
107
|
-
const ths = target.querySelectorAll('th[data-svgrid-header-col]');
|
|
108
|
-
expect(ths.length).toBe(cols.length);
|
|
109
|
-
for (const th of ths) {
|
|
110
|
-
expect(th.getAttribute('draggable')).toBe('true');
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
|
-
destroy();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
it('honors the initial columnOrder prop', async () => {
|
|
118
|
-
const order = ['salary', 'id', 'last', 'first', 'team'];
|
|
119
|
-
const { target, destroy } = await mountGrid({
|
|
120
|
-
enableColumnReorder: true,
|
|
121
|
-
columnOrder: order,
|
|
122
|
-
});
|
|
123
|
-
try {
|
|
124
|
-
await tick();
|
|
125
|
-
expect(headerOrder(target)).toEqual(order);
|
|
126
|
-
}
|
|
127
|
-
finally {
|
|
128
|
-
destroy();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
describe('SvGrid column reorder - imperative api', () => {
|
|
133
|
-
it('api.getColumnOrder() returns the current visual order', async () => {
|
|
134
|
-
const { api, destroy } = await mountGrid({ enableColumnReorder: true });
|
|
135
|
-
try {
|
|
136
|
-
expect(api.getColumnOrder()).toEqual(['id', 'first', 'last', 'team', 'salary']);
|
|
137
|
-
}
|
|
138
|
-
finally {
|
|
139
|
-
destroy();
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
it('api.setColumnOrder() reorders the DOM headers', async () => {
|
|
143
|
-
const { target, api, destroy } = await mountGrid({ enableColumnReorder: true });
|
|
144
|
-
try {
|
|
145
|
-
api.setColumnOrder(['team', 'id', 'salary', 'first', 'last']);
|
|
146
|
-
await tick();
|
|
147
|
-
expect(headerOrder(target)).toEqual(['team', 'id', 'salary', 'first', 'last']);
|
|
148
|
-
}
|
|
149
|
-
finally {
|
|
150
|
-
destroy();
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
it('api.setColumnOrder() fires onColumnOrderChange with the new order', async () => {
|
|
154
|
-
const events = [];
|
|
155
|
-
const { api, destroy } = await mountGrid({
|
|
156
|
-
enableColumnReorder: true,
|
|
157
|
-
onColumnOrderChange: (order) => events.push([...order]),
|
|
158
|
-
});
|
|
159
|
-
try {
|
|
160
|
-
api.setColumnOrder(['last', 'first', 'id', 'team', 'salary']);
|
|
161
|
-
await tick();
|
|
162
|
-
expect(events.length).toBeGreaterThanOrEqual(1);
|
|
163
|
-
const latest = events[events.length - 1];
|
|
164
|
-
expect(latest).toEqual(['last', 'first', 'id', 'team', 'salary']);
|
|
165
|
-
}
|
|
166
|
-
finally {
|
|
167
|
-
destroy();
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
it('api.setColumnOrder() with a partial list keeps unlisted columns in their existing relative position', async () => {
|
|
171
|
-
// Pass only ['salary', 'id']; the other three keep their original
|
|
172
|
-
// relative order AFTER the listed ones.
|
|
173
|
-
const { target, api, destroy } = await mountGrid({ enableColumnReorder: true });
|
|
174
|
-
try {
|
|
175
|
-
api.setColumnOrder(['salary', 'id']);
|
|
176
|
-
await tick();
|
|
177
|
-
const order = headerOrder(target);
|
|
178
|
-
expect(order[0]).toBe('salary');
|
|
179
|
-
expect(order[1]).toBe('id');
|
|
180
|
-
// The remaining columns (first / last / team) should follow,
|
|
181
|
-
// in their original relative order from `cols`.
|
|
182
|
-
expect(order.slice(2)).toEqual(['first', 'last', 'team']);
|
|
183
|
-
}
|
|
184
|
-
finally {
|
|
185
|
-
destroy();
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
describe('SvGrid column reorder - drag wiring (DOM)', () => {
|
|
190
|
-
// Note: jsdom does not ship `DragEvent`, so we can't drive the full
|
|
191
|
-
// dragstart → dragover → drop pipeline end-to-end here. Real-browser
|
|
192
|
-
// coverage lives in the Playwright suite. These tests verify that the
|
|
193
|
-
// wiring IS present in the DOM so a real DragEvent would reach the
|
|
194
|
-
// grid's reactive state.
|
|
195
|
-
it('every header has dragstart / dragover / dragleave / drop / dragend listeners attached', async () => {
|
|
196
|
-
const { target, destroy } = await mountGrid({ enableColumnReorder: true });
|
|
197
|
-
try {
|
|
198
|
-
await tick();
|
|
199
|
-
const ths = target.querySelectorAll('th[data-svgrid-header-col]');
|
|
200
|
-
expect(ths.length).toBe(cols.length);
|
|
201
|
-
// Each <th> must be draggable AND have an onclick-equivalent
|
|
202
|
-
// dispatch chain. We can't easily probe addEventListener
|
|
203
|
-
// attachments, so use the `draggable` attribute as the proxy -
|
|
204
|
-
// the grid only sets it when listeners are wired.
|
|
205
|
-
for (const th of ths) {
|
|
206
|
-
expect(th.getAttribute('draggable')).toBe('true');
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
finally {
|
|
210
|
-
destroy();
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
it('does NOT add the drop-target class before any drag is initiated', async () => {
|
|
214
|
-
const { target, destroy } = await mountGrid({ enableColumnReorder: true });
|
|
215
|
-
try {
|
|
216
|
-
await tick();
|
|
217
|
-
const drop = target.querySelectorAll('.is-drag-target-before, .is-drag-target-after, .is-dragging');
|
|
218
|
-
expect(drop.length).toBe(0);
|
|
219
|
-
}
|
|
220
|
-
finally {
|
|
221
|
-
destroy();
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Track C: free-text autocomplete cell type + editable cell comments.
|
|
3
|
-
*/
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { mount, unmount } from 'svelte';
|
|
6
|
-
import SvGrid from './SvGrid.svelte';
|
|
7
|
-
import { createCoreRowModel, tableFeatures } from './index';
|
|
8
|
-
const features = tableFeatures({});
|
|
9
|
-
const rows = [
|
|
10
|
-
{ id: 1, name: 'Ann', city: 'Paris' },
|
|
11
|
-
{ id: 2, name: 'Bo', city: 'Berlin' },
|
|
12
|
-
];
|
|
13
|
-
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
14
|
-
function mountGrid(extraCols, props = {}) {
|
|
15
|
-
return new Promise((res) => {
|
|
16
|
-
const target = document.createElement('div');
|
|
17
|
-
document.body.appendChild(target);
|
|
18
|
-
const app = mount(SvGrid, {
|
|
19
|
-
target,
|
|
20
|
-
props: {
|
|
21
|
-
data: rows,
|
|
22
|
-
columns: [{ field: 'name', header: 'Name', width: 160, editorType: 'text' }, ...extraCols],
|
|
23
|
-
features,
|
|
24
|
-
_rowModels: { coreRowModel: createCoreRowModel() },
|
|
25
|
-
rowHeight: 32,
|
|
26
|
-
containerHeight: 360,
|
|
27
|
-
virtualization: false,
|
|
28
|
-
enableInlineEditing: true,
|
|
29
|
-
enableCellSelection: true,
|
|
30
|
-
onApiReady(api) {
|
|
31
|
-
res({ api, target, destroy: () => { unmount(app); target.remove(); } });
|
|
32
|
-
},
|
|
33
|
-
...props,
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
function dataCell(target, col) {
|
|
39
|
-
return target.querySelectorAll('.sv-grid-cell[data-svgrid-row]')[col];
|
|
40
|
-
}
|
|
41
|
-
describe('autocomplete cell type', () => {
|
|
42
|
-
it('filters suggestions as you type and commits the picked option', async () => {
|
|
43
|
-
const { api, target, destroy } = await mountGrid([
|
|
44
|
-
{
|
|
45
|
-
field: 'city',
|
|
46
|
-
header: 'City',
|
|
47
|
-
width: 200,
|
|
48
|
-
editorType: 'autocomplete',
|
|
49
|
-
editorOptions: ['Paris', 'Berlin', 'Boston', 'Brussels'],
|
|
50
|
-
},
|
|
51
|
-
]);
|
|
52
|
-
await tick();
|
|
53
|
-
// city is the 2nd column => index 1 in the first row's cells
|
|
54
|
-
const cell = target.querySelectorAll('.sv-grid-cell[data-svgrid-row="0"]')[1];
|
|
55
|
-
cell.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
|
|
56
|
-
await tick();
|
|
57
|
-
const input = target.querySelector('.sv-grid-cell-editor-autocomplete');
|
|
58
|
-
expect(input).not.toBeNull();
|
|
59
|
-
input.value = 'B';
|
|
60
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
61
|
-
await tick();
|
|
62
|
-
const opts = [...target.querySelectorAll('.sv-grid-autocomplete-option')].map((b) => b.textContent?.trim());
|
|
63
|
-
// 'B' matches Berlin, Boston, Brussels - not Paris
|
|
64
|
-
expect(opts).toEqual(['Berlin', 'Boston', 'Brussels']);
|
|
65
|
-
const berlin = [...target.querySelectorAll('.sv-grid-autocomplete-option')].find((b) => b.textContent?.trim() === 'Boston');
|
|
66
|
-
berlin.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
67
|
-
await tick();
|
|
68
|
-
expect(api.getCellValue(0, 'city')).toBe('Boston');
|
|
69
|
-
destroy();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
describe('editable comments', () => {
|
|
73
|
-
it('adds a comment via the context menu and emits onNoteChange', async () => {
|
|
74
|
-
const changes = [];
|
|
75
|
-
const { target, destroy } = await mountGrid([{ field: 'city', header: 'City', width: 200, editorType: 'text' }], { contextMenu: true, editableComments: true, onNoteChange: (e) => changes.push(e) });
|
|
76
|
-
await tick();
|
|
77
|
-
const cell = dataCell(target, 0);
|
|
78
|
-
cell.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }));
|
|
79
|
-
await tick();
|
|
80
|
-
const editItem = [...target.querySelectorAll('.sv-grid-context-menu .sv-grid-menu-item')].find((b) => b.textContent?.trim() === 'Edit comment');
|
|
81
|
-
expect(editItem).toBeTruthy();
|
|
82
|
-
editItem.click();
|
|
83
|
-
await tick();
|
|
84
|
-
const ta = target.querySelector('.sv-grid-comment-textarea');
|
|
85
|
-
expect(ta).not.toBeNull();
|
|
86
|
-
ta.value = 'Needs review';
|
|
87
|
-
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
|
88
|
-
await tick();
|
|
89
|
-
target.querySelector('.sv-grid-comment-save').click();
|
|
90
|
-
await tick();
|
|
91
|
-
expect(changes.length).toBe(1);
|
|
92
|
-
expect(changes[0].note).toBe('Needs review');
|
|
93
|
-
expect(target.querySelector('.sv-grid-comment-editor')).toBeNull();
|
|
94
|
-
destroy();
|
|
95
|
-
});
|
|
96
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Right-click context menu (Track A). Mounts a grid with `contextMenu`,
|
|
3
|
-
* fires a synthetic `contextmenu` event on a cell, and asserts the menu
|
|
4
|
-
* opens with the expected items and that actions/close work.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, expect, it } from 'vitest';
|
|
7
|
-
import { mount, unmount } from 'svelte';
|
|
8
|
-
import SvGrid from './SvGrid.svelte';
|
|
9
|
-
import { createCoreRowModel, createFilteredRowModel, createSortedRowModel, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, } from './index';
|
|
10
|
-
const features = tableFeatures({ rowSortingFeature, rowSelectionFeature });
|
|
11
|
-
const rows = Array.from({ length: 6 }, (_, i) => ({
|
|
12
|
-
id: i + 1,
|
|
13
|
-
name: `Person ${i + 1}`,
|
|
14
|
-
team: ['A', 'B'][i % 2],
|
|
15
|
-
}));
|
|
16
|
-
const cols = [
|
|
17
|
-
{ field: 'name', header: 'Name', width: 200, editorType: 'text' },
|
|
18
|
-
{ field: 'team', header: 'Team', width: 160, editorType: 'text' },
|
|
19
|
-
];
|
|
20
|
-
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
21
|
-
function mountGrid(contextMenu) {
|
|
22
|
-
return new Promise((res, rej) => {
|
|
23
|
-
const target = document.createElement('div');
|
|
24
|
-
document.body.appendChild(target);
|
|
25
|
-
const app = mount(SvGrid, {
|
|
26
|
-
target,
|
|
27
|
-
props: {
|
|
28
|
-
data: rows,
|
|
29
|
-
columns: cols,
|
|
30
|
-
features,
|
|
31
|
-
_rowModels: {
|
|
32
|
-
coreRowModel: createCoreRowModel(),
|
|
33
|
-
filteredRowModel: createFilteredRowModel(),
|
|
34
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
35
|
-
},
|
|
36
|
-
rowHeight: 32,
|
|
37
|
-
containerHeight: 400,
|
|
38
|
-
virtualization: false,
|
|
39
|
-
enableCellSelection: true,
|
|
40
|
-
contextMenu,
|
|
41
|
-
onApiReady(api) {
|
|
42
|
-
res({ api, target, destroy: () => { unmount(app); target.remove(); } });
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
queueMicrotask(() => { if (!target.querySelector('[role="grid"]'))
|
|
47
|
-
rej(new Error('no grid')); });
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
function rightClickFirstCell(target) {
|
|
51
|
-
const cell = target.querySelector('.sv-grid-cell[data-svgrid-row]');
|
|
52
|
-
const r = cell.getBoundingClientRect();
|
|
53
|
-
cell.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: r.x + 4, clientY: r.y + 4 }));
|
|
54
|
-
return cell;
|
|
55
|
-
}
|
|
56
|
-
describe('SvGrid context menu', () => {
|
|
57
|
-
it('opens the default menu on right-click with the built-in items', async () => {
|
|
58
|
-
const { target, destroy } = await mountGrid(true);
|
|
59
|
-
await tick();
|
|
60
|
-
rightClickFirstCell(target);
|
|
61
|
-
await tick();
|
|
62
|
-
const menu = target.querySelector('.sv-grid-context-menu');
|
|
63
|
-
expect(menu).not.toBeNull();
|
|
64
|
-
const labels = [...menu.querySelectorAll('.sv-grid-menu-item')].map((b) => b.textContent?.trim());
|
|
65
|
-
expect(labels).toContain('Copy');
|
|
66
|
-
expect(labels).toContain('Paste');
|
|
67
|
-
expect(labels).toContain('Remove row');
|
|
68
|
-
expect(labels).toContain('Remove column');
|
|
69
|
-
destroy();
|
|
70
|
-
});
|
|
71
|
-
it('does not open when contextMenu is omitted (native menu shows)', async () => {
|
|
72
|
-
const { target, destroy } = await mountGrid(undefined);
|
|
73
|
-
await tick();
|
|
74
|
-
const cell = target.querySelector('.sv-grid-cell[data-svgrid-row]');
|
|
75
|
-
const ev = new MouseEvent('contextmenu', { bubbles: true, cancelable: true });
|
|
76
|
-
cell.dispatchEvent(ev);
|
|
77
|
-
await tick();
|
|
78
|
-
expect(target.querySelector('.sv-grid-context-menu')).toBeNull();
|
|
79
|
-
expect(ev.defaultPrevented).toBe(false);
|
|
80
|
-
destroy();
|
|
81
|
-
});
|
|
82
|
-
it('renders only the configured custom items and runs the action', async () => {
|
|
83
|
-
let ran = 0;
|
|
84
|
-
const items = [
|
|
85
|
-
'copy',
|
|
86
|
-
'separator',
|
|
87
|
-
{ key: 'flag', label: 'Flag row', action: () => { ran += 1; } },
|
|
88
|
-
];
|
|
89
|
-
const { target, destroy } = await mountGrid(items);
|
|
90
|
-
await tick();
|
|
91
|
-
rightClickFirstCell(target);
|
|
92
|
-
await tick();
|
|
93
|
-
const labels = [...target.querySelectorAll('.sv-grid-context-menu .sv-grid-menu-item')].map((b) => b.textContent?.trim());
|
|
94
|
-
expect(labels).toEqual(['Copy', 'Flag row']);
|
|
95
|
-
const flag = [...target.querySelectorAll('.sv-grid-context-menu .sv-grid-menu-item')].find((b) => b.textContent?.trim() === 'Flag row');
|
|
96
|
-
flag.click();
|
|
97
|
-
expect(ran).toBe(1);
|
|
98
|
-
await tick();
|
|
99
|
-
expect(target.querySelector('.sv-grid-context-menu')).toBeNull();
|
|
100
|
-
destroy();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createSvGrid, rowExpandingFeature, rowSelectionFeature, rowSortingFeature, sortFns, subscribeSvGrid, tableFeatures, } from './index';
|
|
3
|
-
describe('SvGrid features', () => {
|
|
4
|
-
it('supports sorting and pagination row model pipeline', () => {
|
|
5
|
-
const _features = tableFeatures({
|
|
6
|
-
columnFilteringFeature,
|
|
7
|
-
columnGroupingFeature,
|
|
8
|
-
rowExpandingFeature,
|
|
9
|
-
rowSelectionFeature,
|
|
10
|
-
rowSortingFeature,
|
|
11
|
-
});
|
|
12
|
-
const columns = [
|
|
13
|
-
{ field: 'firstName' },
|
|
14
|
-
{ field: 'age' },
|
|
15
|
-
];
|
|
16
|
-
const data = [
|
|
17
|
-
{ firstName: 'John', age: 40, status: 'pending' },
|
|
18
|
-
{ firstName: 'Amy', age: 22, status: 'active' },
|
|
19
|
-
{ firstName: 'Kate', age: 31, status: 'active' },
|
|
20
|
-
];
|
|
21
|
-
const grid = createSvGrid({
|
|
22
|
-
_features,
|
|
23
|
-
_rowModels: {
|
|
24
|
-
coreRowModel: createCoreRowModel(),
|
|
25
|
-
filteredRowModel: createFilteredRowModel(),
|
|
26
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
27
|
-
groupedRowModel: createGroupedRowModel(),
|
|
28
|
-
expandedRowModel: createExpandedRowModel(),
|
|
29
|
-
paginatedRowModel: createPaginatedRowModel(),
|
|
30
|
-
},
|
|
31
|
-
columns,
|
|
32
|
-
data,
|
|
33
|
-
state: {
|
|
34
|
-
columnFilters: [{ id: 'firstName', value: 'a', fn: 'includesString' }],
|
|
35
|
-
grouping: ['status'],
|
|
36
|
-
expanded: { group_status_active: true },
|
|
37
|
-
sorting: [{ id: 'age', desc: false }],
|
|
38
|
-
pagination: { pageIndex: 0, pageSize: 2 },
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
const rows = grid.getRowModel().rows;
|
|
42
|
-
expect(rows).toHaveLength(1);
|
|
43
|
-
expect(rows[0]?.id).toContain('group_status_');
|
|
44
|
-
});
|
|
45
|
-
it('supports row selection and expansion toggles', () => {
|
|
46
|
-
const _features = tableFeatures({
|
|
47
|
-
rowExpandingFeature,
|
|
48
|
-
rowSelectionFeature,
|
|
49
|
-
});
|
|
50
|
-
const columns = [
|
|
51
|
-
{ field: 'firstName' },
|
|
52
|
-
];
|
|
53
|
-
const data = [
|
|
54
|
-
{ firstName: 'Amy', age: 22 },
|
|
55
|
-
];
|
|
56
|
-
const grid = createSvGrid({
|
|
57
|
-
_features,
|
|
58
|
-
_rowModels: {
|
|
59
|
-
coreRowModel: createCoreRowModel(),
|
|
60
|
-
},
|
|
61
|
-
columns,
|
|
62
|
-
data,
|
|
63
|
-
});
|
|
64
|
-
const row = grid.getRowModel().rows[0];
|
|
65
|
-
expect(row).toBeDefined();
|
|
66
|
-
row?.toggleSelected();
|
|
67
|
-
row?.toggleExpanded();
|
|
68
|
-
expect(row?.getIsSelected()).toBe(true);
|
|
69
|
-
expect(row?.getIsExpanded()).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
it('supports selector subscriptions with stable shape', () => {
|
|
72
|
-
const _features = tableFeatures({ rowSortingFeature });
|
|
73
|
-
const grid = createSvGrid({
|
|
74
|
-
_features,
|
|
75
|
-
_rowModels: {
|
|
76
|
-
coreRowModel: createCoreRowModel(),
|
|
77
|
-
},
|
|
78
|
-
columns: [{ field: 'firstName' }],
|
|
79
|
-
data: [{ firstName: 'Amy', age: 22 }],
|
|
80
|
-
});
|
|
81
|
-
const selected = subscribeSvGrid(grid, (state) => ({
|
|
82
|
-
sorting: state.sorting,
|
|
83
|
-
pageIndex: state.pagination?.pageIndex ?? 0,
|
|
84
|
-
}));
|
|
85
|
-
expect(selected.current.pageIndex).toBe(0);
|
|
86
|
-
});
|
|
87
|
-
it('supports active cell movement primitives', () => {
|
|
88
|
-
const _features = tableFeatures({ rowSortingFeature });
|
|
89
|
-
const grid = createSvGrid({
|
|
90
|
-
_features,
|
|
91
|
-
_rowModels: { coreRowModel: createCoreRowModel() },
|
|
92
|
-
columns: [
|
|
93
|
-
{ field: 'firstName' },
|
|
94
|
-
{ field: 'age' },
|
|
95
|
-
],
|
|
96
|
-
data: [
|
|
97
|
-
{ firstName: 'Amy', age: 22 },
|
|
98
|
-
{ firstName: 'John', age: 40 },
|
|
99
|
-
],
|
|
100
|
-
});
|
|
101
|
-
grid.moveActiveCell({ rowDelta: 1, colDelta: 1 });
|
|
102
|
-
expect(grid.getState().activeCell.rowIndex).toBe(1);
|
|
103
|
-
expect(grid.getState().activeCell.colIndex).toBe(1);
|
|
104
|
-
});
|
|
105
|
-
it('supports multi-column sorting clauses', () => {
|
|
106
|
-
const _features = tableFeatures({ rowSortingFeature });
|
|
107
|
-
const grid = createSvGrid({
|
|
108
|
-
_features,
|
|
109
|
-
_rowModels: {
|
|
110
|
-
coreRowModel: createCoreRowModel(),
|
|
111
|
-
sortedRowModel: createSortedRowModel(sortFns),
|
|
112
|
-
},
|
|
113
|
-
columns: [
|
|
114
|
-
{ field: 'firstName' },
|
|
115
|
-
{ field: 'age', editorType: 'number' },
|
|
116
|
-
],
|
|
117
|
-
data: [
|
|
118
|
-
{ firstName: 'Amy', age: 20 },
|
|
119
|
-
{ firstName: 'Amy', age: 30 },
|
|
120
|
-
{ firstName: 'Bob', age: 10 },
|
|
121
|
-
],
|
|
122
|
-
state: { sorting: [{ id: 'firstName', desc: false }, { id: 'age', desc: true }] },
|
|
123
|
-
});
|
|
124
|
-
const rows = grid.getRowModel().rows;
|
|
125
|
-
expect(rows[0]?.original.firstName).toBe('Amy');
|
|
126
|
-
expect(rows[0]?.original.age).toBe(30);
|
|
127
|
-
expect(rows[1]?.original.age).toBe(20);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1,9 +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
|
-
export {};
|