@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,128 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { DashboardService } from '../../services/dashboard.service';
3
+ import { DashboardStore } from '../dashboard-store';
4
+
5
+ describe('DashboardStore - Grid Configuration', () => {
6
+ let store: InstanceType<typeof DashboardStore>;
7
+ let mockDashboardService: jasmine.SpyObj<DashboardService>;
8
+
9
+ beforeEach(() => {
10
+ const dashboardServiceSpy = jasmine.createSpyObj('DashboardService', ['getFactory']);
11
+
12
+ TestBed.configureTestingModule({
13
+ providers: [
14
+ DashboardStore,
15
+ { provide: DashboardService, useValue: dashboardServiceSpy }
16
+ ]
17
+ });
18
+
19
+ store = TestBed.inject(DashboardStore);
20
+ mockDashboardService = TestBed.inject(DashboardService) as jasmine.SpyObj<DashboardService>;
21
+ store.setGridConfig({ rows: 8, columns: 16 });
22
+ });
23
+
24
+ describe('setGridConfig', () => {
25
+ it('should update rows only', () => {
26
+ store.setGridConfig({ rows: 12 });
27
+ expect(store.rows()).toBe(12);
28
+ expect(store.columns()).toBe(16); // unchanged
29
+ expect(store.gutterSize()).toBe('0.5em'); // unchanged
30
+ });
31
+
32
+ it('should update columns only', () => {
33
+ store.setGridConfig({ columns: 20 });
34
+ expect(store.columns()).toBe(20);
35
+ expect(store.rows()).toBe(8); // unchanged
36
+ });
37
+
38
+ it('should update gutterSize only', () => {
39
+ store.setGridConfig({ gutterSize: '1em' });
40
+ expect(store.gutterSize()).toBe('1em');
41
+ expect(store.rows()).toBe(8); // unchanged
42
+ expect(store.columns()).toBe(16); // unchanged
43
+ });
44
+
45
+ it('should update multiple properties at once', () => {
46
+ store.setGridConfig({ rows: 10, columns: 24, gutterSize: '2rem' });
47
+ expect(store.rows()).toBe(10);
48
+ expect(store.columns()).toBe(24);
49
+ expect(store.gutterSize()).toBe('2rem');
50
+ });
51
+
52
+ it('should handle edge case values', () => {
53
+ store.setGridConfig({ rows: 1, columns: 1, gutterSize: '0px' });
54
+ expect(store.rows()).toBe(1);
55
+ expect(store.columns()).toBe(1);
56
+ expect(store.gutterSize()).toBe('0px');
57
+ });
58
+ });
59
+
60
+ describe('setGridCellDimensions', () => {
61
+ it('should update grid cell dimensions', () => {
62
+ store.setGridCellDimensions(100, 50);
63
+ expect(store.gridCellDimensions()).toEqual({ width: 100, height: 50 });
64
+ });
65
+
66
+ it('should handle zero dimensions', () => {
67
+ store.setGridCellDimensions(0, 0);
68
+ expect(store.gridCellDimensions()).toEqual({ width: 0, height: 0 });
69
+ });
70
+
71
+ it('should handle large dimensions', () => {
72
+ store.setGridCellDimensions(9999, 9999);
73
+ expect(store.gridCellDimensions()).toEqual({ width: 9999, height: 9999 });
74
+ });
75
+
76
+ it('should handle fractional dimensions', () => {
77
+ store.setGridCellDimensions(100.5, 75.25);
78
+ expect(store.gridCellDimensions()).toEqual({ width: 100.5, height: 75.25 });
79
+ });
80
+ });
81
+
82
+ describe('toggleEditMode', () => {
83
+ it('should toggle from false to true', () => {
84
+ expect(store.isEditMode()).toBe(false);
85
+ store.toggleEditMode();
86
+ expect(store.isEditMode()).toBe(true);
87
+ });
88
+
89
+ it('should toggle from true to false', () => {
90
+ store.setEditMode(true);
91
+ expect(store.isEditMode()).toBe(true);
92
+ store.toggleEditMode();
93
+ expect(store.isEditMode()).toBe(false);
94
+ });
95
+
96
+ it('should toggle multiple times', () => {
97
+ expect(store.isEditMode()).toBe(false);
98
+ store.toggleEditMode();
99
+ expect(store.isEditMode()).toBe(true);
100
+ store.toggleEditMode();
101
+ expect(store.isEditMode()).toBe(false);
102
+ store.toggleEditMode();
103
+ expect(store.isEditMode()).toBe(true);
104
+ });
105
+ });
106
+
107
+ describe('setEditMode', () => {
108
+ it('should set edit mode to true', () => {
109
+ store.setEditMode(true);
110
+ expect(store.isEditMode()).toBe(true);
111
+ });
112
+
113
+ it('should set edit mode to false', () => {
114
+ store.setEditMode(false);
115
+ expect(store.isEditMode()).toBe(false);
116
+ });
117
+
118
+ it('should be idempotent', () => {
119
+ store.setEditMode(true);
120
+ store.setEditMode(true);
121
+ expect(store.isEditMode()).toBe(true);
122
+
123
+ store.setEditMode(false);
124
+ store.setEditMode(false);
125
+ expect(store.isEditMode()).toBe(false);
126
+ });
127
+ });
128
+ });
@@ -0,0 +1,229 @@
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 - Grid Query Methods', () => {
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: 10, columns: 10 });
25
+
26
+ mockWidgetFactory = {
27
+ widgetTypeid: 'test-widget',
28
+ createComponent: jasmine.createSpy()
29
+ } as any;
30
+ });
31
+
32
+ describe('isCellOccupied', () => {
33
+ it('should return false for empty grid', () => {
34
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 5)).toBe(false);
35
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 1, 1)).toBe(false);
36
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 10, 10)).toBe(false);
37
+ });
38
+
39
+ it('should return true for occupied single cell', () => {
40
+ const cell: CellData = {
41
+ widgetId: WidgetIdUtils.generate(),
42
+ cellId: CellIdUtils.create(5, 5),
43
+ row: 5,
44
+ col: 5,
45
+ rowSpan: 1,
46
+ colSpan: 1,
47
+ widgetFactory: mockWidgetFactory,
48
+ widgetState: {},
49
+ };
50
+ store.addWidget(cell);
51
+
52
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 5)).toBe(true);
53
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 4, 5)).toBe(false);
54
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 4)).toBe(false);
55
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 6, 5)).toBe(false);
56
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 6)).toBe(false);
57
+ });
58
+
59
+ it('should return true for cells within multi-cell widget', () => {
60
+ const cell: CellData = {
61
+ widgetId: WidgetIdUtils.generate(),
62
+ cellId: CellIdUtils.create(3, 4),
63
+ row: 3,
64
+ col: 4,
65
+ rowSpan: 3,
66
+ colSpan: 2,
67
+ widgetFactory: mockWidgetFactory,
68
+ widgetState: {},
69
+ };
70
+ store.addWidget(cell);
71
+
72
+ // Check all cells within the widget (3,4) to (5,5)
73
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 3, 4)).toBe(true);
74
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 3, 5)).toBe(true);
75
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 4, 4)).toBe(true);
76
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 4, 5)).toBe(true);
77
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 4)).toBe(true);
78
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 5)).toBe(true);
79
+
80
+ // Check boundary cells
81
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 2, 4)).toBe(false);
82
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 3, 3)).toBe(false);
83
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 6, 4)).toBe(false);
84
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 3, 6)).toBe(false);
85
+ });
86
+
87
+ it('should exclude specified widget from occupation check', () => {
88
+ const cellId = CellIdUtils.create(5, 5);
89
+ const widgetId = WidgetIdUtils.generate();
90
+ const cell: CellData = {
91
+ widgetId,
92
+ cellId,
93
+ row: 5,
94
+ col: 5,
95
+ rowSpan: 2,
96
+ colSpan: 2,
97
+ widgetFactory: mockWidgetFactory,
98
+ widgetState: {},
99
+ };
100
+ store.addWidget(cell);
101
+
102
+ // Without exclusion
103
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 5)).toBe(true);
104
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 6, 6)).toBe(true);
105
+
106
+ // With exclusion (now using widgetId instead of cellId)
107
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 5, 5, widgetId)).toBe(false);
108
+ expect(GridQueryInternalUtils.isCellOccupied(store.cells(), 6, 6, widgetId)).toBe(false);
109
+ });
110
+ });
111
+
112
+ describe('isOutOfBounds', () => {
113
+ it('should return false for valid single cell placement', () => {
114
+ expect(GridQueryInternalUtils.isOutOfBounds(1, 1, 1, 1, 10, 10)).toBe(false);
115
+ expect(GridQueryInternalUtils.isOutOfBounds(5, 5, 1, 1, 10, 10)).toBe(false);
116
+ expect(GridQueryInternalUtils.isOutOfBounds(10, 10, 1, 1, 10, 10)).toBe(false);
117
+ });
118
+
119
+ it('should return false for valid multi-cell placement', () => {
120
+ expect(GridQueryInternalUtils.isOutOfBounds(1, 1, 3, 3, 10, 10)).toBe(false);
121
+ expect(GridQueryInternalUtils.isOutOfBounds(8, 8, 3, 3, 10, 10)).toBe(false);
122
+ expect(GridQueryInternalUtils.isOutOfBounds(1, 8, 3, 3, 10, 10)).toBe(false);
123
+ expect(GridQueryInternalUtils.isOutOfBounds(8, 1, 3, 3, 10, 10)).toBe(false);
124
+ });
125
+
126
+ it('should return true for row out of bounds', () => {
127
+ expect(GridQueryInternalUtils.isOutOfBounds(11, 5, 1, 1, 10, 10)).toBe(true);
128
+ expect(GridQueryInternalUtils.isOutOfBounds(9, 5, 3, 1, 10, 10)).toBe(true); // Spans to row 11
129
+ expect(GridQueryInternalUtils.isOutOfBounds(10, 5, 2, 1, 10, 10)).toBe(true); // Spans to row 11
130
+ });
131
+
132
+ it('should return true for column out of bounds', () => {
133
+ expect(GridQueryInternalUtils.isOutOfBounds(5, 11, 1, 1, 10, 10)).toBe(true);
134
+ expect(GridQueryInternalUtils.isOutOfBounds(5, 9, 1, 3, 10, 10)).toBe(true); // Spans to col 11
135
+ expect(GridQueryInternalUtils.isOutOfBounds(5, 10, 1, 2, 10, 10)).toBe(true); // Spans to col 11
136
+ });
137
+
138
+ it('should return true for both row and column out of bounds', () => {
139
+ expect(GridQueryInternalUtils.isOutOfBounds(11, 11, 1, 1, 10, 10)).toBe(true);
140
+ expect(GridQueryInternalUtils.isOutOfBounds(9, 9, 3, 3, 10, 10)).toBe(true); // Spans to (11,11)
141
+ });
142
+
143
+ it('should handle edge cases at exact boundaries', () => {
144
+ expect(GridQueryInternalUtils.isOutOfBounds(10, 10, 1, 1, 10, 10)).toBe(false); // Exactly at boundary
145
+ expect(GridQueryInternalUtils.isOutOfBounds(8, 8, 3, 3, 10, 10)).toBe(false); // Spans exactly to boundary
146
+ expect(GridQueryInternalUtils.isOutOfBounds(9, 9, 2, 2, 10, 10)).toBe(false); // Spans exactly to boundary
147
+ });
148
+ });
149
+
150
+ describe('getCellAt', () => {
151
+ it('should return null for empty grid', () => {
152
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 5, 5)).toBeNull();
153
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 1, 1)).toBeNull();
154
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 10, 10)).toBeNull();
155
+ });
156
+
157
+ it('should return cell at exact position', () => {
158
+ const cell: CellData = {
159
+ widgetId: WidgetIdUtils.generate(),
160
+ cellId: CellIdUtils.create(3, 7),
161
+ row: 3,
162
+ col: 7,
163
+ rowSpan: 1,
164
+ colSpan: 1,
165
+ widgetFactory: mockWidgetFactory,
166
+ widgetState: {},
167
+ };
168
+ store.addWidget(cell);
169
+
170
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 3, 7)).toBe(cell);
171
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 3, 6)).toBeNull();
172
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 4, 7)).toBeNull();
173
+ });
174
+
175
+ it('should return null for cells within multi-cell widget but not at origin', () => {
176
+ const cell: CellData = {
177
+ widgetId: WidgetIdUtils.generate(),
178
+ cellId: CellIdUtils.create(5, 5),
179
+ row: 5,
180
+ col: 5,
181
+ rowSpan: 3,
182
+ colSpan: 3,
183
+ widgetFactory: mockWidgetFactory,
184
+ widgetState: {},
185
+ };
186
+ store.addWidget(cell);
187
+
188
+ // Only origin position should return the cell
189
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 5, 5)).toBe(cell);
190
+
191
+ // Other positions within the widget should return null
192
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 5, 6)).toBeNull();
193
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 6, 5)).toBeNull();
194
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 7, 7)).toBeNull();
195
+ });
196
+
197
+ it('should handle multiple widgets correctly', () => {
198
+ const cell1: CellData = {
199
+ widgetId: WidgetIdUtils.generate(),
200
+ cellId: CellIdUtils.create(2, 2),
201
+ row: 2,
202
+ col: 2,
203
+ rowSpan: 1,
204
+ colSpan: 1,
205
+ widgetFactory: mockWidgetFactory,
206
+ widgetState: {},
207
+ };
208
+
209
+ const cell2: CellData = {
210
+ widgetId: WidgetIdUtils.generate(),
211
+ cellId: CellIdUtils.create(8, 8),
212
+ row: 8,
213
+ col: 8,
214
+ rowSpan: 2,
215
+ colSpan: 2,
216
+ widgetFactory: mockWidgetFactory,
217
+ widgetState: {},
218
+ };
219
+
220
+ store.addWidget(cell1);
221
+ store.addWidget(cell2);
222
+
223
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 2, 2)).toBe(cell1);
224
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 8, 8)).toBe(cell2);
225
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 5, 5)).toBeNull();
226
+ expect(GridQueryInternalUtils.getCellAt(store.cells(), 8, 9)).toBeNull(); // Within cell2 but not at origin
227
+ });
228
+ });
229
+ });