@dragonworks/ngx-dashboard-widgets 20.0.4 → 20.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/ng-package.json +7 -0
  2. package/package.json +31 -42
  3. package/src/lib/arrow-widget/arrow-state-dialog.component.ts +187 -0
  4. package/src/lib/arrow-widget/arrow-widget.component.html +9 -0
  5. package/src/lib/arrow-widget/arrow-widget.component.scss +52 -0
  6. package/src/lib/arrow-widget/arrow-widget.component.ts +78 -0
  7. package/src/lib/arrow-widget/arrow-widget.metadata.ts +3 -0
  8. package/src/lib/clock-widget/analog-clock/analog-clock.component.html +66 -0
  9. package/src/lib/clock-widget/analog-clock/analog-clock.component.scss +103 -0
  10. package/src/lib/clock-widget/analog-clock/analog-clock.component.ts +120 -0
  11. package/src/lib/clock-widget/clock-state-dialog.component.ts +170 -0
  12. package/src/lib/clock-widget/clock-widget.component.html +16 -0
  13. package/src/lib/clock-widget/clock-widget.component.scss +160 -0
  14. package/src/lib/clock-widget/clock-widget.component.ts +87 -0
  15. package/src/lib/clock-widget/clock-widget.metadata.ts +42 -0
  16. package/src/lib/clock-widget/digital-clock/__tests__/digital-clock.component.spec.ts +276 -0
  17. package/src/lib/clock-widget/digital-clock/digital-clock.component.html +1 -0
  18. package/src/lib/clock-widget/digital-clock/digital-clock.component.scss +43 -0
  19. package/src/lib/clock-widget/digital-clock/digital-clock.component.ts +105 -0
  20. package/src/lib/directives/__tests__/responsive-text.directive.spec.ts +906 -0
  21. package/src/lib/directives/responsive-text.directive.ts +334 -0
  22. package/src/lib/label-widget/__tests__/label-widget.component.spec.ts +539 -0
  23. package/src/lib/label-widget/label-state-dialog.component.ts +385 -0
  24. package/src/lib/label-widget/label-widget.component.html +21 -0
  25. package/src/lib/label-widget/label-widget.component.scss +112 -0
  26. package/src/lib/label-widget/label-widget.component.ts +96 -0
  27. package/src/lib/label-widget/label-widget.metadata.ts +3 -0
  28. package/src/public-api.ts +7 -0
  29. package/tsconfig.lib.json +15 -0
  30. package/tsconfig.lib.prod.json +11 -0
  31. package/tsconfig.spec.json +14 -0
  32. package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs +0 -1255
  33. package/fesm2022/dragonworks-ngx-dashboard-widgets.mjs.map +0 -1
  34. package/index.d.ts +0 -147
