@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.
- package/ng-package.json +7 -0
- package/package.json +34 -45
- package/src/lib/__tests__/dashboard-component-widget-state-integration.spec.ts +537 -0
- package/src/lib/cell/__tests__/cell-resize.component.spec.ts +442 -0
- package/src/lib/cell/__tests__/cell.component.spec.ts +541 -0
- package/src/lib/cell/cell-context-menu.component.ts +138 -0
- package/src/lib/cell/cell-context-menu.service.ts +36 -0
- package/src/lib/cell/cell.component.html +37 -0
- package/src/lib/cell/cell.component.scss +198 -0
- package/src/lib/cell/cell.component.ts +375 -0
- package/src/lib/dashboard/dashboard.component.html +18 -0
- package/src/lib/dashboard/dashboard.component.scss +17 -0
- package/src/lib/dashboard/dashboard.component.ts +187 -0
- package/src/lib/dashboard-editor/dashboard-editor.component.html +57 -0
- package/src/lib/dashboard-editor/dashboard-editor.component.scss +87 -0
- package/src/lib/dashboard-editor/dashboard-editor.component.ts +219 -0
- package/src/lib/dashboard-viewer/__tests__/dashboard-viewer.component.spec.ts +258 -0
- package/src/lib/dashboard-viewer/dashboard-viewer.component.html +20 -0
- package/src/lib/dashboard-viewer/dashboard-viewer.component.scss +50 -0
- package/src/lib/dashboard-viewer/dashboard-viewer.component.ts +70 -0
- package/src/lib/drop-zone/__tests__/drop-zone.component.spec.ts +465 -0
- package/src/lib/drop-zone/drop-zone.component.html +20 -0
- package/src/lib/drop-zone/drop-zone.component.scss +67 -0
- package/src/lib/drop-zone/drop-zone.component.ts +122 -0
- package/src/lib/internal-widgets/unknown-widget/unknown-widget.component.ts +72 -0
- package/src/lib/models/cell-data.ts +13 -0
- package/src/lib/models/cell-dialog.ts +7 -0
- package/src/lib/models/cell-id.ts +85 -0
- package/src/lib/models/cell-position.ts +15 -0
- package/src/lib/models/dashboard-data.dto.ts +44 -0
- package/src/lib/models/dashboard-data.utils.ts +49 -0
- package/src/lib/models/drag-data.ts +6 -0
- package/src/lib/models/index.ts +11 -0
- package/src/lib/models/reserved-space.ts +24 -0
- package/src/lib/models/widget-factory.ts +33 -0
- package/src/lib/models/widget-id.ts +70 -0
- package/src/lib/models/widget.ts +21 -0
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.component.ts +127 -0
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.provider.ts +15 -0
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.tokens.ts +20 -0
- package/src/lib/providers/cell-settings-dialog/default-cell-settings-dialog.provider.ts +32 -0
- package/src/lib/providers/cell-settings-dialog/index.ts +3 -0
- package/src/lib/providers/index.ts +1 -0
- package/src/lib/services/__tests__/dashboard-bridge.service.spec.ts +220 -0
- package/src/lib/services/__tests__/dashboard-viewport.service.spec.ts +362 -0
- package/src/lib/services/dashboard-bridge.service.ts +155 -0
- package/src/lib/services/dashboard-viewport.service.ts +148 -0
- package/src/lib/services/dashboard.service.ts +62 -0
- package/src/lib/store/__tests__/dashboard-store-collision-detection.spec.ts +756 -0
- package/src/lib/store/__tests__/dashboard-store-computed-properties.spec.ts +974 -0
- package/src/lib/store/__tests__/dashboard-store-drag-drop.spec.ts +279 -0
- package/src/lib/store/__tests__/dashboard-store-export-import.spec.ts +780 -0
- package/src/lib/store/__tests__/dashboard-store-grid-config.spec.ts +128 -0
- package/src/lib/store/__tests__/dashboard-store-query-methods.spec.ts +229 -0
- package/src/lib/store/__tests__/dashboard-store-resize-operations.spec.ts +652 -0
- package/src/lib/store/__tests__/dashboard-store-widget-management.spec.ts +461 -0
- package/src/lib/store/__tests__/dashboard-store-widget-state-preservation.spec.ts +369 -0
- package/src/lib/store/dashboard-store.ts +239 -0
- package/src/lib/store/features/drag-drop.feature.ts +140 -0
- package/src/lib/store/features/grid-config.feature.ts +43 -0
- package/src/lib/store/features/resize.feature.ts +140 -0
- package/src/lib/store/features/utils/collision.utils.ts +89 -0
- package/src/lib/store/features/utils/grid-query-internal.utils.ts +37 -0
- package/src/lib/store/features/utils/resize.utils.ts +165 -0
- package/src/lib/store/features/widget-management.feature.ts +158 -0
- package/src/lib/styles/_dashboard-grid-vars.scss +11 -0
- package/src/lib/widget-list/__tests__/widget-list-bridge-integration.spec.ts +137 -0
- package/src/lib/widget-list/widget-list.component.html +22 -0
- package/src/lib/widget-list/widget-list.component.scss +154 -0
- package/src/lib/widget-list/widget-list.component.ts +106 -0
- package/src/public-api.ts +21 -0
- package/src/test-setup.ts +10 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +14 -0
- package/fesm2022/dragonworks-ngx-dashboard.mjs +0 -2192
- package/fesm2022/dragonworks-ngx-dashboard.mjs.map +0 -1
- 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
|
+
});
|