@dragonworks/ngx-dashboard 20.0.4 → 20.0.6

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 (78) hide show
  1. package/ng-package.json +7 -0
  2. package/package.json +34 -45
  3. package/src/lib/__tests__/dashboard-component-widget-state-integration.spec.ts +537 -0
  4. package/src/lib/cell/__tests__/cell-resize.component.spec.ts +442 -0
  5. package/src/lib/cell/__tests__/cell.component.spec.ts +541 -0
  6. package/src/lib/cell/cell-context-menu.component.ts +138 -0
  7. package/src/lib/cell/cell-context-menu.service.ts +36 -0
  8. package/src/lib/cell/cell.component.html +37 -0
  9. package/src/lib/cell/cell.component.scss +198 -0
  10. package/src/lib/cell/cell.component.ts +375 -0
  11. package/src/lib/dashboard/dashboard.component.html +18 -0
  12. package/src/lib/dashboard/dashboard.component.scss +17 -0
  13. package/src/lib/dashboard/dashboard.component.ts +187 -0
  14. package/src/lib/dashboard-editor/dashboard-editor.component.html +57 -0
  15. package/src/lib/dashboard-editor/dashboard-editor.component.scss +87 -0
  16. package/src/lib/dashboard-editor/dashboard-editor.component.ts +219 -0
  17. package/src/lib/dashboard-viewer/__tests__/dashboard-viewer.component.spec.ts +258 -0
  18. package/src/lib/dashboard-viewer/dashboard-viewer.component.html +20 -0
  19. package/src/lib/dashboard-viewer/dashboard-viewer.component.scss +50 -0
  20. package/src/lib/dashboard-viewer/dashboard-viewer.component.ts +70 -0
  21. package/src/lib/drop-zone/__tests__/drop-zone.component.spec.ts +465 -0
  22. package/src/lib/drop-zone/drop-zone.component.html +20 -0
  23. package/src/lib/drop-zone/drop-zone.component.scss +67 -0
  24. package/src/lib/drop-zone/drop-zone.component.ts +122 -0
  25. package/src/lib/internal-widgets/unknown-widget/unknown-widget.component.ts +72 -0
  26. package/src/lib/models/cell-data.ts +13 -0
  27. package/src/lib/models/cell-dialog.ts +7 -0
  28. package/src/lib/models/cell-id.ts +85 -0
  29. package/src/lib/models/cell-position.ts +15 -0
  30. package/src/lib/models/dashboard-data.dto.ts +44 -0
  31. package/src/lib/models/dashboard-data.utils.ts +49 -0
  32. package/src/lib/models/drag-data.ts +6 -0
  33. package/src/lib/models/index.ts +11 -0
  34. package/src/lib/models/reserved-space.ts +24 -0
  35. package/src/lib/models/widget-factory.ts +33 -0
  36. package/src/lib/models/widget-id.ts +70 -0
  37. package/src/lib/models/widget.ts +21 -0
  38. package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.component.ts +127 -0
  39. package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.provider.ts +15 -0
  40. package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.tokens.ts +20 -0
  41. package/src/lib/providers/cell-settings-dialog/default-cell-settings-dialog.provider.ts +32 -0
  42. package/src/lib/providers/cell-settings-dialog/index.ts +3 -0
  43. package/src/lib/providers/index.ts +1 -0
  44. package/src/lib/services/__tests__/dashboard-bridge.service.spec.ts +220 -0
  45. package/src/lib/services/__tests__/dashboard-viewport.service.spec.ts +362 -0
  46. package/src/lib/services/dashboard-bridge.service.ts +155 -0
  47. package/src/lib/services/dashboard-viewport.service.ts +148 -0
  48. package/src/lib/services/dashboard.service.ts +62 -0
  49. package/src/lib/store/__tests__/dashboard-store-collision-detection.spec.ts +756 -0
  50. package/src/lib/store/__tests__/dashboard-store-computed-properties.spec.ts +974 -0
  51. package/src/lib/store/__tests__/dashboard-store-drag-drop.spec.ts +279 -0
  52. package/src/lib/store/__tests__/dashboard-store-export-import.spec.ts +780 -0
  53. package/src/lib/store/__tests__/dashboard-store-grid-config.spec.ts +128 -0
  54. package/src/lib/store/__tests__/dashboard-store-query-methods.spec.ts +229 -0
  55. package/src/lib/store/__tests__/dashboard-store-resize-operations.spec.ts +652 -0
  56. package/src/lib/store/__tests__/dashboard-store-widget-management.spec.ts +461 -0
  57. package/src/lib/store/__tests__/dashboard-store-widget-state-preservation.spec.ts +369 -0
  58. package/src/lib/store/dashboard-store.ts +239 -0
  59. package/src/lib/store/features/drag-drop.feature.ts +140 -0
  60. package/src/lib/store/features/grid-config.feature.ts +43 -0
  61. package/src/lib/store/features/resize.feature.ts +140 -0
  62. package/src/lib/store/features/utils/collision.utils.ts +89 -0
  63. package/src/lib/store/features/utils/grid-query-internal.utils.ts +37 -0
  64. package/src/lib/store/features/utils/resize.utils.ts +165 -0
  65. package/src/lib/store/features/widget-management.feature.ts +158 -0
  66. package/src/lib/styles/_dashboard-grid-vars.scss +11 -0
  67. package/src/lib/widget-list/__tests__/widget-list-bridge-integration.spec.ts +137 -0
  68. package/src/lib/widget-list/widget-list.component.html +22 -0
  69. package/src/lib/widget-list/widget-list.component.scss +154 -0
  70. package/src/lib/widget-list/widget-list.component.ts +106 -0
  71. package/src/public-api.ts +21 -0
  72. package/src/test-setup.ts +10 -0
  73. package/tsconfig.lib.json +15 -0
  74. package/tsconfig.lib.prod.json +11 -0
  75. package/tsconfig.spec.json +14 -0
  76. package/fesm2022/dragonworks-ngx-dashboard.mjs +0 -2192
  77. package/fesm2022/dragonworks-ngx-dashboard.mjs.map +0 -1
  78. package/index.d.ts +0 -678
