@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.
Files changed (143) hide show
  1. package/dist/SvGrid.css +294 -16
  2. package/dist/SvGrid.svelte +4 -1
  3. package/dist/SvGridDropdown.svelte +7 -3
  4. package/dist/cdn/svgrid.js +13506 -0
  5. package/dist/cdn/svgrid.svelte-external.js +11190 -0
  6. package/package.json +12 -2
  7. package/src/SvGrid.css +294 -16
  8. package/src/SvGrid.svelte +4 -1
  9. package/src/SvGridDropdown.svelte +7 -3
  10. package/dist/SvGrid.helpers.test.d.ts +0 -1
  11. package/dist/SvGrid.helpers.test.js +0 -298
  12. package/dist/SvGridChart.test.d.ts +0 -1
  13. package/dist/SvGridChart.test.js +0 -62
  14. package/dist/a11y.contract.test.d.ts +0 -1
  15. package/dist/a11y.contract.test.js +0 -39
  16. package/dist/a11y.test.d.ts +0 -1
  17. package/dist/a11y.test.js +0 -44
  18. package/dist/aligned-grids.test.d.ts +0 -1
  19. package/dist/aligned-grids.test.js +0 -75
  20. package/dist/build-api.coverage.test.d.ts +0 -20
  21. package/dist/build-api.coverage.test.js +0 -505
  22. package/dist/cell-formatting.test.d.ts +0 -1
  23. package/dist/cell-formatting.test.js +0 -234
  24. package/dist/cell-render.test.d.ts +0 -1
  25. package/dist/cell-render.test.js +0 -338
  26. package/dist/chart-export.test.d.ts +0 -1
  27. package/dist/chart-export.test.js +0 -302
  28. package/dist/chart.coverage.test.d.ts +0 -1
  29. package/dist/chart.coverage.test.js +0 -748
  30. package/dist/chart.test.d.ts +0 -1
  31. package/dist/chart.test.js +0 -289
  32. package/dist/clipboard.test.d.ts +0 -1
  33. package/dist/clipboard.test.js +0 -700
  34. package/dist/collaboration.coverage.test.d.ts +0 -1
  35. package/dist/collaboration.coverage.test.js +0 -200
  36. package/dist/collaboration.test.d.ts +0 -1
  37. package/dist/collaboration.test.js +0 -65
  38. package/dist/column-groups.test.d.ts +0 -1
  39. package/dist/column-groups.test.js +0 -56
  40. package/dist/column-types.test.d.ts +0 -1
  41. package/dist/column-types.test.js +0 -62
  42. package/dist/columns.test.d.ts +0 -1
  43. package/dist/columns.test.js +0 -625
  44. package/dist/conditional-formatting.test.d.ts +0 -1
  45. package/dist/conditional-formatting.test.js +0 -87
  46. package/dist/core.coverage.test.d.ts +0 -1
  47. package/dist/core.coverage.test.js +0 -186
  48. package/dist/core.performance.test.d.ts +0 -1
  49. package/dist/core.performance.test.js +0 -29
  50. package/dist/createGrid.test.d.ts +0 -1
  51. package/dist/createGrid.test.js +0 -9
  52. package/dist/editing.test.d.ts +0 -1
  53. package/dist/editing.test.js +0 -732
  54. package/dist/editors/cell-editors.coverage.test.d.ts +0 -1
  55. package/dist/editors/cell-editors.coverage.test.js +0 -139
  56. package/dist/editors/cell-editors.test.d.ts +0 -1
  57. package/dist/editors/cell-editors.test.js +0 -75
  58. package/dist/facet-buckets.test.d.ts +0 -1
  59. package/dist/facet-buckets.test.js +0 -296
  60. package/dist/fill-patterns.test.d.ts +0 -1
  61. package/dist/fill-patterns.test.js +0 -81
  62. package/dist/filter-operators.test.d.ts +0 -1
  63. package/dist/filter-operators.test.js +0 -135
  64. package/dist/filtering/excel-filters.test.d.ts +0 -1
  65. package/dist/filtering/excel-filters.test.js +0 -116
  66. package/dist/filtering/locale-filter.test.d.ts +0 -13
  67. package/dist/filtering/locale-filter.test.js +0 -189
  68. package/dist/flex-render.test.d.ts +0 -1
  69. package/dist/flex-render.test.js +0 -142
  70. package/dist/group-aggregate.test.d.ts +0 -1
  71. package/dist/group-aggregate.test.js +0 -32
  72. package/dist/hyperformula-adapter.test.d.ts +0 -1
  73. package/dist/hyperformula-adapter.test.js +0 -205
  74. package/dist/keyboard-handlers.coverage.test.d.ts +0 -1
  75. package/dist/keyboard-handlers.coverage.test.js +0 -495
  76. package/dist/keyboard.test.d.ts +0 -1
  77. package/dist/keyboard.test.js +0 -27
  78. package/dist/menus.test.d.ts +0 -1
  79. package/dist/menus.test.js +0 -560
  80. package/dist/named-views.coverage.test.d.ts +0 -1
  81. package/dist/named-views.coverage.test.js +0 -180
  82. package/dist/named-views.test.d.ts +0 -1
  83. package/dist/named-views.test.js +0 -57
  84. package/dist/new-features.test.d.ts +0 -1
  85. package/dist/new-features.test.js +0 -183
  86. package/dist/render-component.test.d.ts +0 -1
  87. package/dist/render-component.test.js +0 -41
  88. package/dist/row-drag.test.d.ts +0 -1
  89. package/dist/row-drag.test.js +0 -142
  90. package/dist/row-resize.test.d.ts +0 -1
  91. package/dist/row-resize.test.js +0 -329
  92. package/dist/scroll-sync.test.d.ts +0 -1
  93. package/dist/scroll-sync.test.js +0 -290
  94. package/dist/selection.multi-range.test.d.ts +0 -1
  95. package/dist/selection.multi-range.test.js +0 -55
  96. package/dist/selection.test.d.ts +0 -1
  97. package/dist/selection.test.js +0 -647
  98. package/dist/server-data-source.coverage.test.d.ts +0 -1
  99. package/dist/server-data-source.coverage.test.js +0 -154
  100. package/dist/server-data-source.test.d.ts +0 -1
  101. package/dist/server-data-source.test.js +0 -81
  102. package/dist/sparkline.test.d.ts +0 -1
  103. package/dist/sparkline.test.js +0 -52
  104. package/dist/spreadsheet.test.d.ts +0 -1
  105. package/dist/spreadsheet.test.js +0 -446
  106. package/dist/subscribe.test.d.ts +0 -1
  107. package/dist/subscribe.test.js +0 -93
  108. package/dist/svgrid.api-extensions.test.d.ts +0 -14
  109. package/dist/svgrid.api-extensions.test.js +0 -334
  110. package/dist/svgrid.api.test.d.ts +0 -15
  111. package/dist/svgrid.api.test.js +0 -169
  112. package/dist/svgrid.behavior.test.d.ts +0 -15
  113. package/dist/svgrid.behavior.test.js +0 -625
  114. package/dist/svgrid.column-reorder.test.d.ts +0 -15
  115. package/dist/svgrid.column-reorder.test.js +0 -224
  116. package/dist/svgrid.comments-autocomplete.test.d.ts +0 -1
  117. package/dist/svgrid.comments-autocomplete.test.js +0 -96
  118. package/dist/svgrid.context-menu.test.d.ts +0 -1
  119. package/dist/svgrid.context-menu.test.js +0 -102
  120. package/dist/svgrid.features.test.d.ts +0 -1
  121. package/dist/svgrid.features.test.js +0 -129
  122. package/dist/svgrid.interaction.test.d.ts +0 -9
  123. package/dist/svgrid.interaction.test.js +0 -349
  124. package/dist/svgrid.locale-filtering.test.d.ts +0 -15
  125. package/dist/svgrid.locale-filtering.test.js +0 -261
  126. package/dist/svgrid.new-features.wrapper.test.d.ts +0 -1
  127. package/dist/svgrid.new-features.wrapper.test.js +0 -194
  128. package/dist/svgrid.row-pinning.test.d.ts +0 -14
  129. package/dist/svgrid.row-pinning.test.js +0 -250
  130. package/dist/svgrid.set-filter.test.d.ts +0 -17
  131. package/dist/svgrid.set-filter.test.js +0 -268
  132. package/dist/svgrid.wrapper.test.d.ts +0 -1
  133. package/dist/svgrid.wrapper.test.js +0 -59
  134. package/dist/test-setup.d.ts +0 -1
  135. package/dist/test-setup.js +0 -35
  136. package/dist/transaction.test.d.ts +0 -1
  137. package/dist/transaction.test.js +0 -89
  138. package/dist/virtualization/column-virtualizer.test.d.ts +0 -1
  139. package/dist/virtualization/column-virtualizer.test.js +0 -25
  140. package/dist/virtualization/scroll-scaling.test.d.ts +0 -1
  141. package/dist/virtualization/scroll-scaling.test.js +0 -127
  142. package/dist/virtualization/virtualizer.test.d.ts +0 -1
  143. package/dist/virtualization/virtualizer.test.js +0 -41