@@ -0,0 +1,539 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { LabelWidgetComponent, LabelWidgetState } from '../label-widget.component';
3
+
4
+ describe('LabelWidgetComponent', () => {
5
+ let component: LabelWidgetComponent;
6
+ let fixture: ComponentFixture<LabelWidgetComponent>;
7
+
8
+ beforeEach(async () => {
9
+ await TestBed.configureTestingModule({
10
+ imports: [LabelWidgetComponent]
11
+ })
12
+ .compileComponents();
13
+
14
+ fixture = TestBed.createComponent(LabelWidgetComponent);
15
+ component = fixture.componentInstance;
16
+ });
17
+
18
+ afterEach(() => {
19
+ fixture.destroy();
20
+ });
21
+
22
+ it('should create', () => {
23
+ expect(component).toBeTruthy();
24
+ });
25
+
26
+ describe('Default State Serialization', () => {
27
+ it('should return complete default state with correct min/max font sizes', () => {
28
+ fixture.detectChanges();
29
+
30
+ const state = component.dashboardGetState();
31
+
32
+ expect(state).toEqual({
33
+ label: '',
34
+ fontSize: 16,
35
+ alignment: 'center',
36
+ fontWeight: 'normal',
37
+ opacity: 1,
38
+ hasBackground: true,
39
+ responsive: false,
40
+ minFontSize: 8,
41
+ maxFontSize: 64
42
+ });
43
+ });
44
+
45
+ it('should have all required properties in default state', () => {
46
+ fixture.detectChanges();
47
+
48
+ const state = component.dashboardGetState();
49
+
50
+ expect(state.label).toBeDefined();
51
+ expect(state.fontSize).toBeDefined();
52
+ expect(state.alignment).toBeDefined();
53
+ expect(state.fontWeight).toBeDefined();
54
+ expect(state.opacity).toBeDefined();
55
+ expect(state.hasBackground).toBeDefined();
56
+ expect(state.responsive).toBeDefined();
57
+ expect(state.minFontSize).toBeDefined();
58
+ expect(state.maxFontSize).toBeDefined();
59
+ });
60
+
61
+ it('should use correct default values for new font size properties', () => {
62
+ fixture.detectChanges();
63
+
64
+ const state = component.dashboardGetState();
65
+
66
+ expect(state.minFontSize).toBe(8);
67
+ expect(state.maxFontSize).toBe(64);
68
+ });
69
+ });
70
+
71
+ describe('State Deserialization', () => {
72
+ it('should accept complete state object and preserve all properties', () => {
73
+ const inputState: LabelWidgetState = {
74
+ label: 'Test Label',
75
+ fontSize: 20,
76
+ alignment: 'left',
77
+ fontWeight: 'bold',
78
+ opacity: 0.8,
79
+ hasBackground: false,
80
+ responsive: true,
81
+ minFontSize: 10,
82
+ maxFontSize: 48
83
+ };
84
+
85
+ component.dashboardSetState(inputState);
86
+ fixture.detectChanges();
87
+
88
+ const retrievedState = component.dashboardGetState();
89
+ expect(retrievedState).toEqual(inputState);
90
+ });
91
+
92
+ it('should handle partial state objects and use defaults for missing properties', () => {
93
+ const partialState = {
94
+ label: 'Partial State Test',
95
+ responsive: true
96
+ };
97
+
98
+ component.dashboardSetState(partialState);
99
+ fixture.detectChanges();
100
+
101
+ const state = component.dashboardGetState();
102
+
103
+ expect(state.label).toBe('Partial State Test');
104
+ expect(state.responsive).toBe(true);
105
+ expect(state.fontSize).toBe(16); // default
106
+ expect(state.alignment).toBe('center'); // default
107
+ expect(state.fontWeight).toBe('normal'); // default
108
+ expect(state.opacity).toBe(1); // default
109
+ expect(state.hasBackground).toBe(true); // default
110
+ expect(state.minFontSize).toBe(8); // default
111
+ expect(state.maxFontSize).toBe(64); // default
112
+ });
113
+
114
+ it('should handle undefined state input gracefully', () => {
115
+ component.dashboardSetState(undefined);
116
+ fixture.detectChanges();
117
+
118
+ const state = component.dashboardGetState();
119
+
120
+ // Should return default state
121
+ expect(state).toEqual({
122
+ label: '',
123
+ fontSize: 16,
124
+ alignment: 'center',
125
+ fontWeight: 'normal',
126
+ opacity: 1,
127
+ hasBackground: true,
128
+ responsive: false,
129
+ minFontSize: 8,
130
+ maxFontSize: 64
131
+ });
132
+ });
133
+
134
+ it('should handle null state input gracefully', () => {
135
+ component.dashboardSetState(null);
136
+ fixture.detectChanges();
137
+
138
+ const state = component.dashboardGetState();
139
+
140
+ // Should return default state
141
+ expect(state).toEqual({
142
+ label: '',
143
+ fontSize: 16,
144
+ alignment: 'center',
145
+ fontWeight: 'normal',
146
+ opacity: 1,
147
+ hasBackground: true,
148
+ responsive: false,
149
+ minFontSize: 8,
150
+ maxFontSize: 64
151
+ });
152
+ });
153
+ });
154
+
155
+ describe('Responsive Mode State Preservation', () => {
156
+ it('should preserve min/max font sizes when responsive is true', () => {
157
+ const responsiveState: LabelWidgetState = {
158
+ label: 'Responsive Label',
159
+ fontSize: 18,
160
+ alignment: 'center',
161
+ fontWeight: 'normal',
162
+ opacity: 1,
163
+ hasBackground: true,
164
+ responsive: true,
165
+ minFontSize: 12,
166
+ maxFontSize: 72
167
+ };
168
+
169
+ component.dashboardSetState(responsiveState);
170
+ fixture.detectChanges();
171
+
172
+ const state = component.dashboardGetState();
173
+
174
+ expect(state.responsive).toBe(true);
175
+ expect(state.minFontSize).toBe(12);
176
+ expect(state.maxFontSize).toBe(72);
177
+ expect(state.fontSize).toBe(18); // Should still be preserved even when responsive
178
+ });
179
+
180
+ it('should preserve min/max font sizes when responsive is false', () => {
181
+ const nonResponsiveState: LabelWidgetState = {
182
+ label: 'Non-Responsive Label',
183
+ fontSize: 24,
184
+ alignment: 'right',
185
+ fontWeight: 'bold',
186
+ opacity: 0.9,
187
+ hasBackground: false,
188
+ responsive: false,
189
+ minFontSize: 16,
190
+ maxFontSize: 96
191
+ };
192
+
193
+ component.dashboardSetState(nonResponsiveState);
194
+ fixture.detectChanges();
195
+
196
+ const state = component.dashboardGetState();
197
+
198
+ expect(state.responsive).toBe(false);
199
+ expect(state.minFontSize).toBe(16);
200
+ expect(state.maxFontSize).toBe(96);
201
+ expect(state.fontSize).toBe(24);
202
+ });
203
+
204
+ it('should preserve custom min/max values across responsive mode changes', () => {
205
+ // Start with responsive mode
206
+ component.dashboardSetState({
207
+ label: 'Toggle Test',
208
+ responsive: true,
209
+ minFontSize: 14,
210
+ maxFontSize: 80
211
+ });
212
+ fixture.detectChanges();
213
+
214
+ let state = component.dashboardGetState();
215
+ expect(state.responsive).toBe(true);
216
+ expect(state.minFontSize).toBe(14);
217
+ expect(state.maxFontSize).toBe(80);
218
+
219
+ // Switch to non-responsive mode
220
+ component.dashboardSetState({
221
+ ...state,
222
+ responsive: false
223
+ });
224
+ fixture.detectChanges();
225
+
226
+ state = component.dashboardGetState();
227
+ expect(state.responsive).toBe(false);
228
+ expect(state.minFontSize).toBe(14); // Should be preserved
229
+ expect(state.maxFontSize).toBe(80); // Should be preserved
230
+ });
231
+ });
232
+
233
+ describe('Backward Compatibility', () => {
234
+ it('should handle legacy state format without min/max font size properties', () => {
235
+ const legacyState = {
236
+ label: 'Legacy Widget',
237
+ fontSize: 18,
238
+ alignment: 'left' as const,
239
+ fontWeight: 'bold' as const,
240
+ opacity: 0.7,
241
+ hasBackground: true,
242
+ responsive: true
243
+ // Note: minFontSize and maxFontSize are intentionally missing
244
+ };
245
+
246
+ component.dashboardSetState(legacyState);
247
+ fixture.detectChanges();
248
+
249
+ const state = component.dashboardGetState();
250
+
251
+ // Legacy properties should be preserved
252
+ expect(state.label).toBe('Legacy Widget');
253
+ expect(state.fontSize).toBe(18);
254
+ expect(state.alignment).toBe('left');
255
+ expect(state.fontWeight).toBe('bold');
256
+ expect(state.opacity).toBe(0.7);
257
+ expect(state.hasBackground).toBe(true);
258
+ expect(state.responsive).toBe(true);
259
+
260
+ // Missing properties should get default values
261
+ expect(state.minFontSize).toBe(8);
262
+ expect(state.maxFontSize).toBe(64);
263
+ });
264
+
265
+ it('should handle very old state format with minimal properties', () => {
266
+ const minimalState = {
267
+ label: 'Old Widget'
268
+ };
269
+
270
+ component.dashboardSetState(minimalState);
271
+ fixture.detectChanges();
272
+
273
+ const state = component.dashboardGetState();
274
+
275
+ expect(state.label).toBe('Old Widget');
276
+ expect(state.fontSize).toBe(16);
277
+ expect(state.alignment).toBe('center');
278
+ expect(state.fontWeight).toBe('normal');
279
+ expect(state.opacity).toBe(1);
280
+ expect(state.hasBackground).toBe(true);
281
+ expect(state.responsive).toBe(false);
282
+ expect(state.minFontSize).toBe(8);
283
+ expect(state.maxFontSize).toBe(64);
284
+ });
285
+
286
+ it('should upgrade legacy responsive state correctly', () => {
287
+ const legacyResponsiveState = {
288
+ label: 'Legacy Responsive',
289
+ responsive: true
290
+ // Missing min/max properties
291
+ };
292
+
293
+ component.dashboardSetState(legacyResponsiveState);
294
+ fixture.detectChanges();
295
+
296
+ const state = component.dashboardGetState();
297
+
298
+ expect(state.responsive).toBe(true);
299
+ expect(state.minFontSize).toBe(8); // Should get defaults
300
+ expect(state.maxFontSize).toBe(64); // Should get defaults
301
+ });
302
+ });
303
+
304
+ describe('State Roundtrip Consistency', () => {
305
+ it('should maintain state consistency through multiple set/get cycles', () => {
306
+ const originalState: LabelWidgetState = {
307
+ label: 'Roundtrip Test',
308
+ fontSize: 22,
309
+ alignment: 'right',
310
+ fontWeight: 'bold',
311
+ opacity: 0.85,
312
+ hasBackground: false,
313
+ responsive: true,
314
+ minFontSize: 10,
315
+ maxFontSize: 100
316
+ };
317
+
318
+ // First roundtrip
319
+ component.dashboardSetState(originalState);
320
+ fixture.detectChanges();
321
+ const firstRetrieved = component.dashboardGetState();
322
+
323
+ // Second roundtrip
324
+ component.dashboardSetState(firstRetrieved);
325
+ fixture.detectChanges();
326
+ const secondRetrieved = component.dashboardGetState();
327
+
328
+ // Third roundtrip
329
+ component.dashboardSetState(secondRetrieved);
330
+ fixture.detectChanges();
331
+ const thirdRetrieved = component.dashboardGetState();
332
+
333
+ // All states should be identical
334
+ expect(firstRetrieved).toEqual(originalState);
335
+ expect(secondRetrieved).toEqual(originalState);
336
+ expect(thirdRetrieved).toEqual(originalState);
337
+ });
338
+
339
+ it('should handle edge case font size values consistently', () => {
340
+ const edgeCaseState: LabelWidgetState = {
341
+ label: 'Edge Cases',
342
+ fontSize: 8,
343
+ alignment: 'center',
344
+ fontWeight: 'normal',
345
+ opacity: 0.1,
346
+ hasBackground: true,
347
+ responsive: true,
348
+ minFontSize: 8, // Minimum allowed value
349
+ maxFontSize: 128 // Maximum allowed value
350
+ };
351
+
352
+ component.dashboardSetState(edgeCaseState);
353
+ fixture.detectChanges();
354
+ const retrievedState = component.dashboardGetState();
355
+
356
+ expect(retrievedState).toEqual(edgeCaseState);
357
+ expect(retrievedState.minFontSize).toBe(8);
358
+ expect(retrievedState.maxFontSize).toBe(128);
359
+ });
360
+
361
+ it('should preserve all alignment options through serialization', () => {
362
+ const alignmentOptions: Array<'left' | 'center' | 'right'> = ['left', 'center', 'right'];
363
+
364
+ alignmentOptions.forEach(alignment => {
365
+ const testState: LabelWidgetState = {
366
+ label: `${alignment} aligned`,
367
+ fontSize: 16,
368
+ alignment: alignment,
369
+ fontWeight: 'normal',
370
+ opacity: 1,
371
+ hasBackground: true,
372
+ responsive: false,
373
+ minFontSize: 8,
374
+ maxFontSize: 64
375
+ };
376
+
377
+ component.dashboardSetState(testState);
378
+ fixture.detectChanges();
379
+ const retrievedState = component.dashboardGetState();
380
+
381
+ expect(retrievedState.alignment).toBe(alignment);
382
+ expect(retrievedState).toEqual(testState);
383
+ });
384
+ });
385
+
386
+ it('should preserve all font weight options through serialization', () => {
387
+ const fontWeightOptions: Array<'normal' | 'bold'> = ['normal', 'bold'];
388
+
389
+ fontWeightOptions.forEach(fontWeight => {
390
+ const testState: LabelWidgetState = {
391
+ label: `${fontWeight} text`,
392
+ fontSize: 16,
393
+ alignment: 'center',
394
+ fontWeight: fontWeight,
395
+ opacity: 1,
396
+ hasBackground: true,
397
+ responsive: false,
398
+ minFontSize: 8,
399
+ maxFontSize: 64
400
+ };
401
+
402
+ component.dashboardSetState(testState);
403
+ fixture.detectChanges();
404
+ const retrievedState = component.dashboardGetState();
405
+
406
+ expect(retrievedState.fontWeight).toBe(fontWeight);
407
+ expect(retrievedState).toEqual(testState);
408
+ });
409
+ });
410
+ });
411
+
412
+ describe('Custom Font Size Range Serialization', () => {
413
+ it('should serialize custom min/max font sizes correctly', () => {
414
+ const customRangeStates = [
415
+ { minFontSize: 8, maxFontSize: 32 },
416
+ { minFontSize: 12, maxFontSize: 48 },
417
+ { minFontSize: 16, maxFontSize: 96 },
418
+ { minFontSize: 8, maxFontSize: 128 }
419
+ ];
420
+
421
+ customRangeStates.forEach(({ minFontSize, maxFontSize }) => {
422
+ const testState: LabelWidgetState = {
423
+ label: `Range ${minFontSize}-${maxFontSize}`,
424
+ fontSize: 16,
425
+ alignment: 'center',
426
+ fontWeight: 'normal',
427
+ opacity: 1,
428
+ hasBackground: true,
429
+ responsive: true,
430
+ minFontSize: minFontSize,
431
+ maxFontSize: maxFontSize
432
+ };
433
+
434
+ component.dashboardSetState(testState);
435
+ fixture.detectChanges();
436
+ const retrievedState = component.dashboardGetState();
437
+
438
+ expect(retrievedState.minFontSize).toBe(minFontSize);
439
+ expect(retrievedState.maxFontSize).toBe(maxFontSize);
440
+ expect(retrievedState).toEqual(testState);
441
+ });
442
+ });
443
+
444
+ it('should handle all combinations of responsive mode and custom font ranges', () => {
445
+ const testCombinations = [
446
+ { responsive: true, minFontSize: 10, maxFontSize: 50 },
447
+ { responsive: false, minFontSize: 10, maxFontSize: 50 },
448
+ { responsive: true, minFontSize: 8, maxFontSize: 128 },
449
+ { responsive: false, minFontSize: 8, maxFontSize: 128 }
450
+ ];
451
+
452
+ testCombinations.forEach(({ responsive, minFontSize, maxFontSize }) => {
453
+ const testState: LabelWidgetState = {
454
+ label: `Combo ${responsive ? 'responsive' : 'fixed'} ${minFontSize}-${maxFontSize}`,
455
+ fontSize: 18,
456
+ alignment: 'left',
457
+ fontWeight: 'bold',
458
+ opacity: 0.9,
459
+ hasBackground: false,
460
+ responsive: responsive,
461
+ minFontSize: minFontSize,
462
+ maxFontSize: maxFontSize
463
+ };
464
+
465
+ component.dashboardSetState(testState);
466
+ fixture.detectChanges();
467
+ const retrievedState = component.dashboardGetState();
468
+
469
+ expect(retrievedState.responsive).toBe(responsive);
470
+ expect(retrievedState.minFontSize).toBe(minFontSize);
471
+ expect(retrievedState.maxFontSize).toBe(maxFontSize);
472
+ expect(retrievedState).toEqual(testState);
473
+ });
474
+ });
475
+ });
476
+
477
+ describe('State Object Type Safety', () => {
478
+ it('should handle unknown properties in state object gracefully', () => {
479
+ const stateWithExtraProps = {
480
+ label: 'Extra Props Test',
481
+ fontSize: 16,
482
+ alignment: 'center' as const,
483
+ fontWeight: 'normal' as const,
484
+ opacity: 1,
485
+ hasBackground: true,
486
+ responsive: false,
487
+ minFontSize: 8,
488
+ maxFontSize: 64,
489
+ unknownProperty: 'should be preserved',
490
+ anotherExtra: 123
491
+ };
492
+
493
+ // TypeScript won't allow this normally, but simulate receiving unknown props
494
+ component.dashboardSetState(stateWithExtraProps as any);
495
+ fixture.detectChanges();
496
+
497
+ const retrievedState = component.dashboardGetState();
498
+
499
+ // Should include all known properties with correct values
500
+ expect(retrievedState.label).toBe('Extra Props Test');
501
+ expect(retrievedState.fontSize).toBe(16);
502
+ expect(retrievedState.alignment).toBe('center');
503
+ expect(retrievedState.fontWeight).toBe('normal');
504
+ expect(retrievedState.opacity).toBe(1);
505
+ expect(retrievedState.hasBackground).toBe(true);
506
+ expect(retrievedState.responsive).toBe(false);
507
+ expect(retrievedState.minFontSize).toBe(8);
508
+ expect(retrievedState.maxFontSize).toBe(64);
509
+
510
+ // Unknown properties are preserved by the current implementation (this is acceptable behavior)
511
+ expect((retrievedState as any).unknownProperty).toBeDefined();
512
+ expect((retrievedState as any).anotherExtra).toBeDefined();
513
+ });
514
+
515
+ it('should return a new object instance on each call to dashboardGetState', () => {
516
+ const testState: LabelWidgetState = {
517
+ label: 'Instance Test',
518
+ fontSize: 16,
519
+ alignment: 'center',
520
+ fontWeight: 'normal',
521
+ opacity: 1,
522
+ hasBackground: true,
523
+ responsive: false,
524
+ minFontSize: 8,
525
+ maxFontSize: 64
526
+ };
527
+
528
+ component.dashboardSetState(testState);
529
+ fixture.detectChanges();
530
+
531
+ const firstCall = component.dashboardGetState();
532
+ const secondCall = component.dashboardGetState();
533
+
534
+ // Should be equal in content but different instances
535
+ expect(firstCall).toEqual(secondCall);
536
+ expect(firstCall).not.toBe(secondCall);
537
+ });
538
+ });
539
+ });