@dragonworks/ngx-dashboard 20.0.6 → 20.1.0
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/fesm2022/dragonworks-ngx-dashboard.mjs +2250 -0
- package/fesm2022/dragonworks-ngx-dashboard.mjs.map +1 -0
- package/index.d.ts +727 -0
- package/package.json +45 -34
- package/ng-package.json +0 -7
- package/src/lib/__tests__/dashboard-component-widget-state-integration.spec.ts +0 -537
- package/src/lib/cell/__tests__/cell-resize.component.spec.ts +0 -442
- package/src/lib/cell/__tests__/cell.component.spec.ts +0 -541
- package/src/lib/cell/cell-context-menu.component.ts +0 -138
- package/src/lib/cell/cell-context-menu.service.ts +0 -36
- package/src/lib/cell/cell.component.html +0 -37
- package/src/lib/cell/cell.component.scss +0 -198
- package/src/lib/cell/cell.component.ts +0 -375
- package/src/lib/dashboard/dashboard.component.html +0 -18
- package/src/lib/dashboard/dashboard.component.scss +0 -17
- package/src/lib/dashboard/dashboard.component.ts +0 -187
- package/src/lib/dashboard-editor/dashboard-editor.component.html +0 -57
- package/src/lib/dashboard-editor/dashboard-editor.component.scss +0 -87
- package/src/lib/dashboard-editor/dashboard-editor.component.ts +0 -219
- package/src/lib/dashboard-viewer/__tests__/dashboard-viewer.component.spec.ts +0 -258
- package/src/lib/dashboard-viewer/dashboard-viewer.component.html +0 -20
- package/src/lib/dashboard-viewer/dashboard-viewer.component.scss +0 -50
- package/src/lib/dashboard-viewer/dashboard-viewer.component.ts +0 -70
- package/src/lib/drop-zone/__tests__/drop-zone.component.spec.ts +0 -465
- package/src/lib/drop-zone/drop-zone.component.html +0 -20
- package/src/lib/drop-zone/drop-zone.component.scss +0 -67
- package/src/lib/drop-zone/drop-zone.component.ts +0 -122
- package/src/lib/internal-widgets/unknown-widget/unknown-widget.component.ts +0 -72
- package/src/lib/models/cell-data.ts +0 -13
- package/src/lib/models/cell-dialog.ts +0 -7
- package/src/lib/models/cell-id.ts +0 -85
- package/src/lib/models/cell-position.ts +0 -15
- package/src/lib/models/dashboard-data.dto.ts +0 -44
- package/src/lib/models/dashboard-data.utils.ts +0 -49
- package/src/lib/models/drag-data.ts +0 -6
- package/src/lib/models/index.ts +0 -11
- package/src/lib/models/reserved-space.ts +0 -24
- package/src/lib/models/widget-factory.ts +0 -33
- package/src/lib/models/widget-id.ts +0 -70
- package/src/lib/models/widget.ts +0 -21
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.component.ts +0 -127
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.provider.ts +0 -15
- package/src/lib/providers/cell-settings-dialog/cell-settings-dialog.tokens.ts +0 -20
- package/src/lib/providers/cell-settings-dialog/default-cell-settings-dialog.provider.ts +0 -32
- package/src/lib/providers/cell-settings-dialog/index.ts +0 -3
- package/src/lib/providers/index.ts +0 -1
- package/src/lib/services/__tests__/dashboard-bridge.service.spec.ts +0 -220
- package/src/lib/services/__tests__/dashboard-viewport.service.spec.ts +0 -362
- package/src/lib/services/dashboard-bridge.service.ts +0 -155
- package/src/lib/services/dashboard-viewport.service.ts +0 -148
- package/src/lib/services/dashboard.service.ts +0 -62
- package/src/lib/store/__tests__/dashboard-store-collision-detection.spec.ts +0 -756
- package/src/lib/store/__tests__/dashboard-store-computed-properties.spec.ts +0 -974
- package/src/lib/store/__tests__/dashboard-store-drag-drop.spec.ts +0 -279
- package/src/lib/store/__tests__/dashboard-store-export-import.spec.ts +0 -780
- package/src/lib/store/__tests__/dashboard-store-grid-config.spec.ts +0 -128
- package/src/lib/store/__tests__/dashboard-store-query-methods.spec.ts +0 -229
- package/src/lib/store/__tests__/dashboard-store-resize-operations.spec.ts +0 -652
- package/src/lib/store/__tests__/dashboard-store-widget-management.spec.ts +0 -461
- package/src/lib/store/__tests__/dashboard-store-widget-state-preservation.spec.ts +0 -369
- package/src/lib/store/dashboard-store.ts +0 -239
- package/src/lib/store/features/drag-drop.feature.ts +0 -140
- package/src/lib/store/features/grid-config.feature.ts +0 -43
- package/src/lib/store/features/resize.feature.ts +0 -140
- package/src/lib/store/features/utils/collision.utils.ts +0 -89
- package/src/lib/store/features/utils/grid-query-internal.utils.ts +0 -37
- package/src/lib/store/features/utils/resize.utils.ts +0 -165
- package/src/lib/store/features/widget-management.feature.ts +0 -158
- package/src/lib/styles/_dashboard-grid-vars.scss +0 -11
- package/src/lib/widget-list/__tests__/widget-list-bridge-integration.spec.ts +0 -137
- package/src/lib/widget-list/widget-list.component.html +0 -22
- package/src/lib/widget-list/widget-list.component.scss +0 -154
- package/src/lib/widget-list/widget-list.component.ts +0 -106
- package/src/public-api.ts +0 -21
- package/src/test-setup.ts +0 -10
- package/tsconfig.lib.json +0 -15
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -14
|
@@ -1,974 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { DashboardStore } from '../dashboard-store';
|
|
3
|
-
import { CellIdUtils, WidgetIdUtils, CellData, DragData, WidgetMetadata, WidgetFactory } from '../../models';
|
|
4
|
-
import { DashboardService } from '../../services/dashboard.service';
|
|
5
|
-
|
|
6
|
-
describe('DashboardStore - Computed Properties', () => {
|
|
7
|
-
let store: InstanceType<typeof DashboardStore>;
|
|
8
|
-
let mockWidgetFactory: WidgetFactory;
|
|
9
|
-
let testWidgetMetadata: WidgetMetadata;
|
|
10
|
-
let dashboardService: jasmine.SpyObj<DashboardService>;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
const spy = jasmine.createSpyObj('DashboardService', ['getFactory']);
|
|
14
|
-
|
|
15
|
-
TestBed.configureTestingModule({
|
|
16
|
-
providers: [
|
|
17
|
-
DashboardStore,
|
|
18
|
-
{ provide: DashboardService, useValue: spy }
|
|
19
|
-
]
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
store = TestBed.inject(DashboardStore);
|
|
23
|
-
dashboardService = 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('createComponent')
|
|
29
|
-
} as any;
|
|
30
|
-
|
|
31
|
-
testWidgetMetadata = {
|
|
32
|
-
widgetTypeid: 'test-widget',
|
|
33
|
-
name: 'Test Widget',
|
|
34
|
-
description: 'A test widget for unit tests',
|
|
35
|
-
svgIcon: '<svg></svg>'
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
dashboardService.getFactory.and.returnValue(mockWidgetFactory);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('resizePreviewCells', () => {
|
|
42
|
-
let cellId: ReturnType<typeof CellIdUtils.create>;
|
|
43
|
-
|
|
44
|
-
beforeEach(() => {
|
|
45
|
-
cellId = CellIdUtils.create(4, 4);
|
|
46
|
-
const cell: CellData = {
|
|
47
|
-
widgetId: WidgetIdUtils.generate(),
|
|
48
|
-
cellId,
|
|
49
|
-
row: 4,
|
|
50
|
-
col: 4,
|
|
51
|
-
rowSpan: 2,
|
|
52
|
-
colSpan: 3,
|
|
53
|
-
widgetFactory: mockWidgetFactory,
|
|
54
|
-
widgetState: {},
|
|
55
|
-
};
|
|
56
|
-
store.addWidget(cell);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should return empty array when no resize is active', () => {
|
|
60
|
-
expect(store.resizePreviewCells()).toEqual([]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should return empty array when resize data exists but widget is removed', () => {
|
|
64
|
-
store.startResize(cellId);
|
|
65
|
-
const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!; store.removeWidget(widget.widgetId);
|
|
66
|
-
|
|
67
|
-
expect(store.resizePreviewCells()).toEqual([]);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should calculate preview cells for original widget size', () => {
|
|
71
|
-
store.startResize(cellId);
|
|
72
|
-
|
|
73
|
-
const previewCells = store.resizePreviewCells();
|
|
74
|
-
|
|
75
|
-
expect(previewCells.length).toBe(6); // 2x3 widget
|
|
76
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 4 }));
|
|
77
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 5 }));
|
|
78
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 6 }));
|
|
79
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 4 }));
|
|
80
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 5 }));
|
|
81
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 6 }));
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should update preview cells when resize preview changes horizontally', () => {
|
|
85
|
-
store.startResize(cellId);
|
|
86
|
-
store.updateResizePreview('horizontal', 2); // colSpan becomes 5
|
|
87
|
-
|
|
88
|
-
const previewCells = store.resizePreviewCells();
|
|
89
|
-
|
|
90
|
-
expect(previewCells.length).toBe(10); // 2x5 widget
|
|
91
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 4 }));
|
|
92
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 8 })); // rightmost
|
|
93
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 4 }));
|
|
94
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 5, col: 8 })); // bottom-right
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should update preview cells when resize preview changes vertically', () => {
|
|
98
|
-
store.startResize(cellId);
|
|
99
|
-
store.updateResizePreview('vertical', 1); // rowSpan becomes 3
|
|
100
|
-
|
|
101
|
-
const previewCells = store.resizePreviewCells();
|
|
102
|
-
|
|
103
|
-
expect(previewCells.length).toBe(9); // 3x3 widget
|
|
104
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 4 }));
|
|
105
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 6, col: 4 })); // bottommost
|
|
106
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 6, col: 6 })); // bottom-right
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should handle single cell preview when resized to minimum', () => {
|
|
110
|
-
store.startResize(cellId);
|
|
111
|
-
store.updateResizePreview('horizontal', -5); // Reduce to minimum
|
|
112
|
-
store.updateResizePreview('vertical', -5); // Reduce to minimum
|
|
113
|
-
|
|
114
|
-
const previewCells = store.resizePreviewCells();
|
|
115
|
-
|
|
116
|
-
expect(previewCells.length).toBe(1);
|
|
117
|
-
expect(previewCells[0]).toEqual(jasmine.objectContaining({ row: 4, col: 4 }));
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should handle large preview sizes', () => {
|
|
121
|
-
store.startResize(cellId);
|
|
122
|
-
store.updateResizePreview('horizontal', 8); // colSpan becomes 11
|
|
123
|
-
store.updateResizePreview('vertical', 6); // rowSpan becomes 8
|
|
124
|
-
|
|
125
|
-
const previewCells = store.resizePreviewCells();
|
|
126
|
-
|
|
127
|
-
expect(previewCells.length).toBe(88); // 8x11 widget
|
|
128
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 4, col: 4 })); // top-left
|
|
129
|
-
expect(previewCells).toContain(jasmine.objectContaining({ row: 11, col: 14 })); // bottom-right
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should update reactively when resize preview changes multiple times', () => {
|
|
133
|
-
store.startResize(cellId);
|
|
134
|
-
|
|
135
|
-
// Initial state
|
|
136
|
-
expect(store.resizePreviewCells().length).toBe(6);
|
|
137
|
-
|
|
138
|
-
// First change
|
|
139
|
-
store.updateResizePreview('horizontal', 1);
|
|
140
|
-
expect(store.resizePreviewCells().length).toBe(8); // 2x4
|
|
141
|
-
|
|
142
|
-
// Second change
|
|
143
|
-
store.updateResizePreview('vertical', 1);
|
|
144
|
-
expect(store.resizePreviewCells().length).toBe(12); // 3x4
|
|
145
|
-
|
|
146
|
-
// Third change
|
|
147
|
-
store.updateResizePreview('horizontal', -2);
|
|
148
|
-
const finalResizeData = store.resizeData();
|
|
149
|
-
expect(finalResizeData?.previewRowSpan).toBe(3);
|
|
150
|
-
expect(finalResizeData?.previewColSpan).toBe(1);
|
|
151
|
-
expect(store.resizePreviewCells().length).toBe(3); // 3x1
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
describe('resizePreviewMap', () => {
|
|
156
|
-
let cellId: ReturnType<typeof CellIdUtils.create>;
|
|
157
|
-
|
|
158
|
-
beforeEach(() => {
|
|
159
|
-
cellId = CellIdUtils.create(8, 8);
|
|
160
|
-
const cell: CellData = {
|
|
161
|
-
widgetId: WidgetIdUtils.generate(),
|
|
162
|
-
cellId,
|
|
163
|
-
row: 8,
|
|
164
|
-
col: 8,
|
|
165
|
-
rowSpan: 1,
|
|
166
|
-
colSpan: 1,
|
|
167
|
-
widgetFactory: mockWidgetFactory,
|
|
168
|
-
widgetState: {},
|
|
169
|
-
};
|
|
170
|
-
store.addWidget(cell);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should return empty set when no resize is active', () => {
|
|
174
|
-
expect(store.resizePreviewMap().size).toBe(0);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should create map from preview cells', () => {
|
|
178
|
-
store.startResize(cellId);
|
|
179
|
-
store.updateResizePreview('horizontal', 2);
|
|
180
|
-
store.updateResizePreview('vertical', 1);
|
|
181
|
-
|
|
182
|
-
const previewMap = store.resizePreviewMap();
|
|
183
|
-
|
|
184
|
-
expect(previewMap.size).toBe(6); // 2x3 grid
|
|
185
|
-
expect(previewMap.has(CellIdUtils.create(8, 8))).toBe(true);
|
|
186
|
-
expect(previewMap.has(CellIdUtils.create(8, 9))).toBe(true);
|
|
187
|
-
expect(previewMap.has(CellIdUtils.create(8, 10))).toBe(true);
|
|
188
|
-
expect(previewMap.has(CellIdUtils.create(9, 8))).toBe(true);
|
|
189
|
-
expect(previewMap.has(CellIdUtils.create(9, 9))).toBe(true);
|
|
190
|
-
expect(previewMap.has(CellIdUtils.create(9, 10))).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should not contain cells outside preview area', () => {
|
|
194
|
-
store.startResize(cellId);
|
|
195
|
-
store.updateResizePreview('horizontal', 1);
|
|
196
|
-
|
|
197
|
-
const previewMap = store.resizePreviewMap();
|
|
198
|
-
|
|
199
|
-
expect(previewMap.has(CellIdUtils.create(8, 8))).toBe(true);
|
|
200
|
-
expect(previewMap.has(CellIdUtils.create(8, 9))).toBe(true);
|
|
201
|
-
expect(previewMap.has(CellIdUtils.create(8, 10))).toBe(false);
|
|
202
|
-
expect(previewMap.has(CellIdUtils.create(9, 8))).toBe(false);
|
|
203
|
-
expect(previewMap.has(CellIdUtils.create(7, 8))).toBe(false);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('should update when preview changes', () => {
|
|
207
|
-
store.startResize(cellId);
|
|
208
|
-
|
|
209
|
-
let previewMap = store.resizePreviewMap();
|
|
210
|
-
expect(previewMap.size).toBe(1);
|
|
211
|
-
|
|
212
|
-
store.updateResizePreview('horizontal', 1);
|
|
213
|
-
previewMap = store.resizePreviewMap();
|
|
214
|
-
expect(previewMap.size).toBe(2);
|
|
215
|
-
|
|
216
|
-
store.updateResizePreview('vertical', 2);
|
|
217
|
-
previewMap = store.resizePreviewMap();
|
|
218
|
-
expect(previewMap.size).toBe(6);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should handle single cell map efficiently', () => {
|
|
222
|
-
store.startResize(cellId);
|
|
223
|
-
|
|
224
|
-
const previewMap = store.resizePreviewMap();
|
|
225
|
-
|
|
226
|
-
expect(previewMap.size).toBe(1);
|
|
227
|
-
expect(previewMap.has(CellIdUtils.create(8, 8))).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should be empty when widget is removed during resize', () => {
|
|
231
|
-
store.startResize(cellId);
|
|
232
|
-
store.updateResizePreview('horizontal', 2);
|
|
233
|
-
|
|
234
|
-
const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!; store.removeWidget(widget.widgetId);
|
|
235
|
-
|
|
236
|
-
expect(store.resizePreviewMap().size).toBe(0);
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('highlightedZones', () => {
|
|
241
|
-
it('should return empty array when no drag data', () => {
|
|
242
|
-
expect(store.highlightedZones()).toEqual([]);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('should return empty array when no hovered drop zone', () => {
|
|
246
|
-
const dragData: DragData = {
|
|
247
|
-
kind: 'widget',
|
|
248
|
-
content: testWidgetMetadata
|
|
249
|
-
};
|
|
250
|
-
store.startDrag(dragData);
|
|
251
|
-
|
|
252
|
-
expect(store.highlightedZones()).toEqual([]);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('should return empty array when both drag data and hovered zone are null', () => {
|
|
256
|
-
expect(store.highlightedZones()).toEqual([]);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('should calculate highlighted zones for widget drag (single cell)', () => {
|
|
260
|
-
const dragData: DragData = {
|
|
261
|
-
kind: 'widget',
|
|
262
|
-
content: testWidgetMetadata
|
|
263
|
-
};
|
|
264
|
-
store.startDrag(dragData);
|
|
265
|
-
store.setHoveredDropZone({ row: 5, col: 8 });
|
|
266
|
-
|
|
267
|
-
const zones = store.highlightedZones();
|
|
268
|
-
|
|
269
|
-
expect(zones.length).toBe(1);
|
|
270
|
-
expect(zones[0]).toEqual({ row: 5, col: 8 });
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it('should calculate highlighted zones for cell drag (multi-cell)', () => {
|
|
274
|
-
const dragData: DragData = {
|
|
275
|
-
kind: 'cell',
|
|
276
|
-
content: {
|
|
277
|
-
widgetId: WidgetIdUtils.generate(),
|
|
278
|
-
cellId: CellIdUtils.create(3, 3),
|
|
279
|
-
row: 3,
|
|
280
|
-
col: 3,
|
|
281
|
-
rowSpan: 2,
|
|
282
|
-
colSpan: 3,
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
store.startDrag(dragData);
|
|
286
|
-
store.setHoveredDropZone({ row: 6, col: 7 });
|
|
287
|
-
|
|
288
|
-
const zones = store.highlightedZones();
|
|
289
|
-
|
|
290
|
-
expect(zones.length).toBe(6); // 2x3 grid
|
|
291
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 6, col: 7 }));
|
|
292
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 6, col: 8 }));
|
|
293
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 6, col: 9 }));
|
|
294
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 7, col: 7 }));
|
|
295
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 7, col: 8 }));
|
|
296
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 7, col: 9 }));
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it('should update when hovered drop zone changes', () => {
|
|
300
|
-
const dragData: DragData = {
|
|
301
|
-
kind: 'cell',
|
|
302
|
-
content: {
|
|
303
|
-
widgetId: WidgetIdUtils.generate(),
|
|
304
|
-
cellId: CellIdUtils.create(1, 1),
|
|
305
|
-
row: 1,
|
|
306
|
-
col: 1,
|
|
307
|
-
rowSpan: 1,
|
|
308
|
-
colSpan: 2,
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
store.startDrag(dragData);
|
|
312
|
-
|
|
313
|
-
store.setHoveredDropZone({ row: 3, col: 3 });
|
|
314
|
-
let zones = store.highlightedZones();
|
|
315
|
-
expect(zones).toEqual([
|
|
316
|
-
{ row: 3, col: 3 },
|
|
317
|
-
{ row: 3, col: 4 }
|
|
318
|
-
]);
|
|
319
|
-
|
|
320
|
-
store.setHoveredDropZone({ row: 8, col: 10 });
|
|
321
|
-
zones = store.highlightedZones();
|
|
322
|
-
expect(zones).toEqual([
|
|
323
|
-
{ row: 8, col: 10 },
|
|
324
|
-
{ row: 8, col: 11 }
|
|
325
|
-
]);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it('should update when drag data changes', () => {
|
|
329
|
-
store.setHoveredDropZone({ row: 5, col: 5 });
|
|
330
|
-
|
|
331
|
-
const dragData1: DragData = {
|
|
332
|
-
kind: 'widget',
|
|
333
|
-
content: testWidgetMetadata
|
|
334
|
-
};
|
|
335
|
-
store.startDrag(dragData1);
|
|
336
|
-
let zones = store.highlightedZones();
|
|
337
|
-
expect(zones.length).toBe(1);
|
|
338
|
-
|
|
339
|
-
const dragData2: DragData = {
|
|
340
|
-
kind: 'cell',
|
|
341
|
-
content: {
|
|
342
|
-
widgetId: WidgetIdUtils.generate(),
|
|
343
|
-
cellId: CellIdUtils.create(2, 2),
|
|
344
|
-
row: 2,
|
|
345
|
-
col: 2,
|
|
346
|
-
rowSpan: 3,
|
|
347
|
-
colSpan: 2,
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
store.startDrag(dragData2);
|
|
351
|
-
zones = store.highlightedZones();
|
|
352
|
-
expect(zones.length).toBe(6); // 3x2 grid
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it('should handle large cell drag spans', () => {
|
|
356
|
-
const dragData: DragData = {
|
|
357
|
-
kind: 'cell',
|
|
358
|
-
content: {
|
|
359
|
-
widgetId: WidgetIdUtils.generate(),
|
|
360
|
-
cellId: CellIdUtils.create(1, 1),
|
|
361
|
-
row: 1,
|
|
362
|
-
col: 1,
|
|
363
|
-
rowSpan: 4,
|
|
364
|
-
colSpan: 5,
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
store.startDrag(dragData);
|
|
368
|
-
store.setHoveredDropZone({ row: 2, col: 3 });
|
|
369
|
-
|
|
370
|
-
const zones = store.highlightedZones();
|
|
371
|
-
|
|
372
|
-
expect(zones.length).toBe(20); // 4x5 grid
|
|
373
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 2, col: 3 })); // top-left
|
|
374
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 2, col: 7 })); // top-right
|
|
375
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 5, col: 3 })); // bottom-left
|
|
376
|
-
expect(zones).toContain(jasmine.objectContaining({ row: 5, col: 7 })); // bottom-right
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('should clear when drag ends', () => {
|
|
380
|
-
const dragData: DragData = {
|
|
381
|
-
kind: 'widget',
|
|
382
|
-
content: testWidgetMetadata
|
|
383
|
-
};
|
|
384
|
-
store.startDrag(dragData);
|
|
385
|
-
store.setHoveredDropZone({ row: 5, col: 5 });
|
|
386
|
-
|
|
387
|
-
expect(store.highlightedZones().length).toBe(1);
|
|
388
|
-
|
|
389
|
-
store.endDrag();
|
|
390
|
-
|
|
391
|
-
expect(store.highlightedZones()).toEqual([]);
|
|
392
|
-
});
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
describe('highlightMap', () => {
|
|
396
|
-
it('should return empty set when no highlighted zones', () => {
|
|
397
|
-
expect(store.highlightMap().size).toBe(0);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
it('should create map from highlighted zones for widget drag', () => {
|
|
401
|
-
const dragData: DragData = {
|
|
402
|
-
kind: 'widget',
|
|
403
|
-
content: testWidgetMetadata
|
|
404
|
-
};
|
|
405
|
-
store.startDrag(dragData);
|
|
406
|
-
store.setHoveredDropZone({ row: 7, col: 9 });
|
|
407
|
-
|
|
408
|
-
const highlightMap = store.highlightMap();
|
|
409
|
-
|
|
410
|
-
expect(highlightMap.size).toBe(1);
|
|
411
|
-
expect(highlightMap.has(CellIdUtils.create(7, 9))).toBe(true);
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it('should create map from highlighted zones for cell drag', () => {
|
|
415
|
-
const dragData: DragData = {
|
|
416
|
-
kind: 'cell',
|
|
417
|
-
content: {
|
|
418
|
-
widgetId: WidgetIdUtils.generate(),
|
|
419
|
-
cellId: CellIdUtils.create(4, 4),
|
|
420
|
-
row: 4,
|
|
421
|
-
col: 4,
|
|
422
|
-
rowSpan: 2,
|
|
423
|
-
colSpan: 2,
|
|
424
|
-
}
|
|
425
|
-
};
|
|
426
|
-
store.startDrag(dragData);
|
|
427
|
-
store.setHoveredDropZone({ row: 8, col: 10 });
|
|
428
|
-
|
|
429
|
-
const highlightMap = store.highlightMap();
|
|
430
|
-
|
|
431
|
-
expect(highlightMap.size).toBe(4);
|
|
432
|
-
expect(highlightMap.has(CellIdUtils.create(8, 10))).toBe(true);
|
|
433
|
-
expect(highlightMap.has(CellIdUtils.create(8, 11))).toBe(true);
|
|
434
|
-
expect(highlightMap.has(CellIdUtils.create(9, 10))).toBe(true);
|
|
435
|
-
expect(highlightMap.has(CellIdUtils.create(9, 11))).toBe(true);
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
it('should not contain cells outside highlighted zones', () => {
|
|
439
|
-
const dragData: DragData = {
|
|
440
|
-
kind: 'cell',
|
|
441
|
-
content: {
|
|
442
|
-
widgetId: WidgetIdUtils.generate(),
|
|
443
|
-
cellId: CellIdUtils.create(1, 1),
|
|
444
|
-
row: 1,
|
|
445
|
-
col: 1,
|
|
446
|
-
rowSpan: 1,
|
|
447
|
-
colSpan: 2,
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
store.startDrag(dragData);
|
|
451
|
-
store.setHoveredDropZone({ row: 5, col: 5 });
|
|
452
|
-
|
|
453
|
-
const highlightMap = store.highlightMap();
|
|
454
|
-
|
|
455
|
-
expect(highlightMap.has(CellIdUtils.create(5, 5))).toBe(true);
|
|
456
|
-
expect(highlightMap.has(CellIdUtils.create(5, 6))).toBe(true);
|
|
457
|
-
expect(highlightMap.has(CellIdUtils.create(5, 7))).toBe(false);
|
|
458
|
-
expect(highlightMap.has(CellIdUtils.create(6, 5))).toBe(false);
|
|
459
|
-
expect(highlightMap.has(CellIdUtils.create(4, 5))).toBe(false);
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it('should update when highlighted zones change', () => {
|
|
463
|
-
const dragData: DragData = {
|
|
464
|
-
kind: 'widget',
|
|
465
|
-
content: testWidgetMetadata
|
|
466
|
-
};
|
|
467
|
-
store.startDrag(dragData);
|
|
468
|
-
|
|
469
|
-
store.setHoveredDropZone({ row: 3, col: 3 });
|
|
470
|
-
let highlightMap = store.highlightMap();
|
|
471
|
-
expect(highlightMap.size).toBe(1);
|
|
472
|
-
expect(highlightMap.has(CellIdUtils.create(3, 3))).toBe(true);
|
|
473
|
-
|
|
474
|
-
store.setHoveredDropZone({ row: 7, col: 12 });
|
|
475
|
-
highlightMap = store.highlightMap();
|
|
476
|
-
expect(highlightMap.size).toBe(1);
|
|
477
|
-
expect(highlightMap.has(CellIdUtils.create(3, 3))).toBe(false);
|
|
478
|
-
expect(highlightMap.has(CellIdUtils.create(7, 12))).toBe(true);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it('should handle large highlighted zones efficiently', () => {
|
|
482
|
-
const dragData: DragData = {
|
|
483
|
-
kind: 'cell',
|
|
484
|
-
content: {
|
|
485
|
-
widgetId: WidgetIdUtils.generate(),
|
|
486
|
-
cellId: CellIdUtils.create(1, 1),
|
|
487
|
-
row: 1,
|
|
488
|
-
col: 1,
|
|
489
|
-
rowSpan: 5,
|
|
490
|
-
colSpan: 4,
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
store.startDrag(dragData);
|
|
494
|
-
store.setHoveredDropZone({ row: 2, col: 2 });
|
|
495
|
-
|
|
496
|
-
const highlightMap = store.highlightMap();
|
|
497
|
-
|
|
498
|
-
expect(highlightMap.size).toBe(20); // 5x4 grid
|
|
499
|
-
expect(highlightMap.has(CellIdUtils.create(2, 2))).toBe(true);
|
|
500
|
-
expect(highlightMap.has(CellIdUtils.create(6, 5))).toBe(true);
|
|
501
|
-
expect(highlightMap.has(CellIdUtils.create(1, 1))).toBe(false);
|
|
502
|
-
expect(highlightMap.has(CellIdUtils.create(7, 6))).toBe(false);
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
it('should be empty when drag ends', () => {
|
|
506
|
-
const dragData: DragData = {
|
|
507
|
-
kind: 'widget',
|
|
508
|
-
content: testWidgetMetadata
|
|
509
|
-
};
|
|
510
|
-
store.startDrag(dragData);
|
|
511
|
-
store.setHoveredDropZone({ row: 5, col: 5 });
|
|
512
|
-
|
|
513
|
-
expect(store.highlightMap().size).toBe(1);
|
|
514
|
-
|
|
515
|
-
store.endDrag();
|
|
516
|
-
|
|
517
|
-
expect(store.highlightMap().size).toBe(0);
|
|
518
|
-
});
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
describe('invalidHighlightMap', () => {
|
|
522
|
-
let widget1: CellData;
|
|
523
|
-
let widget2: CellData;
|
|
524
|
-
|
|
525
|
-
beforeEach(() => {
|
|
526
|
-
// Add some existing widgets to create collision scenarios
|
|
527
|
-
widget1 = {
|
|
528
|
-
widgetId: WidgetIdUtils.generate(),
|
|
529
|
-
cellId: CellIdUtils.create(3, 3),
|
|
530
|
-
row: 3,
|
|
531
|
-
col: 3,
|
|
532
|
-
rowSpan: 2,
|
|
533
|
-
colSpan: 2,
|
|
534
|
-
widgetFactory: mockWidgetFactory,
|
|
535
|
-
widgetState: {},
|
|
536
|
-
};
|
|
537
|
-
widget2 = {
|
|
538
|
-
widgetId: WidgetIdUtils.generate(),
|
|
539
|
-
cellId: CellIdUtils.create(8, 8),
|
|
540
|
-
row: 8,
|
|
541
|
-
col: 8,
|
|
542
|
-
rowSpan: 1,
|
|
543
|
-
colSpan: 3,
|
|
544
|
-
widgetFactory: mockWidgetFactory,
|
|
545
|
-
widgetState: {},
|
|
546
|
-
};
|
|
547
|
-
store.addWidget(widget1);
|
|
548
|
-
store.addWidget(widget2);
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
it('should return empty set when no drag data', () => {
|
|
552
|
-
expect(store.invalidHighlightMap().size).toBe(0);
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
it('should return empty set when no hovered drop zone', () => {
|
|
556
|
-
const dragData: DragData = {
|
|
557
|
-
kind: 'widget',
|
|
558
|
-
content: testWidgetMetadata
|
|
559
|
-
};
|
|
560
|
-
store.startDrag(dragData);
|
|
561
|
-
|
|
562
|
-
expect(store.invalidHighlightMap().size).toBe(0);
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
it('should return empty set for valid placement', () => {
|
|
566
|
-
const dragData: DragData = {
|
|
567
|
-
kind: 'widget',
|
|
568
|
-
content: testWidgetMetadata
|
|
569
|
-
};
|
|
570
|
-
store.startDrag(dragData);
|
|
571
|
-
store.setHoveredDropZone({ row: 1, col: 1 }); // Empty space
|
|
572
|
-
|
|
573
|
-
expect(store.invalidHighlightMap().size).toBe(0);
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
it('should return invalid cells for collision with existing widget', () => {
|
|
577
|
-
const dragData: DragData = {
|
|
578
|
-
kind: 'widget',
|
|
579
|
-
content: testWidgetMetadata
|
|
580
|
-
};
|
|
581
|
-
store.startDrag(dragData);
|
|
582
|
-
store.setHoveredDropZone({ row: 3, col: 3 }); // Collides with widget1
|
|
583
|
-
|
|
584
|
-
const invalidMap = store.invalidHighlightMap();
|
|
585
|
-
|
|
586
|
-
expect(invalidMap.size).toBe(1);
|
|
587
|
-
expect(invalidMap.has(CellIdUtils.create(3, 3))).toBe(true);
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
it('should return invalid cells for partial collision', () => {
|
|
591
|
-
const dragData: DragData = {
|
|
592
|
-
kind: 'cell',
|
|
593
|
-
content: {
|
|
594
|
-
widgetId: WidgetIdUtils.generate(),
|
|
595
|
-
cellId: CellIdUtils.create(1, 1),
|
|
596
|
-
row: 1,
|
|
597
|
-
col: 1,
|
|
598
|
-
rowSpan: 2,
|
|
599
|
-
colSpan: 2,
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
store.startDrag(dragData);
|
|
603
|
-
store.setHoveredDropZone({ row: 2, col: 2 }); // Partially overlaps widget1
|
|
604
|
-
|
|
605
|
-
const invalidMap = store.invalidHighlightMap();
|
|
606
|
-
|
|
607
|
-
expect(invalidMap.size).toBe(4); // All cells in the 2x2 footprint
|
|
608
|
-
expect(invalidMap.has(CellIdUtils.create(2, 2))).toBe(true);
|
|
609
|
-
expect(invalidMap.has(CellIdUtils.create(2, 3))).toBe(true);
|
|
610
|
-
expect(invalidMap.has(CellIdUtils.create(3, 2))).toBe(true);
|
|
611
|
-
expect(invalidMap.has(CellIdUtils.create(3, 3))).toBe(true);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it('should return invalid cells for out of bounds placement', () => {
|
|
615
|
-
const dragData: DragData = {
|
|
616
|
-
kind: 'widget',
|
|
617
|
-
content: testWidgetMetadata
|
|
618
|
-
};
|
|
619
|
-
store.startDrag(dragData);
|
|
620
|
-
store.setHoveredDropZone({ row: 17, col: 5 }); // Row out of bounds
|
|
621
|
-
|
|
622
|
-
const invalidMap = store.invalidHighlightMap();
|
|
623
|
-
|
|
624
|
-
expect(invalidMap.size).toBe(1);
|
|
625
|
-
expect(invalidMap.has(CellIdUtils.create(17, 5))).toBe(true);
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
it('should return invalid cells for multi-cell out of bounds', () => {
|
|
629
|
-
const dragData: DragData = {
|
|
630
|
-
kind: 'cell',
|
|
631
|
-
content: {
|
|
632
|
-
widgetId: WidgetIdUtils.generate(),
|
|
633
|
-
cellId: CellIdUtils.create(1, 1),
|
|
634
|
-
row: 1,
|
|
635
|
-
col: 1,
|
|
636
|
-
rowSpan: 2,
|
|
637
|
-
colSpan: 3,
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
store.startDrag(dragData);
|
|
641
|
-
store.setHoveredDropZone({ row: 15, col: 15 }); // Spans out of bounds
|
|
642
|
-
|
|
643
|
-
const invalidMap = store.invalidHighlightMap();
|
|
644
|
-
|
|
645
|
-
expect(invalidMap.size).toBe(6); // All cells in the 2x3 footprint
|
|
646
|
-
expect(invalidMap.has(CellIdUtils.create(15, 15))).toBe(true);
|
|
647
|
-
expect(invalidMap.has(CellIdUtils.create(15, 16))).toBe(true);
|
|
648
|
-
expect(invalidMap.has(CellIdUtils.create(15, 17))).toBe(true); // Out of bounds
|
|
649
|
-
expect(invalidMap.has(CellIdUtils.create(16, 15))).toBe(true);
|
|
650
|
-
expect(invalidMap.has(CellIdUtils.create(16, 16))).toBe(true);
|
|
651
|
-
expect(invalidMap.has(CellIdUtils.create(16, 17))).toBe(true); // Out of bounds
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
it('should exclude self when moving existing cell', () => {
|
|
655
|
-
const cellId = CellIdUtils.create(3, 3);
|
|
656
|
-
const dragData: DragData = {
|
|
657
|
-
kind: 'cell',
|
|
658
|
-
content: {
|
|
659
|
-
widgetId: widget1.widgetId, // Use the SAME widgetId from beforeEach
|
|
660
|
-
cellId,
|
|
661
|
-
row: 3,
|
|
662
|
-
col: 3,
|
|
663
|
-
rowSpan: 2,
|
|
664
|
-
colSpan: 2,
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
store.startDrag(dragData);
|
|
668
|
-
store.setHoveredDropZone({ row: 4, col: 4 }); // Partial self-overlap
|
|
669
|
-
|
|
670
|
-
const invalidMap = store.invalidHighlightMap();
|
|
671
|
-
|
|
672
|
-
expect(invalidMap.size).toBe(0); // Self-overlap should be allowed
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
it('should detect collision when moving cell to occupied space', () => {
|
|
676
|
-
const cellId = CellIdUtils.create(3, 3);
|
|
677
|
-
const dragData: DragData = {
|
|
678
|
-
kind: 'cell',
|
|
679
|
-
content: {
|
|
680
|
-
widgetId: WidgetIdUtils.generate(),
|
|
681
|
-
cellId,
|
|
682
|
-
row: 3,
|
|
683
|
-
col: 3,
|
|
684
|
-
rowSpan: 2,
|
|
685
|
-
colSpan: 2,
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
store.startDrag(dragData);
|
|
689
|
-
store.setHoveredDropZone({ row: 8, col: 8 }); // Collides with widget2
|
|
690
|
-
|
|
691
|
-
const invalidMap = store.invalidHighlightMap();
|
|
692
|
-
|
|
693
|
-
expect(invalidMap.size).toBe(4); // All cells in the 2x2 footprint
|
|
694
|
-
expect(invalidMap.has(CellIdUtils.create(8, 8))).toBe(true);
|
|
695
|
-
expect(invalidMap.has(CellIdUtils.create(8, 9))).toBe(true);
|
|
696
|
-
expect(invalidMap.has(CellIdUtils.create(9, 8))).toBe(true);
|
|
697
|
-
expect(invalidMap.has(CellIdUtils.create(9, 9))).toBe(true);
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
it('should update when hovered zone changes from valid to invalid', () => {
|
|
701
|
-
const dragData: DragData = {
|
|
702
|
-
kind: 'widget',
|
|
703
|
-
content: testWidgetMetadata
|
|
704
|
-
};
|
|
705
|
-
store.startDrag(dragData);
|
|
706
|
-
|
|
707
|
-
// Valid placement
|
|
708
|
-
store.setHoveredDropZone({ row: 1, col: 1 });
|
|
709
|
-
expect(store.invalidHighlightMap().size).toBe(0);
|
|
710
|
-
|
|
711
|
-
// Invalid placement
|
|
712
|
-
store.setHoveredDropZone({ row: 3, col: 3 });
|
|
713
|
-
expect(store.invalidHighlightMap().size).toBe(1);
|
|
714
|
-
|
|
715
|
-
// Valid placement again
|
|
716
|
-
store.setHoveredDropZone({ row: 6, col: 6 });
|
|
717
|
-
expect(store.invalidHighlightMap().size).toBe(0);
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
it('should handle complex collision scenarios', () => {
|
|
721
|
-
const dragData: DragData = {
|
|
722
|
-
kind: 'cell',
|
|
723
|
-
content: {
|
|
724
|
-
widgetId: WidgetIdUtils.generate(),
|
|
725
|
-
cellId: CellIdUtils.create(1, 1),
|
|
726
|
-
row: 1,
|
|
727
|
-
col: 1,
|
|
728
|
-
rowSpan: 3,
|
|
729
|
-
colSpan: 4,
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
store.startDrag(dragData);
|
|
733
|
-
store.setHoveredDropZone({ row: 2, col: 2 }); // Large widget overlapping both existing widgets
|
|
734
|
-
|
|
735
|
-
const invalidMap = store.invalidHighlightMap();
|
|
736
|
-
|
|
737
|
-
expect(invalidMap.size).toBe(12); // All cells in the 3x4 footprint are invalid
|
|
738
|
-
expect(invalidMap.has(CellIdUtils.create(2, 2))).toBe(true);
|
|
739
|
-
expect(invalidMap.has(CellIdUtils.create(4, 5))).toBe(true);
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
it('should be empty when drag ends', () => {
|
|
743
|
-
const dragData: DragData = {
|
|
744
|
-
kind: 'widget',
|
|
745
|
-
content: testWidgetMetadata
|
|
746
|
-
};
|
|
747
|
-
store.startDrag(dragData);
|
|
748
|
-
store.setHoveredDropZone({ row: 3, col: 3 }); // Invalid placement
|
|
749
|
-
|
|
750
|
-
expect(store.invalidHighlightMap().size).toBe(1);
|
|
751
|
-
|
|
752
|
-
store.endDrag();
|
|
753
|
-
|
|
754
|
-
expect(store.invalidHighlightMap().size).toBe(0);
|
|
755
|
-
});
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
describe('isValidPlacement', () => {
|
|
759
|
-
let existingWidget: CellData;
|
|
760
|
-
|
|
761
|
-
beforeEach(() => {
|
|
762
|
-
// Add existing widget for collision testing
|
|
763
|
-
existingWidget = {
|
|
764
|
-
widgetId: WidgetIdUtils.generate(),
|
|
765
|
-
cellId: CellIdUtils.create(5, 5),
|
|
766
|
-
row: 5,
|
|
767
|
-
col: 5,
|
|
768
|
-
rowSpan: 2,
|
|
769
|
-
colSpan: 2,
|
|
770
|
-
widgetFactory: mockWidgetFactory,
|
|
771
|
-
widgetState: {},
|
|
772
|
-
};
|
|
773
|
-
store.addWidget(existingWidget);
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
it('should return true when no drag data', () => {
|
|
777
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
778
|
-
});
|
|
779
|
-
|
|
780
|
-
it('should return true when no hovered drop zone', () => {
|
|
781
|
-
const dragData: DragData = {
|
|
782
|
-
kind: 'widget',
|
|
783
|
-
content: testWidgetMetadata
|
|
784
|
-
};
|
|
785
|
-
store.startDrag(dragData);
|
|
786
|
-
|
|
787
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
it('should return true for valid placement', () => {
|
|
791
|
-
const dragData: DragData = {
|
|
792
|
-
kind: 'widget',
|
|
793
|
-
content: testWidgetMetadata
|
|
794
|
-
};
|
|
795
|
-
store.startDrag(dragData);
|
|
796
|
-
store.setHoveredDropZone({ row: 1, col: 1 }); // Empty space
|
|
797
|
-
|
|
798
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
it('should return false for collision', () => {
|
|
802
|
-
const dragData: DragData = {
|
|
803
|
-
kind: 'widget',
|
|
804
|
-
content: testWidgetMetadata
|
|
805
|
-
};
|
|
806
|
-
store.startDrag(dragData);
|
|
807
|
-
store.setHoveredDropZone({ row: 5, col: 5 }); // Collides with existing widget
|
|
808
|
-
|
|
809
|
-
expect(store.isValidPlacement()).toBe(false);
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
it('should return false for out of bounds', () => {
|
|
813
|
-
const dragData: DragData = {
|
|
814
|
-
kind: 'widget',
|
|
815
|
-
content: testWidgetMetadata
|
|
816
|
-
};
|
|
817
|
-
store.startDrag(dragData);
|
|
818
|
-
store.setHoveredDropZone({ row: 17, col: 5 }); // Row out of bounds
|
|
819
|
-
|
|
820
|
-
expect(store.isValidPlacement()).toBe(false);
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
it('should return true for self-overlap when moving existing cell', () => {
|
|
824
|
-
const cellId = CellIdUtils.create(5, 5);
|
|
825
|
-
const dragData: DragData = {
|
|
826
|
-
kind: 'cell',
|
|
827
|
-
content: {
|
|
828
|
-
widgetId: existingWidget.widgetId, // Use the SAME widgetId from beforeEach
|
|
829
|
-
cellId,
|
|
830
|
-
row: 5,
|
|
831
|
-
col: 5,
|
|
832
|
-
rowSpan: 2,
|
|
833
|
-
colSpan: 2,
|
|
834
|
-
}
|
|
835
|
-
};
|
|
836
|
-
store.startDrag(dragData);
|
|
837
|
-
store.setHoveredDropZone({ row: 6, col: 6 }); // Partial self-overlap
|
|
838
|
-
|
|
839
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
840
|
-
});
|
|
841
|
-
|
|
842
|
-
it('should return false for collision when moving cell to occupied space', () => {
|
|
843
|
-
// Add another widget
|
|
844
|
-
const widget2: CellData = {
|
|
845
|
-
widgetId: WidgetIdUtils.generate(),
|
|
846
|
-
cellId: CellIdUtils.create(10, 10),
|
|
847
|
-
row: 10,
|
|
848
|
-
col: 10,
|
|
849
|
-
rowSpan: 1,
|
|
850
|
-
colSpan: 1,
|
|
851
|
-
widgetFactory: mockWidgetFactory,
|
|
852
|
-
widgetState: {},
|
|
853
|
-
};
|
|
854
|
-
store.addWidget(widget2);
|
|
855
|
-
|
|
856
|
-
const cellId = CellIdUtils.create(5, 5);
|
|
857
|
-
const dragData: DragData = {
|
|
858
|
-
kind: 'cell',
|
|
859
|
-
content: {
|
|
860
|
-
widgetId: WidgetIdUtils.generate(),
|
|
861
|
-
cellId,
|
|
862
|
-
row: 5,
|
|
863
|
-
col: 5,
|
|
864
|
-
rowSpan: 2,
|
|
865
|
-
colSpan: 2,
|
|
866
|
-
}
|
|
867
|
-
};
|
|
868
|
-
store.startDrag(dragData);
|
|
869
|
-
store.setHoveredDropZone({ row: 10, col: 10 }); // Collides with widget2
|
|
870
|
-
|
|
871
|
-
expect(store.isValidPlacement()).toBe(false);
|
|
872
|
-
});
|
|
873
|
-
|
|
874
|
-
it('should update reactively when placement validity changes', () => {
|
|
875
|
-
const dragData: DragData = {
|
|
876
|
-
kind: 'widget',
|
|
877
|
-
content: testWidgetMetadata
|
|
878
|
-
};
|
|
879
|
-
store.startDrag(dragData);
|
|
880
|
-
|
|
881
|
-
store.setHoveredDropZone({ row: 1, col: 1 });
|
|
882
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
883
|
-
|
|
884
|
-
store.setHoveredDropZone({ row: 5, col: 5 });
|
|
885
|
-
expect(store.isValidPlacement()).toBe(false);
|
|
886
|
-
|
|
887
|
-
store.setHoveredDropZone({ row: 8, col: 8 });
|
|
888
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
889
|
-
});
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
describe('computed properties integration', () => {
|
|
893
|
-
it('should maintain consistency between highlight maps and zones', () => {
|
|
894
|
-
const dragData: DragData = {
|
|
895
|
-
kind: 'cell',
|
|
896
|
-
content: {
|
|
897
|
-
widgetId: WidgetIdUtils.generate(),
|
|
898
|
-
cellId: CellIdUtils.create(1, 1),
|
|
899
|
-
row: 1,
|
|
900
|
-
col: 1,
|
|
901
|
-
rowSpan: 2,
|
|
902
|
-
colSpan: 3,
|
|
903
|
-
}
|
|
904
|
-
};
|
|
905
|
-
store.startDrag(dragData);
|
|
906
|
-
store.setHoveredDropZone({ row: 3, col: 4 });
|
|
907
|
-
|
|
908
|
-
const zones = store.highlightedZones();
|
|
909
|
-
const highlightMap = store.highlightMap();
|
|
910
|
-
|
|
911
|
-
expect(zones.length).toBe(highlightMap.size);
|
|
912
|
-
|
|
913
|
-
zones.forEach(zone => {
|
|
914
|
-
expect(highlightMap.has(CellIdUtils.create(zone.row, zone.col))).toBe(true);
|
|
915
|
-
});
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
it('should maintain consistency between resize preview cells and map', () => {
|
|
919
|
-
const cellId = CellIdUtils.create(4, 4);
|
|
920
|
-
const cell: CellData = {
|
|
921
|
-
widgetId: WidgetIdUtils.generate(),
|
|
922
|
-
cellId,
|
|
923
|
-
row: 4,
|
|
924
|
-
col: 4,
|
|
925
|
-
rowSpan: 2,
|
|
926
|
-
colSpan: 3,
|
|
927
|
-
widgetFactory: mockWidgetFactory,
|
|
928
|
-
widgetState: {},
|
|
929
|
-
};
|
|
930
|
-
store.addWidget(cell);
|
|
931
|
-
store.startResize(cellId);
|
|
932
|
-
store.updateResizePreview('horizontal', 1);
|
|
933
|
-
|
|
934
|
-
const previewCells = store.resizePreviewCells();
|
|
935
|
-
const previewMap = store.resizePreviewMap();
|
|
936
|
-
|
|
937
|
-
expect(previewCells.length).toBe(previewMap.size);
|
|
938
|
-
|
|
939
|
-
previewCells.forEach(cell => {
|
|
940
|
-
expect(previewMap.has(CellIdUtils.create(cell.row, cell.col))).toBe(true);
|
|
941
|
-
});
|
|
942
|
-
});
|
|
943
|
-
|
|
944
|
-
it('should handle simultaneous drag and resize operations', () => {
|
|
945
|
-
// Start resize
|
|
946
|
-
const cellId = CellIdUtils.create(3, 3);
|
|
947
|
-
const cell: CellData = {
|
|
948
|
-
widgetId: WidgetIdUtils.generate(),
|
|
949
|
-
cellId,
|
|
950
|
-
row: 3,
|
|
951
|
-
col: 3,
|
|
952
|
-
rowSpan: 1,
|
|
953
|
-
colSpan: 1,
|
|
954
|
-
widgetFactory: mockWidgetFactory,
|
|
955
|
-
widgetState: {},
|
|
956
|
-
};
|
|
957
|
-
store.addWidget(cell);
|
|
958
|
-
store.startResize(cellId);
|
|
959
|
-
|
|
960
|
-
// Start drag
|
|
961
|
-
const dragData: DragData = {
|
|
962
|
-
kind: 'widget',
|
|
963
|
-
content: testWidgetMetadata
|
|
964
|
-
};
|
|
965
|
-
store.startDrag(dragData);
|
|
966
|
-
store.setHoveredDropZone({ row: 8, col: 8 });
|
|
967
|
-
|
|
968
|
-
// Both should work independently
|
|
969
|
-
expect(store.resizePreviewCells().length).toBe(1);
|
|
970
|
-
expect(store.highlightedZones().length).toBe(1);
|
|
971
|
-
expect(store.isValidPlacement()).toBe(true);
|
|
972
|
-
});
|
|
973
|
-
});
|
|
974
|
-
});
|