@dragonworks/ngx-dashboard 20.0.5 → 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 -2178
  77. package/fesm2022/dragonworks-ngx-dashboard.mjs.map +0 -1
  78. package/index.d.ts +0 -678
@@ -0,0 +1,461 @@
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, WidgetMetadata, WidgetFactory } from '../../models';
5
+
6
+ describe('DashboardStore - Widget Management', () => {
7
+ let store: InstanceType<typeof DashboardStore>;
8
+ let testWidgetMetadata: WidgetMetadata;
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
+ testWidgetMetadata = {
27
+ widgetTypeid: 'test-widget',
28
+ name: 'Test Widget',
29
+ description: 'A test widget for unit tests',
30
+ svgIcon: '<svg></svg>'
31
+ };
32
+
33
+ mockWidgetFactory = {
34
+ widgetTypeid: 'test-widget',
35
+ createComponent: jasmine.createSpy()
36
+ } as any;
37
+
38
+ mockDashboardService.getFactory.and.returnValue(mockWidgetFactory);
39
+ });
40
+
41
+ describe('addWidget', () => {
42
+ it('should add a widget to empty grid', () => {
43
+ const cell: CellData = {
44
+ widgetId: WidgetIdUtils.generate(),
45
+ cellId: CellIdUtils.create(5, 5),
46
+ row: 5,
47
+ col: 5,
48
+ rowSpan: 1,
49
+ colSpan: 1,
50
+ widgetFactory: mockWidgetFactory,
51
+ widgetState: {},
52
+ };
53
+
54
+ store.addWidget(cell);
55
+
56
+ expect(store.cells().length).toBe(1);
57
+ expect(store.cells()[0]).toEqual(cell);
58
+ });
59
+
60
+ it('should add multiple widgets', () => {
61
+ const cell1: CellData = {
62
+ widgetId: WidgetIdUtils.generate(),
63
+ cellId: CellIdUtils.create(1, 1),
64
+ row: 1,
65
+ col: 1,
66
+ rowSpan: 1,
67
+ colSpan: 1,
68
+ widgetFactory: mockWidgetFactory,
69
+ widgetState: {},
70
+ };
71
+
72
+ const cell2: CellData = {
73
+ widgetId: WidgetIdUtils.generate(),
74
+ cellId: CellIdUtils.create(3, 3),
75
+ row: 3,
76
+ col: 3,
77
+ rowSpan: 2,
78
+ colSpan: 2,
79
+ widgetFactory: mockWidgetFactory,
80
+ widgetState: {},
81
+ };
82
+
83
+ store.addWidget(cell1);
84
+ store.addWidget(cell2);
85
+
86
+ expect(store.cells().length).toBe(2);
87
+ expect(store.cells()).toContain(cell1);
88
+ expect(store.cells()).toContain(cell2);
89
+ });
90
+
91
+ it('should add widget with custom state', () => {
92
+ const cell: CellData = {
93
+ widgetId: WidgetIdUtils.generate(),
94
+ cellId: CellIdUtils.create(5, 5),
95
+ row: 5,
96
+ col: 5,
97
+ rowSpan: 1,
98
+ colSpan: 1,
99
+ widgetFactory: mockWidgetFactory,
100
+ widgetState: { customProperty: 'test-value' },
101
+ };
102
+
103
+ store.addWidget(cell);
104
+
105
+ expect(store.cells()[0].widgetState).toEqual({ customProperty: 'test-value' });
106
+ });
107
+ });
108
+
109
+ describe('removeWidget', () => {
110
+ it('should remove existing widget', () => {
111
+ const cellId = CellIdUtils.create(5, 5);
112
+ const cell: CellData = {
113
+ widgetId: WidgetIdUtils.generate(),
114
+ cellId,
115
+ row: 5,
116
+ col: 5,
117
+ rowSpan: 1,
118
+ colSpan: 1,
119
+ widgetFactory: mockWidgetFactory,
120
+ widgetState: {},
121
+ };
122
+
123
+ store.addWidget(cell);
124
+ expect(store.cells().length).toBe(1);
125
+
126
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!; store.removeWidget(widget.widgetId);
127
+ expect(store.cells().length).toBe(0);
128
+ });
129
+
130
+ it('should remove specific widget from multiple widgets', () => {
131
+ const cellId1 = CellIdUtils.create(1, 1);
132
+ const cellId2 = CellIdUtils.create(3, 3);
133
+
134
+ const cell1: CellData = {
135
+ widgetId: WidgetIdUtils.generate(),
136
+ cellId: cellId1,
137
+ row: 1,
138
+ col: 1,
139
+ rowSpan: 1,
140
+ colSpan: 1,
141
+ widgetFactory: mockWidgetFactory,
142
+ widgetState: {},
143
+ };
144
+
145
+ const cell2: CellData = {
146
+ widgetId: WidgetIdUtils.generate(),
147
+ cellId: cellId2,
148
+ row: 3,
149
+ col: 3,
150
+ rowSpan: 2,
151
+ colSpan: 2,
152
+ widgetFactory: mockWidgetFactory,
153
+ widgetState: {},
154
+ };
155
+
156
+ store.addWidget(cell1);
157
+ store.addWidget(cell2);
158
+ expect(store.cells().length).toBe(2);
159
+
160
+ const widget1 = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId1))!; store.removeWidget(widget1.widgetId);
161
+ expect(store.cells().length).toBe(1);
162
+ expect(store.cells()[0].cellId).toEqual(cellId2);
163
+ });
164
+
165
+ it('should handle removing non-existent widget gracefully', () => {
166
+ const existingCellId = CellIdUtils.create(5, 5);
167
+ const nonExistentCellId = CellIdUtils.create(10, 10);
168
+
169
+ const cell: CellData = {
170
+ widgetId: WidgetIdUtils.generate(),
171
+ cellId: existingCellId,
172
+ row: 5,
173
+ col: 5,
174
+ rowSpan: 1,
175
+ colSpan: 1,
176
+ widgetFactory: mockWidgetFactory,
177
+ widgetState: {},
178
+ };
179
+
180
+ store.addWidget(cell);
181
+ expect(store.cells().length).toBe(1);
182
+
183
+ store.removeWidget("non-existent-widget-id" as any);
184
+ expect(store.cells().length).toBe(1); // No change
185
+ });
186
+ });
187
+
188
+ describe('updateWidgetPosition', () => {
189
+ it('should update widget position', () => {
190
+ const cellId = CellIdUtils.create(5, 5);
191
+ const cell: CellData = {
192
+ widgetId: WidgetIdUtils.generate(),
193
+ cellId,
194
+ row: 5,
195
+ col: 5,
196
+ rowSpan: 2,
197
+ colSpan: 2,
198
+ widgetFactory: mockWidgetFactory,
199
+ widgetState: {},
200
+ };
201
+
202
+ store.addWidget(cell);
203
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!;
204
+ store.updateWidgetPosition(widget.widgetId, 8, 9);
205
+
206
+ expect(store.cells()[0].row).toBe(8);
207
+ expect(store.cells()[0].col).toBe(9);
208
+ expect(store.cells()[0].rowSpan).toBe(2); // unchanged
209
+ expect(store.cells()[0].colSpan).toBe(2); // unchanged
210
+ });
211
+
212
+ it('should handle updating non-existent widget gracefully', () => {
213
+ const existingCellId = CellIdUtils.create(5, 5);
214
+ const nonExistentCellId = CellIdUtils.create(10, 10);
215
+
216
+ const cell: CellData = {
217
+ widgetId: WidgetIdUtils.generate(),
218
+ cellId: existingCellId,
219
+ row: 5,
220
+ col: 5,
221
+ rowSpan: 1,
222
+ colSpan: 1,
223
+ widgetFactory: mockWidgetFactory,
224
+ widgetState: {},
225
+ };
226
+
227
+ store.addWidget(cell);
228
+ store.updateWidgetPosition("non-existent-widget-id" as any, 8, 9);
229
+
230
+ // Original widget should be unchanged
231
+ expect(store.cells()[0].row).toBe(5);
232
+ expect(store.cells()[0].col).toBe(5);
233
+ });
234
+ });
235
+
236
+ describe('createWidget', () => {
237
+ it('should create widget with default size', () => {
238
+ store.createWidget(7, 8, mockWidgetFactory);
239
+
240
+ expect(store.cells().length).toBe(1);
241
+ expect(store.cells()[0].row).toBe(7);
242
+ expect(store.cells()[0].col).toBe(8);
243
+ expect(store.cells()[0].rowSpan).toBe(1);
244
+ expect(store.cells()[0].colSpan).toBe(1);
245
+ expect(store.cells()[0].widgetFactory).toBe(mockWidgetFactory);
246
+ });
247
+
248
+ it('should create widget with custom state', () => {
249
+ const customState = { color: 'red', size: 'large' };
250
+ store.createWidget(7, 8, mockWidgetFactory, customState as any);
251
+
252
+ expect(store.cells()[0].widgetState).toBe(customState);
253
+ });
254
+
255
+ it('should create widget with undefined state', () => {
256
+ store.createWidget(7, 8, mockWidgetFactory, undefined);
257
+
258
+ expect(store.cells()[0].widgetState).toBeUndefined();
259
+ });
260
+
261
+ it('should generate correct cellId', () => {
262
+ store.createWidget(12, 15, mockWidgetFactory);
263
+
264
+ const expectedCellId = CellIdUtils.create(12, 15);
265
+ expect(store.cells()[0].cellId).toEqual(expectedCellId);
266
+ });
267
+ });
268
+
269
+ describe('updateCellSettings', () => {
270
+ it('should update flat property to true', () => {
271
+ const cellId = CellIdUtils.create(5, 5);
272
+ const cell: CellData = {
273
+ widgetId: WidgetIdUtils.generate(),
274
+ cellId,
275
+ row: 5,
276
+ col: 5,
277
+ rowSpan: 1,
278
+ colSpan: 1,
279
+ widgetFactory: mockWidgetFactory,
280
+ widgetState: {},
281
+ flat: false,
282
+ };
283
+
284
+ store.addWidget(cell);
285
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!;
286
+ store.updateCellSettings(widget.widgetId, true);
287
+
288
+ expect(store.cells()[0].flat).toBe(true);
289
+ });
290
+
291
+ it('should update flat property to false', () => {
292
+ const cellId = CellIdUtils.create(5, 5);
293
+ const cell: CellData = {
294
+ widgetId: WidgetIdUtils.generate(),
295
+ cellId,
296
+ row: 5,
297
+ col: 5,
298
+ rowSpan: 1,
299
+ colSpan: 1,
300
+ widgetFactory: mockWidgetFactory,
301
+ widgetState: {},
302
+ flat: true,
303
+ };
304
+
305
+ store.addWidget(cell);
306
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!;
307
+ store.updateCellSettings(widget.widgetId, false);
308
+
309
+ expect(store.cells()[0].flat).toBe(false);
310
+ });
311
+
312
+ it('should handle updating non-existent cell gracefully', () => {
313
+ const existingCellId = CellIdUtils.create(5, 5);
314
+ const nonExistentCellId = CellIdUtils.create(10, 10);
315
+
316
+ const cell: CellData = {
317
+ widgetId: WidgetIdUtils.generate(),
318
+ cellId: existingCellId,
319
+ row: 5,
320
+ col: 5,
321
+ rowSpan: 1,
322
+ colSpan: 1,
323
+ widgetFactory: mockWidgetFactory,
324
+ widgetState: {},
325
+ flat: false,
326
+ };
327
+
328
+ store.addWidget(cell);
329
+ store.updateCellSettings("non-existent-widget-id" as any, true);
330
+
331
+ // Original cell should be unchanged
332
+ expect(store.cells()[0].flat).toBe(false);
333
+ });
334
+ });
335
+
336
+ describe('updateWidgetSpan', () => {
337
+ it('should update widget span', () => {
338
+ const cellId = CellIdUtils.create(5, 5);
339
+ const cell: CellData = {
340
+ widgetId: WidgetIdUtils.generate(),
341
+ cellId,
342
+ row: 5,
343
+ col: 5,
344
+ rowSpan: 1,
345
+ colSpan: 1,
346
+ widgetFactory: mockWidgetFactory,
347
+ widgetState: {},
348
+ };
349
+
350
+ store.addWidget(cell);
351
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!;
352
+ store.updateWidgetSpan(widget.widgetId, 3, 4);
353
+
354
+ expect(store.cells()[0].rowSpan).toBe(3);
355
+ expect(store.cells()[0].colSpan).toBe(4);
356
+ expect(store.cells()[0].row).toBe(5); // unchanged
357
+ expect(store.cells()[0].col).toBe(5); // unchanged
358
+ });
359
+
360
+ it('should handle minimum span values', () => {
361
+ const cellId = CellIdUtils.create(5, 5);
362
+ const cell: CellData = {
363
+ widgetId: WidgetIdUtils.generate(),
364
+ cellId,
365
+ row: 5,
366
+ col: 5,
367
+ rowSpan: 3,
368
+ colSpan: 3,
369
+ widgetFactory: mockWidgetFactory,
370
+ widgetState: {},
371
+ };
372
+
373
+ store.addWidget(cell);
374
+ const widget = store.cells().find(c => CellIdUtils.equals(c.cellId, cellId))!;
375
+ store.updateWidgetSpan(widget.widgetId, 1, 1);
376
+
377
+ expect(store.cells()[0].rowSpan).toBe(1);
378
+ expect(store.cells()[0].colSpan).toBe(1);
379
+ });
380
+
381
+ it('should handle updating non-existent widget gracefully', () => {
382
+ const existingCellId = CellIdUtils.create(5, 5);
383
+ const nonExistentCellId = CellIdUtils.create(10, 10);
384
+
385
+ const cell: CellData = {
386
+ widgetId: WidgetIdUtils.generate(),
387
+ cellId: existingCellId,
388
+ row: 5,
389
+ col: 5,
390
+ rowSpan: 2,
391
+ colSpan: 2,
392
+ widgetFactory: mockWidgetFactory,
393
+ widgetState: {},
394
+ };
395
+
396
+ store.addWidget(cell);
397
+ store.updateWidgetSpan("non-existent-widget-id" as any, 4, 4);
398
+
399
+ // Original widget should be unchanged
400
+ expect(store.cells()[0].rowSpan).toBe(2);
401
+ expect(store.cells()[0].colSpan).toBe(2);
402
+ });
403
+ });
404
+
405
+ describe('clearDashboard', () => {
406
+ it('should clear empty dashboard', () => {
407
+ store.clearDashboard();
408
+ expect(store.cells()).toEqual([]);
409
+ });
410
+
411
+ it('should clear dashboard with single widget', () => {
412
+ const cell: CellData = {
413
+ widgetId: WidgetIdUtils.generate(),
414
+ cellId: CellIdUtils.create(5, 5),
415
+ row: 5,
416
+ col: 5,
417
+ rowSpan: 1,
418
+ colSpan: 1,
419
+ widgetFactory: mockWidgetFactory,
420
+ widgetState: {},
421
+ };
422
+
423
+ store.addWidget(cell);
424
+ expect(store.cells().length).toBe(1);
425
+
426
+ store.clearDashboard();
427
+ expect(store.cells()).toEqual([]);
428
+ });
429
+
430
+ it('should clear dashboard with multiple widgets', () => {
431
+ const cell1: CellData = {
432
+ widgetId: WidgetIdUtils.generate(),
433
+ cellId: CellIdUtils.create(1, 1),
434
+ row: 1,
435
+ col: 1,
436
+ rowSpan: 1,
437
+ colSpan: 1,
438
+ widgetFactory: mockWidgetFactory,
439
+ widgetState: {},
440
+ };
441
+
442
+ const cell2: CellData = {
443
+ widgetId: WidgetIdUtils.generate(),
444
+ cellId: CellIdUtils.create(5, 5),
445
+ row: 5,
446
+ col: 5,
447
+ rowSpan: 2,
448
+ colSpan: 2,
449
+ widgetFactory: mockWidgetFactory,
450
+ widgetState: {},
451
+ };
452
+
453
+ store.addWidget(cell1);
454
+ store.addWidget(cell2);
455
+ expect(store.cells().length).toBe(2);
456
+
457
+ store.clearDashboard();
458
+ expect(store.cells()).toEqual([]);
459
+ });
460
+ });
461
+ });