@svgrid/grid 1.1.1 → 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.
Files changed (188) hide show
  1. package/dist/FlexRender.svelte +96 -96
  2. package/dist/SvGrid.controller.svelte.js +17 -6
  3. package/dist/SvGrid.css +2012 -2012
  4. package/dist/SvGrid.svelte +2571 -2346
  5. package/dist/SvGridDropdown.svelte +7 -3
  6. package/dist/build-api.js +2 -2
  7. package/dist/cdn/svgrid.js +13504 -0
  8. package/dist/cdn/svgrid.svelte-external.js +11188 -0
  9. package/dist/cell-values.d.ts +1 -1
  10. package/dist/cell-values.js +7 -7
  11. package/dist/column-types.js +1 -1
  12. package/dist/core.d.ts +1 -1
  13. package/dist/core.js +2 -2
  14. package/dist/summaries.js +4 -4
  15. package/package.json +12 -2
  16. package/src/FlexRender.svelte +96 -96
  17. package/src/SvGrid.controller.svelte.ts +2363 -2352
  18. package/src/SvGrid.css +2012 -2012
  19. package/src/SvGrid.svelte +2571 -2346
  20. package/src/SvGrid.types.ts +537 -537
  21. package/src/SvGridDropdown.svelte +7 -3
  22. package/src/a11y.contract.test.ts +49 -49
  23. package/src/a11y.test.ts +59 -59
  24. package/src/a11y.ts +59 -59
  25. package/src/build-api.ts +683 -683
  26. package/src/cell-formatting.ts +169 -169
  27. package/src/cell-values.ts +4 -4
  28. package/src/column-types.ts +1 -1
  29. package/src/core.performance.test.ts +30 -30
  30. package/src/core.ts +1077 -1077
  31. package/src/createGrid.svelte.ts +42 -42
  32. package/src/createGrid.test.ts +10 -10
  33. package/src/createGridState.svelte.ts +17 -17
  34. package/src/editing.ts +669 -669
  35. package/src/flex-render.ts +3 -3
  36. package/src/index.ts +208 -208
  37. package/src/keyboard.test.ts +59 -59
  38. package/src/keyboard.ts +97 -97
  39. package/src/merge-objects.ts +48 -48
  40. package/src/render-component.ts +28 -28
  41. package/src/spreadsheet.test.ts +489 -489
  42. package/src/spreadsheet.ts +304 -304
  43. package/src/static-functions.ts +11 -11
  44. package/src/subscribe.ts +38 -38
  45. package/src/summaries.ts +4 -4
  46. package/src/svgrid-wrapper.types.ts +412 -412
  47. package/src/svgrid.features.test.ts +157 -157
  48. package/src/svgrid.wrapper.test.ts +40 -40
  49. package/src/virtualization/column-virtualizer.test.ts +27 -27
  50. package/src/virtualization/column-virtualizer.ts +30 -30
  51. package/src/virtualization/svelte-virtualizer.svelte.ts +26 -26
  52. package/src/virtualization/types.ts +30 -30
  53. package/src/virtualization/virtualizer.test.ts +47 -47
  54. package/src/virtualization/virtualizer.ts +296 -296
  55. package/dist/SvGrid.helpers.test.d.ts +0 -1
  56. package/dist/SvGrid.helpers.test.js +0 -298
  57. package/dist/SvGridChart.test.d.ts +0 -1
  58. package/dist/SvGridChart.test.js +0 -62
  59. package/dist/a11y.contract.test.d.ts +0 -1
  60. package/dist/a11y.contract.test.js +0 -39
  61. package/dist/a11y.test.d.ts +0 -1
  62. package/dist/a11y.test.js +0 -44
  63. package/dist/aligned-grids.test.d.ts +0 -1
  64. package/dist/aligned-grids.test.js +0 -75
  65. package/dist/build-api.coverage.test.d.ts +0 -20
  66. package/dist/build-api.coverage.test.js +0 -505
  67. package/dist/cell-formatting.test.d.ts +0 -1
  68. package/dist/cell-formatting.test.js +0 -234
  69. package/dist/cell-render.test.d.ts +0 -1
  70. package/dist/cell-render.test.js +0 -338
  71. package/dist/chart-export.test.d.ts +0 -1
  72. package/dist/chart-export.test.js +0 -302
  73. package/dist/chart.coverage.test.d.ts +0 -1
  74. package/dist/chart.coverage.test.js +0 -748
  75. package/dist/chart.test.d.ts +0 -1
  76. package/dist/chart.test.js +0 -289
  77. package/dist/clipboard.test.d.ts +0 -1
  78. package/dist/clipboard.test.js +0 -700
  79. package/dist/collaboration.coverage.test.d.ts +0 -1
  80. package/dist/collaboration.coverage.test.js +0 -200
  81. package/dist/collaboration.test.d.ts +0 -1
  82. package/dist/collaboration.test.js +0 -65
  83. package/dist/column-groups.test.d.ts +0 -1
  84. package/dist/column-groups.test.js +0 -56
  85. package/dist/column-types.test.d.ts +0 -1
  86. package/dist/column-types.test.js +0 -62
  87. package/dist/columns.test.d.ts +0 -1
  88. package/dist/columns.test.js +0 -625
  89. package/dist/conditional-formatting.test.d.ts +0 -1
  90. package/dist/conditional-formatting.test.js +0 -87
  91. package/dist/core.coverage.test.d.ts +0 -1
  92. package/dist/core.coverage.test.js +0 -186
  93. package/dist/core.performance.test.d.ts +0 -1
  94. package/dist/core.performance.test.js +0 -29
  95. package/dist/createGrid.test.d.ts +0 -1
  96. package/dist/createGrid.test.js +0 -9
  97. package/dist/editing.test.d.ts +0 -1
  98. package/dist/editing.test.js +0 -732
  99. package/dist/editors/cell-editors.coverage.test.d.ts +0 -1
  100. package/dist/editors/cell-editors.coverage.test.js +0 -139
  101. package/dist/editors/cell-editors.test.d.ts +0 -1
  102. package/dist/editors/cell-editors.test.js +0 -75
  103. package/dist/facet-buckets.test.d.ts +0 -1
  104. package/dist/facet-buckets.test.js +0 -296
  105. package/dist/fill-patterns.test.d.ts +0 -1
  106. package/dist/fill-patterns.test.js +0 -81
  107. package/dist/filter-operators.test.d.ts +0 -1
  108. package/dist/filter-operators.test.js +0 -135
  109. package/dist/filtering/excel-filters.test.d.ts +0 -1
  110. package/dist/filtering/excel-filters.test.js +0 -116
  111. package/dist/filtering/locale-filter.test.d.ts +0 -13
  112. package/dist/filtering/locale-filter.test.js +0 -189
  113. package/dist/flex-render.test.d.ts +0 -1
  114. package/dist/flex-render.test.js +0 -142
  115. package/dist/group-aggregate.test.d.ts +0 -1
  116. package/dist/group-aggregate.test.js +0 -32
  117. package/dist/hyperformula-adapter.test.d.ts +0 -1
  118. package/dist/hyperformula-adapter.test.js +0 -205
  119. package/dist/keyboard-handlers.coverage.test.d.ts +0 -1
  120. package/dist/keyboard-handlers.coverage.test.js +0 -495
  121. package/dist/keyboard.test.d.ts +0 -1
  122. package/dist/keyboard.test.js +0 -27
  123. package/dist/menus.test.d.ts +0 -1
  124. package/dist/menus.test.js +0 -560
  125. package/dist/named-views.coverage.test.d.ts +0 -1
  126. package/dist/named-views.coverage.test.js +0 -180
  127. package/dist/named-views.test.d.ts +0 -1
  128. package/dist/named-views.test.js +0 -57
  129. package/dist/new-features.test.d.ts +0 -1
  130. package/dist/new-features.test.js +0 -183
  131. package/dist/render-component.test.d.ts +0 -1
  132. package/dist/render-component.test.js +0 -41
  133. package/dist/row-drag.test.d.ts +0 -1
  134. package/dist/row-drag.test.js +0 -142
  135. package/dist/row-resize.test.d.ts +0 -1
  136. package/dist/row-resize.test.js +0 -329
  137. package/dist/scroll-sync.test.d.ts +0 -1
  138. package/dist/scroll-sync.test.js +0 -290
  139. package/dist/selection.multi-range.test.d.ts +0 -1
  140. package/dist/selection.multi-range.test.js +0 -55
  141. package/dist/selection.test.d.ts +0 -1
  142. package/dist/selection.test.js +0 -647
  143. package/dist/server-data-source.coverage.test.d.ts +0 -1
  144. package/dist/server-data-source.coverage.test.js +0 -154
  145. package/dist/server-data-source.test.d.ts +0 -1
  146. package/dist/server-data-source.test.js +0 -81
  147. package/dist/sparkline.test.d.ts +0 -1
  148. package/dist/sparkline.test.js +0 -52
  149. package/dist/spreadsheet.test.d.ts +0 -1
  150. package/dist/spreadsheet.test.js +0 -446
  151. package/dist/subscribe.test.d.ts +0 -1
  152. package/dist/subscribe.test.js +0 -93
  153. package/dist/svgrid.api-extensions.test.d.ts +0 -14
  154. package/dist/svgrid.api-extensions.test.js +0 -334
  155. package/dist/svgrid.api.test.d.ts +0 -15
  156. package/dist/svgrid.api.test.js +0 -169
  157. package/dist/svgrid.behavior.test.d.ts +0 -15
  158. package/dist/svgrid.behavior.test.js +0 -625
  159. package/dist/svgrid.column-reorder.test.d.ts +0 -15
  160. package/dist/svgrid.column-reorder.test.js +0 -224
  161. package/dist/svgrid.comments-autocomplete.test.d.ts +0 -1
  162. package/dist/svgrid.comments-autocomplete.test.js +0 -96
  163. package/dist/svgrid.context-menu.test.d.ts +0 -1
  164. package/dist/svgrid.context-menu.test.js +0 -102
  165. package/dist/svgrid.features.test.d.ts +0 -1
  166. package/dist/svgrid.features.test.js +0 -129
  167. package/dist/svgrid.interaction.test.d.ts +0 -9
  168. package/dist/svgrid.interaction.test.js +0 -349
  169. package/dist/svgrid.locale-filtering.test.d.ts +0 -15
  170. package/dist/svgrid.locale-filtering.test.js +0 -261
  171. package/dist/svgrid.new-features.wrapper.test.d.ts +0 -1
  172. package/dist/svgrid.new-features.wrapper.test.js +0 -194
  173. package/dist/svgrid.row-pinning.test.d.ts +0 -14
  174. package/dist/svgrid.row-pinning.test.js +0 -250
  175. package/dist/svgrid.set-filter.test.d.ts +0 -17
  176. package/dist/svgrid.set-filter.test.js +0 -268
  177. package/dist/svgrid.wrapper.test.d.ts +0 -1
  178. package/dist/svgrid.wrapper.test.js +0 -59
  179. package/dist/test-setup.d.ts +0 -1
  180. package/dist/test-setup.js +0 -35
  181. package/dist/transaction.test.d.ts +0 -1
  182. package/dist/transaction.test.js +0 -89
  183. package/dist/virtualization/column-virtualizer.test.d.ts +0 -1
  184. package/dist/virtualization/column-virtualizer.test.js +0 -25
  185. package/dist/virtualization/scroll-scaling.test.d.ts +0 -1
  186. package/dist/virtualization/scroll-scaling.test.js +0 -127
  187. package/dist/virtualization/virtualizer.test.d.ts +0 -1
  188. package/dist/virtualization/virtualizer.test.js +0 -41