@@ -1,180 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { attachAutoSavedView, createNamedViews, localStorageViews, memoryViews, } from './named-views';
3
- function fakeHost(initial = {}) {
4
- let state = { sorting: [], columnFilters: [], ...initial };
5
- return {
6
- getState: () => state,
7
- setState: vi.fn((s) => {
8
- state = { ...state, ...s };
9
- }),
10
- set: (s) => {
11
- state = s;
12
- },
13
- };
14
- }
15
- describe('createNamedViews - list ordering and edge cases', () => {
16
- it('lists views sorted by createdAt ascending', () => {
17
- const store = memoryViews([
18
- { name: 'late', state: {}, createdAt: 200 },
19
- { name: 'early', state: {}, createdAt: 100 },
20
- ]);
21
- const views = createNamedViews(fakeHost(), { storage: store });
22
- expect(views.list().map((v) => v.name)).toEqual(['early', 'late']);
23
- });
24
- it('defaults to memory storage when none is passed', () => {
25
- const views = createNamedViews(fakeHost({ sorting: [{ id: 'a', desc: true }] }));
26
- views.save('only');
27
- expect(views.has('only')).toBe(true);
28
- expect(views.list()).toHaveLength(1);
29
- });
30
- it('rename returns false when the source name is unknown', () => {
31
- const views = createNamedViews(fakeHost(), { storage: memoryViews() });
32
- views.save('a');
33
- expect(views.rename('missing', 'b')).toBe(false);
34
- expect(views.list().map((v) => v.name)).toEqual(['a']);
35
- });
36
- it('load returns false and does not call setState for unknown name', () => {
37
- const host = fakeHost();
38
- const views = createNamedViews(host, { storage: memoryViews() });
39
- host.setState.mockClear();
40
- expect(views.load('nope')).toBe(false);
41
- expect(host.setState).not.toHaveBeenCalled();
42
- });
43
- it('save captures the current host state snapshot', () => {
44
- const host = fakeHost({ sorting: [{ id: 'z', desc: false }] });
45
- const views = createNamedViews(host, { storage: memoryViews() });
46
- const v = views.save('snap');
47
- expect(v.state).toMatchObject({ sorting: [{ id: 'z', desc: false }] });
48
- expect(typeof v.createdAt).toBe('number');
49
- });
50
- });
51
- describe('localStorageViews', () => {
52
- beforeEach(() => localStorage.clear());
53
- afterEach(() => localStorage.clear());
54
- it('reads [] when the key is absent', () => {
55
- const store = localStorageViews('svgrid:absent');
56
- expect(store.read()).toEqual([]);
57
- });
58
- it('round-trips views through localStorage', () => {
59
- const store = localStorageViews('svgrid:rt');
60
- const views = [{ name: 'x', state: {}, createdAt: 1 }];
61
- store.write(views);
62
- expect(store.read()).toEqual(views);
63
- // raw JSON is actually present
64
- expect(JSON.parse(localStorage.getItem('svgrid:rt'))).toEqual(views);
65
- });
66
- it('returns [] for corrupt JSON', () => {
67
- localStorage.setItem('svgrid:bad', '{not valid json');
68
- expect(localStorageViews('svgrid:bad').read()).toEqual([]);
69
- });
70
- it('returns [] when the stored value is not an array', () => {
71
- localStorage.setItem('svgrid:obj', JSON.stringify({ foo: 'bar' }));
72
- expect(localStorageViews('svgrid:obj').read()).toEqual([]);
73
- });
74
- it('swallows write errors (quota / private mode)', () => {
75
- const store = localStorageViews('svgrid:quota');
76
- const spy = vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
77
- throw new DOMException('quota', 'QuotaExceededError');
78
- });
79
- expect(() => store.write([{ name: 'a', state: {}, createdAt: 1 }])).not.toThrow();
80
- spy.mockRestore();
81
- });
82
- it('works end-to-end as a NamedViews adapter', () => {
83
- const views = createNamedViews(fakeHost({ sorting: [{ id: 'p', desc: true }] }), {
84
- storage: localStorageViews('svgrid:adapter'),
85
- });
86
- views.save('persisted');
87
- const reopened = createNamedViews(fakeHost(), {
88
- storage: localStorageViews('svgrid:adapter'),
89
- });
90
- expect(reopened.has('persisted')).toBe(true);
91
- expect(reopened.load('persisted')).toBe(true);
92
- });
93
- });
94
- describe('attachAutoSavedView', () => {
95
- beforeEach(() => vi.useFakeTimers());
96
- afterEach(() => vi.useRealTimers());
97
- it('restores an existing autosave slot on attach', () => {
98
- const host = fakeHost();
99
- const store = memoryViews([
100
- { name: '__autosave', state: { sorting: [{ id: 'restored', desc: true }] }, createdAt: 1 },
101
- ]);
102
- const views = createNamedViews(host, { storage: store });
103
- const off = attachAutoSavedView(host, views);
104
- expect(host.setState).toHaveBeenCalledWith(expect.objectContaining({ sorting: [{ id: 'restored', desc: true }] }));
105
- off();
106
- });
107
- it('skips restore when skipRestore is set', () => {
108
- const host = fakeHost();
109
- const store = memoryViews([{ name: '__autosave', state: { sorting: [] }, createdAt: 1 }]);
110
- const views = createNamedViews(host, { storage: store });
111
- const off = attachAutoSavedView(host, views, { skipRestore: true });
112
- expect(host.setState).not.toHaveBeenCalled();
113
- off();
114
- });
115
- it('saves when the state snapshot changes after an interval', () => {
116
- const host = fakeHost({ sorting: [] });
117
- const views = createNamedViews(host, { storage: memoryViews() });
118
- const off = attachAutoSavedView(host, views, { intervalMs: 500 });
119
- expect(views.has('__autosave')).toBe(false);
120
- // mutate the host state, then let the poller tick
121
- host.set({ sorting: [{ id: 'new', desc: false }], columnFilters: [] });
122
- vi.advanceTimersByTime(500);
123
- expect(views.has('__autosave')).toBe(true);
124
- off();
125
- });
126
- it('does not save when the snapshot is unchanged', () => {
127
- const host = fakeHost({ sorting: [] });
128
- const views = createNamedViews(host, { storage: memoryViews() });
129
- const saveSpy = vi.spyOn(views, 'save');
130
- const off = attachAutoSavedView(host, views, { intervalMs: 300 });
131
- vi.advanceTimersByTime(900); // several ticks, no state change
132
- expect(saveSpy).not.toHaveBeenCalled();
133
- off();
134
- });
135
- it('uses a custom slot name', () => {
136
- const host = fakeHost({ sorting: [] });
137
- const views = createNamedViews(host, { storage: memoryViews() });
138
- const off = attachAutoSavedView(host, views, { name: 'mySlot', intervalMs: 200 });
139
- host.set({ sorting: [{ id: 'q', desc: true }], columnFilters: [] });
140
- vi.advanceTimersByTime(200);
141
- expect(views.has('mySlot')).toBe(true);
142
- off();
143
- });
144
- it('clamps the interval to a minimum of 100ms', () => {
145
- const host = fakeHost({ sorting: [] });
146
- const views = createNamedViews(host, { storage: memoryViews() });
147
- const setIntervalSpy = vi.spyOn(globalThis, 'setInterval');
148
- const off = attachAutoSavedView(host, views, { intervalMs: 5 });
149
- expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 100);
150
- off();
151
- setIntervalSpy.mockRestore();
152
- });
153
- it('recovers when getState throws inside the poll loop', () => {
154
- let throwing = false;
155
- const host = {
156
- getState: () => {
157
- if (throwing)
158
- throw new Error('boom');
159
- return { sorting: [], columnFilters: [] };
160
- },
161
- setState: vi.fn(),
162
- };
163
- const views = createNamedViews(host, { storage: memoryViews() });
164
- const saveSpy = vi.spyOn(views, 'save');
165
- const off = attachAutoSavedView(host, views, { intervalMs: 200 });
166
- throwing = true;
167
- expect(() => vi.advanceTimersByTime(400)).not.toThrow();
168
- expect(saveSpy).not.toHaveBeenCalled();
169
- off();
170
- });
171
- it('detach stops the polling interval', () => {
172
- const host = fakeHost({ sorting: [] });
173
- const views = createNamedViews(host, { storage: memoryViews() });
174
- const clearSpy = vi.spyOn(globalThis, 'clearInterval');
175
- const off = attachAutoSavedView(host, views, { intervalMs: 200 });
176
- off();
177
- expect(clearSpy).toHaveBeenCalled();
178
- clearSpy.mockRestore();
179
- });
180
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,57 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { createNamedViews, memoryViews } from './named-views';
3
- function fakeHost(initial = {}) {
4
- let state = { sorting: [], columnFilters: [], ...initial };
5
- return {
6
- getState: () => state,
7
- setState: vi.fn((s) => {
8
- state = { ...state, ...s };
9
- }),
10
- _peek: () => state,
11
- };
12
- }
13
- describe('createNamedViews', () => {
14
- it('saves the current state and lists it', () => {
15
- const host = fakeHost({ sorting: [{ id: 'name', desc: true }] });
16
- const views = createNamedViews(host, { storage: memoryViews() });
17
- views.save('By name');
18
- expect(views.list().map((v) => v.name)).toEqual(['By name']);
19
- expect(views.has('By name')).toBe(true);
20
- });
21
- it('load applies the saved state via setState', () => {
22
- const host = fakeHost({ sorting: [{ id: 'age', desc: false }] });
23
- const views = createNamedViews(host, { storage: memoryViews() });
24
- views.save('snap');
25
- host.setState({ sorting: [] }); // mutate away
26
- host.setState.mockClear();
27
- expect(views.load('snap')).toBe(true);
28
- expect(host.setState).toHaveBeenCalledWith(expect.objectContaining({ sorting: [{ id: 'age', desc: false }] }));
29
- expect(views.load('missing')).toBe(false);
30
- });
31
- it('save overwrites a duplicate name but keeps its createdAt', () => {
32
- const host = fakeHost();
33
- const views = createNamedViews(host, { storage: memoryViews() });
34
- const a = views.save('v');
35
- const b = views.save('v');
36
- expect(views.list()).toHaveLength(1);
37
- expect(b.createdAt).toBe(a.createdAt);
38
- });
39
- it('remove and rename', () => {
40
- const host = fakeHost();
41
- const views = createNamedViews(host, { storage: memoryViews() });
42
- views.save('a');
43
- views.save('b');
44
- expect(views.rename('a', 'c')).toBe(true);
45
- expect(views.rename('c', 'b')).toBe(false); // target exists
46
- expect(views.remove('c')).toBe(true);
47
- expect(views.remove('c')).toBe(false);
48
- expect(views.list().map((v) => v.name)).toEqual(['b']);
49
- });
50
- it('persists through a storage adapter', () => {
51
- const store = memoryViews();
52
- createNamedViews(fakeHost({ sorting: [{ id: 'x', desc: true }] }), { storage: store }).save('keep');
53
- // A fresh manager over the same storage sees it.
54
- const reopened = createNamedViews(fakeHost(), { storage: store });
55
- expect(reopened.has('keep')).toBe(true);
56
- });
57
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,183 +0,0 @@
1
- /**
2
- * Tests for the headless-engine features shipped alongside the wrapper
3
- * additions (`getRowId`, the `between` filter operator). Wrapper-level
4
- * additions are covered by the source-level smoke tests in
5
- * `svgrid.new-features.wrapper.test.ts` - jsdom + Svelte 5 mount is
6
- * brittle for tests that only need to confirm props are read.
7
- */
8
- import { describe, expect, it } from 'vitest';
9
- import { columnFilteringFeature, createCoreRowModel, createSvGrid, rowSortingFeature, tableFeatures, } from './index';
10
- import { applyExcelFilter } from './filtering/excel-filters';
11
- const data = [
12
- { id: 'ada', name: 'Ada Lovelace', age: 36 },
13
- { id: 'linus', name: 'Linus Torvalds', age: 54 },
14
- { id: 'grace', name: 'Grace Hopper', age: 85 },
15
- ];
16
- const columns = [
17
- { field: 'id', header: 'ID' },
18
- { field: 'name', header: 'Name' },
19
- { field: 'age', header: 'Age' },
20
- ];
21
- const features = tableFeatures({ rowSortingFeature });
22
- describe('createSvGrid - getRowId', () => {
23
- it('falls back to the array index as id when no getRowId is provided', () => {
24
- const grid = createSvGrid({
25
- _features: features,
26
- _rowModels: { coreRowModel: createCoreRowModel() },
27
- columns,
28
- data,
29
- });
30
- const rows = grid.getRowModel().rows;
31
- expect(rows.map((r) => r.id)).toEqual(['0', '1', '2']);
32
- });
33
- it('uses the result of getRowId(row, index) as the row id', () => {
34
- const grid = createSvGrid({
35
- _features: features,
36
- _rowModels: { coreRowModel: createCoreRowModel() },
37
- columns,
38
- data,
39
- getRowId: (row) => row.id,
40
- });
41
- const rows = grid.getRowModel().rows;
42
- expect(rows.map((r) => r.id)).toEqual(['ada', 'linus', 'grace']);
43
- });
44
- it('passes the row index to getRowId', () => {
45
- const seen = [];
46
- createSvGrid({
47
- _features: features,
48
- _rowModels: { coreRowModel: createCoreRowModel() },
49
- columns,
50
- data,
51
- getRowId: (row, index) => {
52
- seen.push({ id: row.id, index });
53
- return row.id;
54
- },
55
- }).getRowModel().rows;
56
- expect(seen).toEqual([
57
- { id: 'ada', index: 0 },
58
- { id: 'linus', index: 1 },
59
- { id: 'grace', index: 2 },
60
- ]);
61
- });
62
- it('preserves the same id across `getRowModel()` invocations (stable identity)', () => {
63
- const grid = createSvGrid({
64
- _features: features,
65
- _rowModels: { coreRowModel: createCoreRowModel() },
66
- columns,
67
- data,
68
- getRowId: (row) => `r:${row.id}`,
69
- });
70
- const first = grid.getRowModel().rows.map((r) => r.id);
71
- const second = grid.getRowModel().rows.map((r) => r.id);
72
- expect(first).toEqual(second);
73
- expect(first).toEqual(['r:ada', 'r:linus', 'r:grace']);
74
- });
75
- });
76
- describe('applyExcelFilter - between operator', () => {
77
- it('returns true when the value is inside the inclusive range', () => {
78
- expect(applyExcelFilter(50, { id: 'age', operator: 'between', value: 18, valueTo: 65 })).toBe(true);
79
- });
80
- it('is inclusive at the lower bound', () => {
81
- expect(applyExcelFilter(18, { id: 'age', operator: 'between', value: 18, valueTo: 65 })).toBe(true);
82
- });
83
- it('is inclusive at the upper bound', () => {
84
- expect(applyExcelFilter(65, { id: 'age', operator: 'between', value: 18, valueTo: 65 })).toBe(true);
85
- });
86
- it('returns false when the value is below the range', () => {
87
- expect(applyExcelFilter(17, { id: 'age', operator: 'between', value: 18, valueTo: 65 })).toBe(false);
88
- });
89
- it('returns false when the value is above the range', () => {
90
- expect(applyExcelFilter(66, { id: 'age', operator: 'between', value: 18, valueTo: 65 })).toBe(false);
91
- });
92
- it('parses string endpoints as numbers (the wrapper hands strings through)', () => {
93
- expect(applyExcelFilter('30', { id: 'age', operator: 'between', value: '18', valueTo: '65' })).toBe(true);
94
- });
95
- it('works with ISO date strings via lexicographic compare', () => {
96
- expect(applyExcelFilter('2026-06-15', {
97
- id: 'd', operator: 'between', value: '2026-01-01', valueTo: '2026-12-31',
98
- })).toBe(true);
99
- expect(applyExcelFilter('2025-12-31', {
100
- id: 'd', operator: 'between', value: '2026-01-01', valueTo: '2026-12-31',
101
- })).toBe(false);
102
- expect(applyExcelFilter('2027-01-01', {
103
- id: 'd', operator: 'between', value: '2026-01-01', valueTo: '2026-12-31',
104
- })).toBe(false);
105
- });
106
- });
107
- describe('ColumnDef.sortable + ColumnDef.filterable', () => {
108
- const features = tableFeatures({
109
- rowSortingFeature,
110
- columnFilteringFeature,
111
- });
112
- function columnsWith(opts) {
113
- return [
114
- { field: 'id', header: 'ID' },
115
- { field: 'name', header: 'Name', sortable: opts.nameSortable, filterable: opts.nameFilterable },
116
- { field: 'age', header: 'Age', sortable: opts.ageSortable },
117
- ];
118
- }
119
- it('getCanSort() returns true by default when rowSortingFeature is on', () => {
120
- const grid = createSvGrid({
121
- _features: features,
122
- _rowModels: { coreRowModel: createCoreRowModel() },
123
- data,
124
- columns: columnsWith({}),
125
- });
126
- const name = grid.getAllColumns().find((c) => c.id === 'name');
127
- expect(name?.getCanSort()).toBe(true);
128
- });
129
- it('getCanSort() returns false when columnDef.sortable === false', () => {
130
- const grid = createSvGrid({
131
- _features: features,
132
- _rowModels: { coreRowModel: createCoreRowModel() },
133
- data,
134
- columns: columnsWith({ nameSortable: false }),
135
- });
136
- const name = grid.getAllColumns().find((c) => c.id === 'name');
137
- const age = grid.getAllColumns().find((c) => c.id === 'age');
138
- expect(name?.getCanSort()).toBe(false);
139
- expect(age?.getCanSort()).toBe(true);
140
- });
141
- it('getCanSort() still returns false even with sortable: true if the feature is OFF', () => {
142
- const noSortFeatures = tableFeatures({ columnFilteringFeature });
143
- const grid = createSvGrid({
144
- _features: noSortFeatures,
145
- _rowModels: { coreRowModel: createCoreRowModel() },
146
- data,
147
- columns: [
148
- { field: 'name', header: 'Name', sortable: true },
149
- ],
150
- });
151
- const name = grid.getAllColumns().find((c) => c.id === 'name');
152
- expect(name?.getCanSort()).toBe(false);
153
- });
154
- it('getCanFilter() returns false when columnDef.filterable === false', () => {
155
- const grid = createSvGrid({
156
- _features: features,
157
- _rowModels: { coreRowModel: createCoreRowModel() },
158
- data,
159
- columns: columnsWith({ nameFilterable: false }),
160
- });
161
- const name = grid.getAllColumns().find((c) => c.id === 'name');
162
- const age = grid.getAllColumns().find((c) => c.id === 'age');
163
- expect(name?.getCanFilter()).toBe(false);
164
- expect(age?.getCanFilter()).toBe(true);
165
- });
166
- it('sortable + filterable are independent', () => {
167
- const grid = createSvGrid({
168
- _features: features,
169
- _rowModels: { coreRowModel: createCoreRowModel() },
170
- data,
171
- columns: [
172
- { field: 'name', header: 'Name', sortable: false, filterable: true },
173
- { field: 'age', header: 'Age', sortable: true, filterable: false },
174
- ],
175
- });
176
- const name = grid.getAllColumns().find((c) => c.id === 'name');
177
- const age = grid.getAllColumns().find((c) => c.id === 'age');
178
- expect(name?.getCanSort()).toBe(false);
179
- expect(name?.getCanFilter()).toBe(true);
180
- expect(age?.getCanSort()).toBe(true);
181
- expect(age?.getCanFilter()).toBe(false);
182
- });
183
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,41 +0,0 @@
1
- /**
2
- * Unit tests for the renderComponent / renderSnippet factories.
3
- */
4
- import { describe, expect, it } from 'vitest';
5
- import { RenderComponentConfig, RenderSnippetConfig, renderComponent, renderSnippet, } from './render-component';
6
- describe('renderComponent', () => {
7
- it('returns a RenderComponentConfig instance', () => {
8
- const FakeComponent = (() => null);
9
- const config = renderComponent(FakeComponent);
10
- expect(config).toBeInstanceOf(RenderComponentConfig);
11
- expect(config.component).toBe(FakeComponent);
12
- expect(config.props).toBeUndefined();
13
- });
14
- it('captures the props payload as-is', () => {
15
- const FakeComponent = (() => null);
16
- const config = renderComponent(FakeComponent, { id: 1, label: 'Hello' });
17
- expect(config.props).toEqual({ id: 1, label: 'Hello' });
18
- });
19
- it('reuses the constructor instance fields', () => {
20
- const FakeComponent = (() => null);
21
- const a = renderComponent(FakeComponent, { x: 1 });
22
- const b = renderComponent(FakeComponent, { x: 1 });
23
- // Different instances - the factory does not memoize.
24
- expect(a).not.toBe(b);
25
- expect(a.component).toBe(b.component);
26
- });
27
- });
28
- describe('renderSnippet', () => {
29
- it('returns a RenderSnippetConfig instance with the captured snippet + params', () => {
30
- const fakeSnippet = (() => null);
31
- const config = renderSnippet(fakeSnippet, { value: 42 });
32
- expect(config).toBeInstanceOf(RenderSnippetConfig);
33
- expect(config.snippet).toBe(fakeSnippet);
34
- expect(config.params).toEqual({ value: 42 });
35
- });
36
- it('allows an undefined params payload', () => {
37
- const fakeSnippet = (() => null);
38
- const config = renderSnippet(fakeSnippet);
39
- expect(config.params).toBeUndefined();
40
- });
41
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,142 +0,0 @@
1
- /**
2
- * Unit tests for the managed row-drag handlers produced by createRowDrag(ctx).
3
- *
4
- * createRowDrag is a factory closing over a mutable `ctx` handle and a
5
- * module-level singleton "bus" that carries the dragged row between grids.
6
- * Because the bus is shared, we can create TWO fake ctxs (two grids) and drive
7
- * a genuine grid-to-grid move without mounting anything.
8
- */
9
- import { describe, expect, it, vi } from 'vitest';
10
- import { createRowDrag, rowDropZone } from './row-drag';
11
- function makeCtx(rows, props = {}) {
12
- return {
13
- props,
14
- internalData: rows,
15
- get allRows() {
16
- // allRows tracks internalData order; each Row wraps `.original`.
17
- return this.internalData.map((r) => ({ original: r }));
18
- },
19
- rowDragActive: false,
20
- rowDropIndex: null,
21
- rowDropSide: null,
22
- };
23
- }
24
- function fakeEvent(clientY = 0, rectTop = 0, rectHeight = 40) {
25
- return {
26
- clientY,
27
- preventDefault: vi.fn(),
28
- stopPropagation: vi.fn(),
29
- dataTransfer: { setData: vi.fn(), effectAllowed: '', dropEffect: '' },
30
- currentTarget: {
31
- getBoundingClientRect: () => ({ top: rectTop, height: rectHeight, bottom: rectTop + rectHeight, left: 0 }),
32
- },
33
- };
34
- }
35
- const P = { rowDragManaged: true };
36
- describe('createRowDrag - same-grid reorder', () => {
37
- it('moves a dragged row after the drop target', () => {
38
- const rows = [
39
- { id: 1, title: 'a' },
40
- { id: 2, title: 'b' },
41
- { id: 3, title: 'c' },
42
- { id: 4, title: 'd' },
43
- ];
44
- const ctx = makeCtx(rows, { ...P });
45
- const h = createRowDrag(ctx);
46
- h.onRowDragStart(fakeEvent(), 0); // drag "a"
47
- ctx.rowDropSide = 'after';
48
- h.onRowDrop(fakeEvent(), 2); // drop after "c"
49
- expect(ctx.internalData.map((r) => r.title)).toEqual(['b', 'c', 'a', 'd']);
50
- });
51
- it('drops before the target when the side is "before"', () => {
52
- const rows = [
53
- { id: 1, title: 'a' },
54
- { id: 2, title: 'b' },
55
- { id: 3, title: 'c' },
56
- ];
57
- const ctx = makeCtx(rows, { ...P });
58
- const h = createRowDrag(ctx);
59
- h.onRowDragStart(fakeEvent(), 2); // drag "c"
60
- ctx.rowDropSide = 'before';
61
- h.onRowDrop(fakeEvent(), 0); // before "a"
62
- expect(ctx.internalData.map((r) => r.title)).toEqual(['c', 'a', 'b']);
63
- });
64
- });
65
- describe('createRowDrag - grid-to-grid', () => {
66
- it('moves a row from source into target when groups match', () => {
67
- const a = makeCtx([{ id: 1, title: 'a1' }, { id: 2, title: 'a2' }], { ...P, rowDragGroup: 'g' });
68
- const b = makeCtx([{ id: 9, title: 'b1' }], { ...P, rowDragGroup: 'g', onRowDragEnd: vi.fn() });
69
- const ha = createRowDrag(a);
70
- const hb = createRowDrag(b);
71
- ha.onRowDragStart(fakeEvent(), 1); // drag "a2" out of grid A
72
- b.rowDropSide = 'before';
73
- hb.onRowDrop(fakeEvent(), 0); // drop onto grid B before "b1"
74
- expect(a.internalData.map((r) => r.title)).toEqual(['a1']);
75
- expect(b.internalData.map((r) => r.title)).toEqual(['a2', 'b1']);
76
- expect(b.props.onRowDragEnd).toHaveBeenCalledWith(expect.objectContaining({ sameGrid: false, toIndex: 0, row: expect.objectContaining({ title: 'a2' }) }));
77
- });
78
- it('does NOT accept a drop when groups differ', () => {
79
- const a = makeCtx([{ id: 1, title: 'a1' }, { id: 2, title: 'a2' }], { ...P, rowDragGroup: 'g' });
80
- const b = makeCtx([{ id: 9, title: 'b1' }], { ...P, rowDragGroup: 'other' });
81
- const ha = createRowDrag(a);
82
- const hb = createRowDrag(b);
83
- ha.onRowDragStart(fakeEvent(), 0);
84
- const ev = fakeEvent();
85
- hb.onRowDragOver(ev, 0);
86
- expect(ev.preventDefault).not.toHaveBeenCalled(); // over rejected
87
- hb.onRowDrop(fakeEvent(), 0);
88
- expect(b.internalData.map((r) => r.title)).toEqual(['b1']); // unchanged
89
- expect(a.internalData.length).toBe(2); // source untouched
90
- });
91
- it('appends to the target on an empty-space (container) drop', () => {
92
- const a = makeCtx([{ id: 1, title: 'a1' }, { id: 2, title: 'a2' }], { ...P, rowDragGroup: 'g' });
93
- const b = makeCtx([{ id: 9, title: 'b1' }], { ...P, rowDragGroup: 'g' });
94
- const ha = createRowDrag(a);
95
- const hb = createRowDrag(b);
96
- ha.onRowDragStart(fakeEvent(), 0); // drag "a1"
97
- hb.onRowsContainerDrop(fakeEvent());
98
- expect(b.internalData.map((r) => r.title)).toEqual(['b1', 'a1']);
99
- expect(a.internalData.map((r) => r.title)).toEqual(['a2']);
100
- });
101
- });
102
- describe('createRowDrag - disabled', () => {
103
- it('is inert when rowDragManaged is off', () => {
104
- const ctx = makeCtx([{ id: 1, title: 'a' }, { id: 2, title: 'b' }], {});
105
- const h = createRowDrag(ctx);
106
- h.onRowDragStart(fakeEvent(), 0);
107
- ctx.rowDropSide = 'after';
108
- h.onRowDrop(fakeEvent(), 1);
109
- expect(ctx.internalData.map((r) => r.title)).toEqual(['a', 'b']);
110
- });
111
- });
112
- describe('rowDropZone (external drop target)', () => {
113
- function dropOn(el) {
114
- const ev = new Event('drop', { bubbles: true, cancelable: true });
115
- el.dispatchEvent(ev);
116
- }
117
- it('removes the dragged row from its source grid and calls onDrop', () => {
118
- const a = makeCtx([{ id: 1, title: 'a1' }, { id: 2, title: 'a2' }], { ...P, rowDragGroup: 'g' });
119
- const ha = createRowDrag(a);
120
- ha.onRowDragStart(fakeEvent(), 0); // start dragging "a1"
121
- const el = document.createElement('div');
122
- const dropped = [];
123
- const action = rowDropZone(el, { group: 'g', onDrop: (e) => dropped.push(e) });
124
- dropOn(el);
125
- expect(dropped.length).toBe(1);
126
- expect(dropped[0].row.title).toBe('a1');
127
- expect(a.internalData.map((r) => r.title)).toEqual(['a2']); // removed from source
128
- action.destroy();
129
- });
130
- it('ignores drops when the group does not match', () => {
131
- const a = makeCtx([{ id: 1, title: 'a1' }], { ...P, rowDragGroup: 'g' });
132
- const ha = createRowDrag(a);
133
- ha.onRowDragStart(fakeEvent(), 0);
134
- const el = document.createElement('div');
135
- const dropped = [];
136
- const action = rowDropZone(el, { group: 'other', onDrop: (e) => dropped.push(e) });
137
- dropOn(el);
138
- expect(dropped.length).toBe(0);
139
- expect(a.internalData.length).toBe(1); // untouched
140
- action.destroy();
141
- });
142
- });
@@ -1 +0,0 @@
1
- export {};