@@ -0,0 +1,652 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { DashboardService } from '../../services/dashboard.service';
3
+ import { DashboardStore } from '../dashboard-store';
4
+ import { CellIdUtils, WidgetIdUtils, CellData, WidgetFactory } from '../../models';
5
+ import { GridQueryInternalUtils } from '../features/utils/grid-query-internal.utils';
6
+
7
+ describe('DashboardStore - Resize Operations', () => {
8
+ let store: InstanceType<typeof DashboardStore>;
9
+ let mockWidgetFactory: WidgetFactory;
10
+ let mockDashboardService: jasmine.SpyObj<DashboardService>;
11
+
12
+ beforeEach(() => {
13
+ const dashboardServiceSpy = jasmine.createSpyObj('DashboardService', ['getFactory']);
14
+
15
+ TestBed.configureTestingModule({
16
+ providers: [
17
+ DashboardStore,
18
+ { provide: DashboardService, useValue: dashboardServiceSpy }
19
+ ]
20
+ });
21
+
22
+ store = TestBed.inject(DashboardStore);
23
+ mockDashboardService = TestBed.inject(DashboardService) as jasmine.SpyObj<DashboardService>;
24
+ store.setGridConfig({ rows: 16, columns: 16 });
25
+
26
+ mockWidgetFactory = {
27
+ widgetTypeid: 'test-widget',
28
+ createComponent: jasmine.createSpy()
29
+ } as any;
30
+ });
31
+
32
+ describe('startResize', () => {
33
+ it('should start resize for existing widget', () => {
34
+ const cellId = CellIdUtils.create(5, 5);
35
+ const cell: CellData = {
36
+ widgetId: WidgetIdUtils.generate(),
37
+ cellId,
38
+ row: 5,
39
+ col: 5,
40
+ rowSpan: 3,
41
+ colSpan: 2,
42
+ widgetFactory: mockWidgetFactory,
43
+ widgetState: {},
44
+ };
45
+ store.addWidget(cell);
46
+
47
+ store.startResize(cellId);
48
+
49
+ expect(store.resizeData()).toEqual({
50
+ cellId,
51
+ originalRowSpan: 3,
52
+ originalColSpan: 2,
53
+ previewRowSpan: 3,
54
+ previewColSpan: 2,
55
+ });
56
+ });
57
+
58
+ it('should handle resize for single cell widget', () => {
59
+ const cellId = CellIdUtils.create(8, 8);
60
+ const cell: CellData = {
61
+ widgetId: WidgetIdUtils.generate(),
62
+ cellId,
63
+ row: 8,
64
+ col: 8,
65
+ rowSpan: 1,
66
+ colSpan: 1,
67
+ widgetFactory: mockWidgetFactory,
68
+ widgetState: {},
69
+ };
70
+ store.addWidget(cell);
71
+
72
+ store.startResize(cellId);
73
+
74
+ expect(store.resizeData()).toEqual({
75
+ cellId,
76
+ originalRowSpan: 1,
77
+ originalColSpan: 1,
78
+ previewRowSpan: 1,
79
+ previewColSpan: 1,
80
+ });
81
+ });
82
+
83
+ it('should handle non-existent widget gracefully', () => {
84
+ const nonExistentCellId = CellIdUtils.create(10, 10);
85
+
86
+ store.startResize(nonExistentCellId);
87
+
88
+ expect(store.resizeData()).toBeNull();
89
+ });
90
+
91
+ it('should replace existing resize data when starting new resize', () => {
92
+ const cellId1 = CellIdUtils.create(3, 3);
93
+ const cellId2 = CellIdUtils.create(8, 8);
94
+
95
+ const cell1: CellData = {
96
+ widgetId: WidgetIdUtils.generate(),
97
+ cellId: cellId1,
98
+ row: 3,
99
+ col: 3,
100
+ rowSpan: 2,
101
+ colSpan: 2,
102
+ widgetFactory: mockWidgetFactory,
103
+ widgetState: {},
104
+ };
105
+
106
+ const cell2: CellData = {
107
+ widgetId: WidgetIdUtils.generate(),
108
+ cellId: cellId2,
109
+ row: 8,
110
+ col: 8,
111
+ rowSpan: 1,
112
+ colSpan: 3,
113
+ widgetFactory: mockWidgetFactory,
114
+ widgetState: {},
115
+ };
116
+
117
+ store.addWidget(cell1);
118
+ store.addWidget(cell2);
119
+
120
+ store.startResize(cellId1);
121
+ expect(store.resizeData()?.cellId).toEqual(cellId1);
122
+
123
+ store.startResize(cellId2);
124
+ expect(store.resizeData()?.cellId).toEqual(cellId2);
125
+ expect(store.resizeData()?.originalColSpan).toBe(3);
126
+ });
127
+
128
+ it('should handle widget with maximum spans', () => {
129
+ const cellId = CellIdUtils.create(1, 1);
130
+ const cell: CellData = {
131
+ widgetId: WidgetIdUtils.generate(),
132
+ cellId,
133
+ row: 1,
134
+ col: 1,
135
+ rowSpan: 16,
136
+ colSpan: 16,
137
+ widgetFactory: mockWidgetFactory,
138
+ widgetState: {},
139
+ };
140
+ store.addWidget(cell);
141
+
142
+ store.startResize(cellId);
143
+
144
+ expect(store.resizeData()).toEqual({
145
+ cellId,
146
+ originalRowSpan: 16,
147
+ originalColSpan: 16,
148
+ previewRowSpan: 16,
149
+ previewColSpan: 16,
150
+ });
151
+ });
152
+ });
153
+
154
+ describe('updateResizePreview', () => {
155
+ beforeEach(() => {
156
+ const cellId = CellIdUtils.create(5, 5);
157
+ const cell: CellData = {
158
+ widgetId: WidgetIdUtils.generate(),
159
+ cellId,
160
+ row: 5,
161
+ col: 5,
162
+ rowSpan: 2,
163
+ colSpan: 2,
164
+ widgetFactory: mockWidgetFactory,
165
+ widgetState: {},
166
+ };
167
+ store.addWidget(cell);
168
+ store.startResize(cellId);
169
+ });
170
+
171
+ describe('horizontal resizing', () => {
172
+ it('should increase column span by positive delta', () => {
173
+ store.updateResizePreview('horizontal', 2);
174
+
175
+ expect(store.resizeData()?.previewColSpan).toBe(4);
176
+ expect(store.resizeData()?.previewRowSpan).toBe(2); // unchanged
177
+ });
178
+
179
+ it('should decrease column span by negative delta', () => {
180
+ store.updateResizePreview('horizontal', -1);
181
+
182
+ expect(store.resizeData()?.previewColSpan).toBe(1);
183
+ expect(store.resizeData()?.previewRowSpan).toBe(2); // unchanged
184
+ });
185
+
186
+ it('should not allow column span below 1', () => {
187
+ store.updateResizePreview('horizontal', -5);
188
+
189
+ expect(store.resizeData()?.previewColSpan).toBe(1);
190
+ });
191
+
192
+ it('should respect grid boundaries', () => {
193
+ // Widget at (5,5) with original colSpan 2, grid has 16 columns
194
+ // So max possible colSpan is 16 - 5 + 1 = 12
195
+ store.updateResizePreview('horizontal', 20);
196
+
197
+ expect(store.resizeData()?.previewColSpan).toBe(12);
198
+ });
199
+ });
200
+
201
+ describe('vertical resizing', () => {
202
+ it('should increase row span by positive delta', () => {
203
+ store.updateResizePreview('vertical', 3);
204
+
205
+ expect(store.resizeData()?.previewRowSpan).toBe(5);
206
+ expect(store.resizeData()?.previewColSpan).toBe(2); // unchanged
207
+ });
208
+
209
+ it('should decrease row span by negative delta', () => {
210
+ store.updateResizePreview('vertical', -1);
211
+
212
+ expect(store.resizeData()?.previewRowSpan).toBe(1);
213
+ expect(store.resizeData()?.previewColSpan).toBe(2); // unchanged
214
+ });
215
+
216
+ it('should not allow row span below 1', () => {
217
+ store.updateResizePreview('vertical', -10);
218
+
219
+ expect(store.resizeData()?.previewRowSpan).toBe(1);
220
+ });
221
+
222
+ it('should respect grid boundaries', () => {
223
+ // Widget at (5,5) with original rowSpan 2, grid has 16 rows
224
+ // So max possible rowSpan is 16 - 5 + 1 = 12
225
+ store.updateResizePreview('vertical', 15);
226
+
227
+ expect(store.resizeData()?.previewRowSpan).toBe(12);
228
+ });
229
+ });
230
+
231
+ describe('collision detection during resize', () => {
232
+ beforeEach(() => {
233
+ // Add blocking widget at (5, 8) to (6, 9)
234
+ const blockingCell: CellData = {
235
+ widgetId: WidgetIdUtils.generate(),
236
+ cellId: CellIdUtils.create(5, 8),
237
+ row: 5,
238
+ col: 8,
239
+ rowSpan: 2,
240
+ colSpan: 2,
241
+ widgetFactory: mockWidgetFactory,
242
+ widgetState: {},
243
+ };
244
+ store.addWidget(blockingCell);
245
+ });
246
+
247
+ it('should be limited by horizontal collision', () => {
248
+ // Widget at (5,5) with colSpan 2, blocking widget at (5,8)
249
+ // Max possible expansion is to column 7 (colSpan = 3)
250
+ store.updateResizePreview('horizontal', 5);
251
+
252
+ expect(store.resizeData()?.previewColSpan).toBe(3);
253
+ });
254
+
255
+ it('should be limited by vertical collision', () => {
256
+ // Add blocking widget at (8, 5) to test vertical collision
257
+ const verticalBlockingCell: CellData = {
258
+ widgetId: WidgetIdUtils.generate(),
259
+ cellId: CellIdUtils.create(8, 5),
260
+ row: 8,
261
+ col: 5,
262
+ rowSpan: 2,
263
+ colSpan: 2,
264
+ widgetFactory: mockWidgetFactory,
265
+ widgetState: {},
266
+ };
267
+ store.addWidget(verticalBlockingCell);
268
+
269
+ // Widget at (5,5) with rowSpan 2, blocking widget at (8,5)
270
+ // Max possible expansion is to row 7 (rowSpan = 3)
271
+ store.updateResizePreview('vertical', 10);
272
+
273
+ expect(store.resizeData()?.previewRowSpan).toBe(3);
274
+ });
275
+ });
276
+
277
+ it('should handle no active resize data gracefully', () => {
278
+ store.endResize(false); // Clear resize data
279
+
280
+ store.updateResizePreview('horizontal', 2);
281
+
282
+ expect(store.resizeData()).toBeNull();
283
+ });
284
+
285
+ it('should handle multiple consecutive preview updates', () => {
286
+ store.updateResizePreview('horizontal', 1);
287
+ expect(store.resizeData()?.previewColSpan).toBe(3);
288
+
289
+ store.updateResizePreview('horizontal', 2);
290
+ expect(store.resizeData()?.previewColSpan).toBe(4);
291
+
292
+ store.updateResizePreview('vertical', 1);
293
+ expect(store.resizeData()?.previewRowSpan).toBe(3);
294
+ expect(store.resizeData()?.previewColSpan).toBe(4); // Should remain unchanged
295
+ });
296
+ });
297
+
298
+ describe('endResize', () => {
299
+ let cellId: ReturnType<typeof CellIdUtils.create>;
300
+
301
+ beforeEach(() => {
302
+ cellId = CellIdUtils.create(5, 5);
303
+ const cell: CellData = {
304
+ widgetId: WidgetIdUtils.generate(),
305
+ cellId,
306
+ row: 5,
307
+ col: 5,
308
+ rowSpan: 2,
309
+ colSpan: 2,
310
+ widgetFactory: mockWidgetFactory,
311
+ widgetState: {},
312
+ };
313
+ store.addWidget(cell);
314
+ store.startResize(cellId);
315
+ });
316
+
317
+ it('should apply resize changes when apply is true and preview differs', () => {
318
+ store.updateResizePreview('horizontal', 2);
319
+ store.updateResizePreview('vertical', 1);
320
+
321
+ store.endResize(true);
322
+
323
+ const updatedCell = GridQueryInternalUtils.getCellAt(store.cells(), 5, 5);
324
+ expect(updatedCell?.rowSpan).toBe(3);
325
+ expect(updatedCell?.colSpan).toBe(4);
326
+ expect(store.resizeData()).toBeNull();
327
+ });
328
+
329
+ it('should not apply changes when apply is false', () => {
330
+ store.updateResizePreview('horizontal', 3);
331
+ store.updateResizePreview('vertical', 2);
332
+
333
+ store.endResize(false);
334
+
335
+ const cell = GridQueryInternalUtils.getCellAt(store.cells(), 5, 5);
336
+ expect(cell?.rowSpan).toBe(2); // Original value
337
+ expect(cell?.colSpan).toBe(2); // Original value
338
+ expect(store.resizeData()).toBeNull();
339
+ });
340
+
341
+ it('should not apply changes when preview equals original', () => {
342
+ // Don't change anything in preview
343
+ store.endResize(true);
344
+
345
+ const cell = GridQueryInternalUtils.getCellAt(store.cells(), 5, 5);
346
+ expect(cell?.rowSpan).toBe(2);
347
+ expect(cell?.colSpan).toBe(2);
348
+ expect(store.resizeData()).toBeNull();
349
+ });
350
+
351
+ it('should handle partial changes (only one dimension changed)', () => {
352
+ store.updateResizePreview('horizontal', 1); // Only change colSpan
353
+
354
+ store.endResize(true);
355
+
356
+ const cell = GridQueryInternalUtils.getCellAt(store.cells(), 5, 5);
357
+ expect(cell?.rowSpan).toBe(2); // Unchanged
358
+ expect(cell?.colSpan).toBe(3); // Changed
359
+ expect(store.resizeData()).toBeNull();
360
+ });
361
+
362
+ it('should handle no active resize data gracefully', () => {
363
+ store.endResize(false); // Clear resize data first
364
+
365
+ store.endResize(true); // Try to end again
366
+
367
+ expect(store.resizeData()).toBeNull();
368
+ });
369
+
370
+ it('should clear resize data even when widget no longer exists', () => {
371
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!; store.removeWidget(widget.widgetId); // Remove the widget
372
+
373
+ store.endResize(true);
374
+
375
+ expect(store.resizeData()).toBeNull();
376
+ });
377
+
378
+ it('should handle apply with maximum size changes', () => {
379
+ // Try to resize to maximum grid size
380
+ store.updateResizePreview('horizontal', 20);
381
+ store.updateResizePreview('vertical', 20);
382
+
383
+ store.endResize(true);
384
+
385
+ const cell = GridQueryInternalUtils.getCellAt(store.cells(), 5, 5);
386
+ expect(cell?.rowSpan).toBe(12); // Max possible from position (5,5)
387
+ expect(cell?.colSpan).toBe(12); // Max possible from position (5,5)
388
+ expect(store.resizeData()).toBeNull();
389
+ });
390
+ });
391
+
392
+ describe('resizePreviewCells computed property', () => {
393
+ let cellId: ReturnType<typeof CellIdUtils.create>;
394
+
395
+ beforeEach(() => {
396
+ cellId = CellIdUtils.create(3, 4);
397
+ const cell: CellData = {
398
+ widgetId: WidgetIdUtils.generate(),
399
+ cellId,
400
+ row: 3,
401
+ col: 4,
402
+ rowSpan: 2,
403
+ colSpan: 3,
404
+ widgetFactory: mockWidgetFactory,
405
+ widgetState: {},
406
+ };
407
+ store.addWidget(cell);
408
+ });
409
+
410
+ it('should return empty array when no resize is active', () => {
411
+ expect(store.resizePreviewCells()).toEqual([]);
412
+ });
413
+
414
+ it('should return preview cells for current resize operation', () => {
415
+ store.startResize(cellId);
416
+ store.updateResizePreview('horizontal', 1);
417
+ store.updateResizePreview('vertical', 1);
418
+
419
+ const previewCells = store.resizePreviewCells();
420
+
421
+ // Should be 3x4 grid starting at (3,4)
422
+ expect(previewCells.length).toBe(12);
423
+ expect(previewCells).toContain(jasmine.objectContaining({ row: 3, col: 4 }));
424
+ expect(previewCells).toContain(jasmine.objectContaining({ row: 3, col: 7 })); // rightmost
425
+ expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 4 })); // bottommost
426
+ expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 7 })); // bottom-right corner
427
+ });
428
+
429
+ it('should update when resize preview changes', () => {
430
+ store.startResize(cellId);
431
+
432
+ // Initial preview
433
+ let previewCells = store.resizePreviewCells();
434
+ expect(previewCells.length).toBe(6); // 2x3
435
+
436
+ // Update preview
437
+ store.updateResizePreview('horizontal', 2);
438
+ previewCells = store.resizePreviewCells();
439
+ expect(previewCells.length).toBe(10); // 2x5
440
+
441
+ store.updateResizePreview('vertical', 1);
442
+ previewCells = store.resizePreviewCells();
443
+ expect(previewCells.length).toBe(15); // 3x5
444
+ });
445
+
446
+ it('should handle single cell preview', () => {
447
+ store.startResize(cellId);
448
+ store.updateResizePreview('horizontal', -2);
449
+ store.updateResizePreview('vertical', -1);
450
+
451
+ const previewCells = store.resizePreviewCells();
452
+
453
+ expect(previewCells.length).toBe(1);
454
+ expect(previewCells[0]).toEqual({ row: 3, col: 4 });
455
+ });
456
+
457
+ it('should return empty array when widget is removed during resize', () => {
458
+ store.startResize(cellId);
459
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!; store.removeWidget(widget.widgetId);
460
+
461
+ expect(store.resizePreviewCells()).toEqual([]);
462
+ });
463
+ });
464
+
465
+ describe('resizePreviewMap computed property', () => {
466
+ let cellId: ReturnType<typeof CellIdUtils.create>;
467
+
468
+ beforeEach(() => {
469
+ cellId = CellIdUtils.create(8, 8);
470
+ const cell: CellData = {
471
+ widgetId: WidgetIdUtils.generate(),
472
+ cellId,
473
+ row: 8,
474
+ col: 8,
475
+ rowSpan: 1,
476
+ colSpan: 1,
477
+ widgetFactory: mockWidgetFactory,
478
+ widgetState: {},
479
+ };
480
+ store.addWidget(cell);
481
+ });
482
+
483
+ it('should return empty set when no resize is active', () => {
484
+ expect(store.resizePreviewMap().size).toBe(0);
485
+ });
486
+
487
+ it('should contain cell IDs for all preview cells', () => {
488
+ store.startResize(cellId);
489
+ store.updateResizePreview('horizontal', 2);
490
+ store.updateResizePreview('vertical', 1);
491
+
492
+ const previewMap = store.resizePreviewMap();
493
+
494
+ expect(previewMap.size).toBe(6); // 2x3 grid
495
+ expect(previewMap.has(CellIdUtils.create(8, 8))).toBe(true);
496
+ expect(previewMap.has(CellIdUtils.create(8, 9))).toBe(true);
497
+ expect(previewMap.has(CellIdUtils.create(8, 10))).toBe(true);
498
+ expect(previewMap.has(CellIdUtils.create(9, 8))).toBe(true);
499
+ expect(previewMap.has(CellIdUtils.create(9, 9))).toBe(true);
500
+ expect(previewMap.has(CellIdUtils.create(9, 10))).toBe(true);
501
+ });
502
+
503
+ it('should update when preview cells change', () => {
504
+ store.startResize(cellId);
505
+
506
+ let previewMap = store.resizePreviewMap();
507
+ expect(previewMap.size).toBe(1);
508
+
509
+ store.updateResizePreview('horizontal', 1);
510
+ previewMap = store.resizePreviewMap();
511
+ expect(previewMap.size).toBe(2);
512
+
513
+ store.updateResizePreview('vertical', 1);
514
+ previewMap = store.resizePreviewMap();
515
+ expect(previewMap.size).toBe(4);
516
+ });
517
+
518
+ it('should efficiently check cell membership', () => {
519
+ store.startResize(cellId);
520
+ store.updateResizePreview('horizontal', 1);
521
+
522
+ const previewMap = store.resizePreviewMap();
523
+
524
+ expect(previewMap.has(CellIdUtils.create(8, 8))).toBe(true);
525
+ expect(previewMap.has(CellIdUtils.create(8, 9))).toBe(true);
526
+ expect(previewMap.has(CellIdUtils.create(8, 10))).toBe(false);
527
+ expect(previewMap.has(CellIdUtils.create(9, 8))).toBe(false);
528
+ });
529
+ });
530
+
531
+ describe('resize integration scenarios', () => {
532
+ it('should handle complete resize workflow', () => {
533
+ const cellId = CellIdUtils.create(2, 2);
534
+ const cell: CellData = {
535
+ widgetId: WidgetIdUtils.generate(),
536
+ cellId,
537
+ row: 2,
538
+ col: 2,
539
+ rowSpan: 1,
540
+ colSpan: 1,
541
+ widgetFactory: mockWidgetFactory,
542
+ widgetState: {},
543
+ };
544
+ store.addWidget(cell);
545
+
546
+ // Start resize
547
+ store.startResize(cellId);
548
+ expect(store.resizeData()).toBeTruthy();
549
+ expect(store.resizePreviewCells().length).toBe(1);
550
+
551
+ // Update preview multiple times
552
+ store.updateResizePreview('horizontal', 2);
553
+ expect(store.resizePreviewCells().length).toBe(3);
554
+
555
+ store.updateResizePreview('vertical', 1);
556
+ expect(store.resizePreviewCells().length).toBe(6);
557
+
558
+ // Apply changes
559
+ store.endResize(true);
560
+ expect(store.resizeData()).toBeNull();
561
+ expect(store.resizePreviewCells().length).toBe(0);
562
+
563
+ const updatedCell = GridQueryInternalUtils.getCellAt(store.cells(), 2, 2);
564
+ expect(updatedCell?.rowSpan).toBe(2);
565
+ expect(updatedCell?.colSpan).toBe(3);
566
+ });
567
+
568
+ it('should handle resize cancellation workflow', () => {
569
+ const cellId = CellIdUtils.create(10, 10);
570
+ const cell: CellData = {
571
+ widgetId: WidgetIdUtils.generate(),
572
+ cellId,
573
+ row: 10,
574
+ col: 10,
575
+ rowSpan: 3,
576
+ colSpan: 3,
577
+ widgetFactory: mockWidgetFactory,
578
+ widgetState: {},
579
+ };
580
+ store.addWidget(cell);
581
+
582
+ // Start resize and make changes
583
+ store.startResize(cellId);
584
+ store.updateResizePreview('horizontal', -1);
585
+ store.updateResizePreview('vertical', -2);
586
+
587
+ // Cancel changes
588
+ store.endResize(false);
589
+
590
+ const cell_after = GridQueryInternalUtils.getCellAt(store.cells(), 10, 10);
591
+ expect(cell_after?.rowSpan).toBe(3); // Original values preserved
592
+ expect(cell_after?.colSpan).toBe(3);
593
+ expect(store.resizeData()).toBeNull();
594
+ });
595
+
596
+ it('should handle resize with complex grid layout', () => {
597
+ // Create a complex layout with multiple widgets
598
+ const widgets = [
599
+ { cellId: CellIdUtils.create(1, 1), row: 1, col: 1, rowSpan: 2, colSpan: 2 },
600
+ { cellId: CellIdUtils.create(1, 4), row: 1, col: 4, rowSpan: 1, colSpan: 3 },
601
+ { cellId: CellIdUtils.create(3, 1), row: 3, col: 1, rowSpan: 1, colSpan: 6 },
602
+ { cellId: CellIdUtils.create(5, 5), row: 5, col: 5, rowSpan: 2, colSpan: 2 },
603
+ ];
604
+
605
+ widgets.forEach(w => {
606
+ const cell: CellData = {
607
+ widgetId: WidgetIdUtils.generate(),
608
+ cellId: w.cellId,
609
+ row: w.row,
610
+ col: w.col,
611
+ rowSpan: w.rowSpan,
612
+ colSpan: w.colSpan,
613
+ widgetFactory: mockWidgetFactory,
614
+ widgetState: {},
615
+ };
616
+ store.addWidget(cell);
617
+ });
618
+
619
+ // Try to resize widget at (1,1) - should be limited by other widgets
620
+ store.startResize(widgets[0].cellId);
621
+ store.updateResizePreview('horizontal', 5);
622
+ store.updateResizePreview('vertical', 5);
623
+
624
+ // Should be limited by the widget at (1,4) horizontally and (3,1) vertically
625
+ expect(store.resizeData()?.previewColSpan).toBe(3); // Limited by widget at (1,4)
626
+ expect(store.resizeData()?.previewRowSpan).toBe(2); // Limited by widget at (3,1)
627
+ });
628
+
629
+ it('should handle edge case: resize at grid boundaries', () => {
630
+ const cellId = CellIdUtils.create(16, 16);
631
+ const cell: CellData = {
632
+ widgetId: WidgetIdUtils.generate(),
633
+ cellId,
634
+ row: 16,
635
+ col: 16,
636
+ rowSpan: 1,
637
+ colSpan: 1,
638
+ widgetFactory: mockWidgetFactory,
639
+ widgetState: {},
640
+ };
641
+ store.addWidget(cell);
642
+
643
+ store.startResize(cellId);
644
+ store.updateResizePreview('horizontal', 5);
645
+ store.updateResizePreview('vertical', 5);
646
+
647
+ // Should be limited by grid boundaries
648
+ expect(store.resizeData()?.previewRowSpan).toBe(1);
649
+ expect(store.resizeData()?.previewColSpan).toBe(1);
650
+ });
651
+ });
652
+ });