@@ -1,200 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { broadcastChannelTransport, createCollaboration, } from './collaboration';
3
- const userA = { id: 'a', name: 'Ada', color: '#f00' };
4
- const userB = { id: 'b', name: 'Bob', color: '#00f' };
5
- /** A controllable transport whose subscriber we can drive by hand. */
6
- function manualTransport() {
7
- const handlers = new Set();
8
- const posted = [];
9
- const transport = {
10
- post: (msg) => {
11
- posted.push(msg);
12
- },
13
- subscribe: (h) => {
14
- handlers.add(h);
15
- return () => handlers.delete(h);
16
- },
17
- };
18
- const deliver = (msg) => {
19
- for (const h of [...handlers])
20
- h(msg);
21
- };
22
- return { transport, posted, deliver, handlers };
23
- }
24
- describe('createCollaboration - presence / edit upserts', () => {
25
- it('hello reply carries the responder own cell', () => {
26
- const t = manualTransport();
27
- const a = createCollaboration({ user: userA, transport: t.transport });
28
- a.setCell({ rowId: 'r0', columnId: 'c0' });
29
- t.posted.length = 0;
30
- // a newcomer says hello -> A replies presence with its own cell.
31
- t.deliver({ kind: 'hello', user: userB });
32
- const reply = t.posted.find((m) => m.kind === 'presence');
33
- expect(reply).toEqual({ kind: 'presence', user: userA, cell: { rowId: 'r0', columnId: 'c0' } });
34
- expect(a.peers().map((p) => p.id)).toEqual(['b']);
35
- a.dispose();
36
- });
37
- it('presence message records the peer cursor', () => {
38
- const t = manualTransport();
39
- const onPeersChange = vi.fn();
40
- const a = createCollaboration({ user: userA, transport: t.transport, onPeersChange });
41
- t.deliver({ kind: 'presence', user: userB, cell: { rowId: 'r9', columnId: 'name' } });
42
- expect(a.peers()[0]?.cell).toEqual({ rowId: 'r9', columnId: 'name' });
43
- expect(onPeersChange).toHaveBeenCalled();
44
- a.dispose();
45
- });
46
- it('edit from another peer sets a cursor AND fires onRemoteEdit', () => {
47
- const t = manualTransport();
48
- const onRemoteEdit = vi.fn();
49
- const a = createCollaboration({ user: userA, transport: t.transport, onRemoteEdit });
50
- t.deliver({ kind: 'edit', user: userB, rowId: 'r1', columnId: 'age', value: 30 });
51
- expect(a.peers()[0]?.cell).toEqual({ rowId: 'r1', columnId: 'age' });
52
- expect(onRemoteEdit).toHaveBeenCalledWith({
53
- rowId: 'r1',
54
- columnId: 'age',
55
- value: 30,
56
- user: userB,
57
- });
58
- a.dispose();
59
- });
60
- it('ignores own id on upsert (presence/edit echoed back)', () => {
61
- const t = manualTransport();
62
- const onRemoteEdit = vi.fn();
63
- const a = createCollaboration({ user: userA, transport: t.transport, onRemoteEdit });
64
- // self-echo: should not appear as a peer and should not call onRemoteEdit
65
- t.deliver({ kind: 'presence', user: userA, cell: { rowId: 'x', columnId: 'y' } });
66
- t.deliver({ kind: 'edit', user: userA, rowId: 'x', columnId: 'y', value: 1 });
67
- expect(a.peers()).toHaveLength(0);
68
- expect(onRemoteEdit).not.toHaveBeenCalled();
69
- a.dispose();
70
- });
71
- it('bye for an unknown peer does not notify', () => {
72
- const t = manualTransport();
73
- const onPeersChange = vi.fn();
74
- const a = createCollaboration({ user: userA, transport: t.transport, onPeersChange });
75
- onPeersChange.mockClear();
76
- t.deliver({ kind: 'bye', userId: 'ghost' });
77
- expect(onPeersChange).not.toHaveBeenCalled();
78
- a.dispose();
79
- });
80
- it('setCell broadcasts a presence with the new cell', () => {
81
- const t = manualTransport();
82
- const a = createCollaboration({ user: userA, transport: t.transport });
83
- t.posted.length = 0;
84
- a.setCell(null);
85
- expect(t.posted).toContainEqual({ kind: 'presence', user: userA, cell: null });
86
- a.dispose();
87
- });
88
- it('sendEdit broadcasts an edit message', () => {
89
- const t = manualTransport();
90
- const a = createCollaboration({ user: userA, transport: t.transport });
91
- t.posted.length = 0;
92
- a.sendEdit('r5', 'col', 'v');
93
- expect(t.posted).toContainEqual({
94
- kind: 'edit',
95
- user: userA,
96
- rowId: 'r5',
97
- columnId: 'col',
98
- value: 'v',
99
- });
100
- a.dispose();
101
- });
102
- it('drops messages after dispose and unsubscribes', () => {
103
- const t = manualTransport();
104
- const onPeersChange = vi.fn();
105
- const a = createCollaboration({ user: userA, transport: t.transport, onPeersChange });
106
- a.dispose();
107
- onPeersChange.mockClear();
108
- // After dispose the handler is unsubscribed; even if delivered it no-ops.
109
- t.deliver({ kind: 'hello', user: userB });
110
- expect(onPeersChange).not.toHaveBeenCalled();
111
- expect(a.peers()).toHaveLength(0);
112
- // second dispose is a no-op
113
- a.dispose();
114
- });
115
- });
116
- describe('createCollaboration - prune timer', () => {
117
- beforeEach(() => vi.useFakeTimers());
118
- afterEach(() => vi.useRealTimers());
119
- it('prunes peers gone silent past the timeout', () => {
120
- const t = manualTransport();
121
- const onPeersChange = vi.fn();
122
- const a = createCollaboration({
123
- user: userA,
124
- transport: t.transport,
125
- onPeersChange,
126
- peerTimeoutMs: 3000,
127
- });
128
- t.deliver({ kind: 'presence', user: userB, cell: null });
129
- expect(a.peers()).toHaveLength(1);
130
- onPeersChange.mockClear();
131
- // advance past the timeout; prune interval is max(1000, timeout/3) = 1000
132
- vi.advanceTimersByTime(4000);
133
- expect(a.peers()).toHaveLength(0);
134
- expect(onPeersChange).toHaveBeenCalled();
135
- a.dispose();
136
- });
137
- it('prune tick with no expired peers does not notify', () => {
138
- const t = manualTransport();
139
- const onPeersChange = vi.fn();
140
- const a = createCollaboration({
141
- user: userA,
142
- transport: t.transport,
143
- onPeersChange,
144
- peerTimeoutMs: 9000,
145
- });
146
- t.deliver({ kind: 'presence', user: userB, cell: null });
147
- onPeersChange.mockClear();
148
- vi.advanceTimersByTime(3000); // one tick, peer still fresh
149
- expect(onPeersChange).not.toHaveBeenCalled();
150
- expect(a.peers()).toHaveLength(1);
151
- a.dispose();
152
- });
153
- it('dispose clears the prune timer', () => {
154
- const clearSpy = vi.spyOn(globalThis, 'clearInterval');
155
- const t = manualTransport();
156
- const a = createCollaboration({ user: userA, transport: t.transport });
157
- a.dispose();
158
- expect(clearSpy).toHaveBeenCalled();
159
- clearSpy.mockRestore();
160
- });
161
- });
162
- describe('broadcastChannelTransport', () => {
163
- it('round-trips a message across two channels of the same name', async () => {
164
- const tx = broadcastChannelTransport('svgrid-test-chan');
165
- const rx = broadcastChannelTransport('svgrid-test-chan');
166
- const received = [];
167
- const off = rx.subscribe((m) => received.push(m));
168
- tx.post({ kind: 'hello', user: userA });
169
- // BroadcastChannel delivery is async (microtask/macrotask).
170
- await new Promise((r) => setTimeout(r, 10));
171
- expect(received).toContainEqual({ kind: 'hello', user: userA });
172
- off();
173
- });
174
- it('subscribe returns a working unsubscribe', async () => {
175
- const tx = broadcastChannelTransport('svgrid-unsub-chan');
176
- const rx = broadcastChannelTransport('svgrid-unsub-chan');
177
- const received = [];
178
- const off = rx.subscribe((m) => received.push(m));
179
- off();
180
- tx.post({ kind: 'bye', userId: 'a' });
181
- await new Promise((r) => setTimeout(r, 10));
182
- expect(received).toHaveLength(0);
183
- });
184
- it('no-ops gracefully when BroadcastChannel is unavailable', () => {
185
- const original = globalThis.BroadcastChannel;
186
- // @ts-expect-error - simulate SSR / old environment
187
- delete globalThis.BroadcastChannel;
188
- try {
189
- const tx = broadcastChannelTransport('nope');
190
- const handler = vi.fn();
191
- const off = tx.subscribe(handler);
192
- expect(() => tx.post({ kind: 'bye', userId: 'x' })).not.toThrow();
193
- expect(() => off()).not.toThrow();
194
- expect(handler).not.toHaveBeenCalled();
195
- }
196
- finally {
197
- globalThis.BroadcastChannel = original;
198
- }
199
- });
200
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,65 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { createCollaboration, } from './collaboration';
3
- /** An in-memory bus so two controllers can talk in a test. */
4
- function makeBus() {
5
- const handlers = new Set();
6
- const transportFor = () => ({
7
- post: (msg) => {
8
- // deliver async-ish to every subscriber except none (real channels echo
9
- // to other tabs, not self; our controllers ignore their own id anyway).
10
- for (const h of [...handlers])
11
- h(msg);
12
- },
13
- subscribe: (h) => {
14
- handlers.add(h);
15
- return () => handlers.delete(h);
16
- },
17
- });
18
- return { transportFor };
19
- }
20
- const userA = { id: 'a', name: 'Ada', color: '#f00' };
21
- const userB = { id: 'b', name: 'Bob', color: '#00f' };
22
- describe('createCollaboration', () => {
23
- it('peers see each other after hello', () => {
24
- const bus = makeBus();
25
- const a = createCollaboration({ user: userA, transport: bus.transportFor() });
26
- const b = createCollaboration({ user: userB, transport: bus.transportFor() });
27
- // A is announced; B's hello reaches A; A replies presence.
28
- expect(a.peers().map((p) => p.id)).toEqual(['b']);
29
- expect(b.peers().map((p) => p.id)).toEqual(['a']);
30
- a.dispose();
31
- b.dispose();
32
- });
33
- it('setCell propagates a remote cursor', () => {
34
- const bus = makeBus();
35
- const onPeers = vi.fn();
36
- const a = createCollaboration({ user: userA, transport: bus.transportFor(), onPeersChange: onPeers });
37
- const b = createCollaboration({ user: userB, transport: bus.transportFor() });
38
- b.setCell({ rowId: 'r1', columnId: 'salary' });
39
- const peerB = a.peers().find((p) => p.id === 'b');
40
- expect(peerB?.cell).toEqual({ rowId: 'r1', columnId: 'salary' });
41
- a.dispose();
42
- b.dispose();
43
- });
44
- it('sendEdit fires onRemoteEdit on the other peer only', () => {
45
- const bus = makeBus();
46
- const aEdits = vi.fn();
47
- const bEdits = vi.fn();
48
- const a = createCollaboration({ user: userA, transport: bus.transportFor(), onRemoteEdit: aEdits });
49
- const b = createCollaboration({ user: userB, transport: bus.transportFor(), onRemoteEdit: bEdits });
50
- a.sendEdit('r2', 'name', 'Grace');
51
- expect(bEdits).toHaveBeenCalledWith(expect.objectContaining({ rowId: 'r2', columnId: 'name', value: 'Grace' }));
52
- expect(aEdits).not.toHaveBeenCalled(); // not echoed to self
53
- a.dispose();
54
- b.dispose();
55
- });
56
- it('bye removes a peer', () => {
57
- const bus = makeBus();
58
- const a = createCollaboration({ user: userA, transport: bus.transportFor() });
59
- const b = createCollaboration({ user: userB, transport: bus.transportFor() });
60
- expect(a.peers()).toHaveLength(1);
61
- b.dispose();
62
- expect(a.peers()).toHaveLength(0);
63
- a.dispose();
64
- });
65
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,56 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { computeColumnGroupMeta, hiddenLeavesForCollapse } from './column-groups';
3
- // A group "Q1" with an always-on Total plus Jan/Feb that only show when open.
4
- const columns = [
5
- { field: 'name' },
6
- {
7
- id: 'q1',
8
- header: 'Q1',
9
- columns: [
10
- { field: 'q1Total' },
11
- { field: 'jan', columnGroupShow: 'open' },
12
- { field: 'feb', columnGroupShow: 'open' },
13
- ],
14
- },
15
- {
16
- id: 'q2',
17
- header: 'Q2',
18
- openByDefault: true,
19
- columns: [
20
- { field: 'q2Total', columnGroupShow: 'closed' },
21
- { field: 'apr', columnGroupShow: 'open' },
22
- ],
23
- },
24
- ];
25
- describe('computeColumnGroupMeta', () => {
26
- it('marks groups with columnGroupShow children as collapsible', () => {
27
- const meta = computeColumnGroupMeta(columns);
28
- expect([...meta.collapsibleGroupIds].sort()).toEqual(['q1', 'q2']);
29
- expect(meta.defaultOpen.get('q1')).toBe(false);
30
- expect(meta.defaultOpen.get('q2')).toBe(true);
31
- });
32
- it('maps controlled leaves to their group + show mode', () => {
33
- const meta = computeColumnGroupMeta(columns);
34
- expect(meta.leafControl.get('jan')).toEqual({ groupId: 'q1', show: 'open' });
35
- expect(meta.leafControl.get('q2Total')).toEqual({ groupId: 'q2', show: 'closed' });
36
- // untagged always-on column is not controlled
37
- expect(meta.leafControl.has('q1Total')).toBe(false);
38
- expect(meta.leafControl.has('name')).toBe(false);
39
- });
40
- });
41
- describe('hiddenLeavesForCollapse', () => {
42
- const meta = computeColumnGroupMeta(columns);
43
- it('hides open-columns while collapsed, shows them while expanded', () => {
44
- const collapsed = new Set(['q1']);
45
- const hidden = hiddenLeavesForCollapse(meta, collapsed);
46
- expect(hidden.jan).toBe(true);
47
- expect(hidden.feb).toBe(true);
48
- expect(hidden.q1Total).toBeUndefined(); // always on
49
- });
50
- it('hides closed-columns while expanded', () => {
51
- const collapsed = new Set(); // q2 expanded
52
- const hidden = hiddenLeavesForCollapse(meta, collapsed);
53
- expect(hidden.q2Total).toBe(true); // 'closed' hides when expanded
54
- expect(hidden.apr).toBeUndefined(); // 'open' shows when expanded
55
- });
56
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,62 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { inferCellDataType, resolveColumnTypes } from './column-types';
3
- describe('inferCellDataType', () => {
4
- it('classifies primitives', () => {
5
- expect(inferCellDataType(42)).toBe('number');
6
- expect(inferCellDataType(true)).toBe('boolean');
7
- expect(inferCellDataType(new Date())).toBe('date');
8
- expect(inferCellDataType('2026-06-27')).toBe('dateString');
9
- expect(inferCellDataType('2026-06-27T10:00:00Z')).toBe('dateString');
10
- expect(inferCellDataType('hello')).toBe('text');
11
- });
12
- it('returns undefined for null/undefined so nothing is forced', () => {
13
- expect(inferCellDataType(null)).toBeUndefined();
14
- expect(inferCellDataType(undefined)).toBeUndefined();
15
- });
16
- });
17
- describe('resolveColumnTypes', () => {
18
- it('maps explicit cellDataType to editorType + format', () => {
19
- const cols = [
20
- { field: 'name', cellDataType: 'text' },
21
- { field: 'age', cellDataType: 'number' },
22
- { field: 'active', cellDataType: 'boolean' },
23
- { field: 'joined', cellDataType: 'date' },
24
- { field: 'iso', cellDataType: 'dateString' },
25
- ];
26
- const out = resolveColumnTypes(cols, undefined, false);
27
- expect(out[0].editorType).toBe('text');
28
- expect(out[1].editorType).toBe('number');
29
- expect(out[2].editorType).toBe('checkbox');
30
- expect(out[3].editorType).toBe('date');
31
- expect(out[3].format).toEqual({ type: 'date' });
32
- expect(out[4].editorType).toBe('date');
33
- expect(out[4].format).toBeUndefined();
34
- });
35
- it('never overrides an explicit editorType or format', () => {
36
- const cols = [{ field: 'age', cellDataType: 'number', editorType: 'text', format: { type: 'currency' } }];
37
- const out = resolveColumnTypes(cols, undefined, false);
38
- expect(out[0].editorType).toBe('text');
39
- expect(out[0].format).toEqual({ type: 'currency' });
40
- });
41
- it('infers from a sample row only when infer=true and nothing is declared', () => {
42
- const cols = [
43
- { field: 'age' },
44
- { field: 'name' },
45
- { field: 'when' },
46
- { field: 'flag' },
47
- ];
48
- const sample = { age: 30, name: 'Ada', when: '2026-01-02', flag: false };
49
- const off = resolveColumnTypes(cols, sample, false);
50
- expect(off[0].editorType).toBeUndefined(); // inference off
51
- const on = resolveColumnTypes(cols, sample, true);
52
- expect(on[0].editorType).toBe('number');
53
- expect(on[1].editorType).toBe('text');
54
- expect(on[2].editorType).toBe('date');
55
- expect(on[3].editorType).toBe('checkbox');
56
- });
57
- it('recurses into header groups', () => {
58
- const cols = [{ header: 'Group', columns: [{ field: 'age', cellDataType: 'number' }] }];
59
- const out = resolveColumnTypes(cols, undefined, false);
60
- expect(out[0].columns[0].editorType).toBe('number');
61
- });
62
- });
@@ -1 +0,0 @@
1
- export {};