@imposium-hub/components 2.15.0-3 → 2.16.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.
Files changed (84) hide show
  1. package/dist/cjs/components/app-wrapper/AppWrapper.d.ts +0 -1
  2. package/dist/cjs/components/app-wrapper/AppWrapper.js +1 -1
  3. package/dist/cjs/components/app-wrapper/AppWrapper.js.map +1 -1
  4. package/dist/cjs/components/assets/AssetsTypeIcon.js +2 -0
  5. package/dist/cjs/components/assets/AssetsTypeIcon.js.map +1 -1
  6. package/dist/cjs/components/change-report/ChangeReportTree.js +65 -10
  7. package/dist/cjs/components/change-report/ChangeReportTree.js.map +1 -1
  8. package/dist/cjs/components/number-field/NumberField.d.ts +2 -2
  9. package/dist/cjs/components/number-field/NumberField.js +7 -4
  10. package/dist/cjs/components/number-field/NumberField.js.map +1 -1
  11. package/dist/cjs/components/publish-wizard/PublishWizard.js +40 -39
  12. package/dist/cjs/components/publish-wizard/PublishWizard.js.map +1 -1
  13. package/dist/cjs/constants/copy.d.ts +1 -0
  14. package/dist/cjs/constants/copy.js +3 -2
  15. package/dist/cjs/constants/copy.js.map +1 -1
  16. package/dist/cjs/constants/icons.d.ts +5 -0
  17. package/dist/cjs/constants/icons.js +12 -2
  18. package/dist/cjs/constants/icons.js.map +1 -1
  19. package/dist/cjs/constants/snippets.d.ts +2 -0
  20. package/dist/cjs/redux/actions/publish.d.ts +1 -1
  21. package/dist/cjs/redux/actions/publish.js +2 -2
  22. package/dist/cjs/redux/actions/publish.js.map +1 -1
  23. package/dist/esm/components/app-wrapper/AppWrapper.d.ts +0 -1
  24. package/dist/esm/components/app-wrapper/AppWrapper.js +1 -1
  25. package/dist/esm/components/app-wrapper/AppWrapper.js.map +1 -1
  26. package/dist/esm/components/assets/AssetsTypeIcon.js +2 -0
  27. package/dist/esm/components/assets/AssetsTypeIcon.js.map +1 -1
  28. package/dist/esm/components/change-report/ChangeReportTree.js +64 -10
  29. package/dist/esm/components/change-report/ChangeReportTree.js.map +1 -1
  30. package/dist/esm/components/number-field/NumberField.d.ts +2 -2
  31. package/dist/esm/components/number-field/NumberField.js +5 -4
  32. package/dist/esm/components/number-field/NumberField.js.map +1 -1
  33. package/dist/esm/components/publish-wizard/PublishWizard.js +40 -39
  34. package/dist/esm/components/publish-wizard/PublishWizard.js.map +1 -1
  35. package/dist/esm/constants/copy.d.ts +1 -0
  36. package/dist/esm/constants/copy.js +3 -2
  37. package/dist/esm/constants/copy.js.map +1 -1
  38. package/dist/esm/constants/icons.d.ts +5 -0
  39. package/dist/esm/constants/icons.js +10 -0
  40. package/dist/esm/constants/icons.js.map +1 -1
  41. package/dist/esm/constants/snippets.d.ts +2 -0
  42. package/dist/esm/redux/actions/publish.d.ts +1 -1
  43. package/dist/esm/redux/actions/publish.js +2 -2
  44. package/dist/esm/redux/actions/publish.js.map +1 -1
  45. package/dist/styles.css +54 -10
  46. package/dist/styles.less +65 -13
  47. package/less/components/change-report.less +50 -9
  48. package/less/components/publish-wizard.less +15 -4
  49. package/package.json +5 -1
  50. package/src/components/advanced-number-field/AdvancedNumberField.test.tsx +724 -0
  51. package/src/components/anchor-field/AnchorField.test.tsx +130 -0
  52. package/src/components/app-wrapper/AppWrapper.tsx +1 -6
  53. package/src/components/asset-details/AssetDetails.test.tsx +494 -0
  54. package/src/components/assets/AssetField.test.tsx +449 -0
  55. package/src/components/assets/AssetsTableAssetIdCell.test.tsx +142 -0
  56. package/src/components/assets/AssetsTableAssetIdFilter.test.tsx +95 -0
  57. package/src/components/assets/AssetsTableComplexTagCell.test.tsx +161 -0
  58. package/src/components/assets/AssetsTableDateCell.test.tsx +106 -0
  59. package/src/components/assets/AssetsTableDropzone.test.tsx +132 -0
  60. package/src/components/assets/AssetsTableDurationCell.test.tsx +119 -0
  61. package/src/components/assets/AssetsTableGlobalCell.test.tsx +46 -0
  62. package/src/components/assets/AssetsTableNameCell.test.tsx +166 -0
  63. package/src/components/assets/AssetsTableNameFilter.test.tsx +95 -0
  64. package/src/components/assets/AssetsTablePreviewCell.test.tsx +191 -0
  65. package/src/components/assets/AssetsTableRateCell.test.tsx +87 -0
  66. package/src/components/assets/AssetsTableSelectCell.test.tsx +156 -0
  67. package/src/components/assets/AssetsTableSelectFilter.test.tsx +119 -0
  68. package/src/components/assets/AssetsTableStatusCell.test.tsx +60 -0
  69. package/src/components/assets/AssetsTypeIcon.tsx +2 -0
  70. package/src/components/change-report/ChangeReportTree.tsx +104 -16
  71. package/src/components/number-field/NumberField.test.tsx +383 -0
  72. package/src/components/number-field/NumberField.tsx +15 -9
  73. package/src/components/publish-wizard/PublishWizard.tsx +59 -54
  74. package/src/components/text-field/TextField.test.tsx +988 -0
  75. package/src/constants/copy.ts +3 -2
  76. package/src/constants/icons.tsx +10 -0
  77. package/src/constants/snippets.ts +2 -0
  78. package/src/redux/actions/publish.ts +7 -2
  79. package/src/test/setup.ts +91 -0
  80. package/src/test/utils.tsx +44 -0
  81. package/tsconfig.eslint.json +8 -0
  82. package/tsconfig.json +1 -1
  83. package/vitest.config.ts +31 -0
  84. package/src/components/service-icon/ServiceIcon.test.tsx +0 -0
@@ -0,0 +1,724 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen } from '../../test/utils';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import AdvancedNumberField from './AdvancedNumberField';
5
+
6
+ describe('AdvancedNumberField', () => {
7
+ const defaultProps = {
8
+ value: 12,
9
+ onChange: vi.fn(),
10
+ label: 'Test Advanced Number Field',
11
+ activeOverride: 'test-override'
12
+ };
13
+
14
+ beforeEach(() => {
15
+ vi.clearAllMocks();
16
+ });
17
+
18
+ describe('Basic Rendering', () => {
19
+ it('renders without crashing', () => {
20
+ render(<AdvancedNumberField {...defaultProps} />);
21
+ const input = screen.getByRole('spinbutton');
22
+ expect(input).toBeDefined();
23
+ });
24
+
25
+ it('displays the correct value', () => {
26
+ render(
27
+ <AdvancedNumberField
28
+ {...defaultProps}
29
+ value={25}
30
+ />
31
+ );
32
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
33
+ expect(input.value).toBe('25');
34
+ });
35
+
36
+ it('displays placeholder when provided', () => {
37
+ render(
38
+ <AdvancedNumberField
39
+ {...defaultProps}
40
+ placeholder='Enter font size'
41
+ />
42
+ );
43
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
44
+ expect(input.placeholder).toBe('Enter font size');
45
+ });
46
+
47
+ it('sets readOnly attribute when readOnly prop is true', () => {
48
+ render(
49
+ <AdvancedNumberField
50
+ {...defaultProps}
51
+ readOnly
52
+ />
53
+ );
54
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
55
+ expect(input.readOnly).toBe(true);
56
+ });
57
+
58
+ it('applies min and max attributes', () => {
59
+ render(
60
+ <AdvancedNumberField
61
+ {...defaultProps}
62
+ min={8}
63
+ max={72}
64
+ />
65
+ );
66
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
67
+ expect(input.min).toBe('8');
68
+ expect(input.max).toBe('72');
69
+ });
70
+
71
+ it('renders increment and decrement buttons', () => {
72
+ const { container } = render(<AdvancedNumberField {...defaultProps} />);
73
+
74
+ const decrementButton: HTMLInputElement | null =
75
+ container.querySelector('.minus-font-size');
76
+ const incrementButton: HTMLInputElement | null =
77
+ container.querySelector('.add-font-size');
78
+
79
+ if (decrementButton && incrementButton) {
80
+ expect(decrementButton).toBeDefined();
81
+ expect(incrementButton).toBeDefined();
82
+ expect(decrementButton.value).toBe('-');
83
+ expect(incrementButton.value).toBe('+');
84
+ }
85
+ });
86
+
87
+ it('renders with custom width', () => {
88
+ const { container } = render(
89
+ <AdvancedNumberField
90
+ {...defaultProps}
91
+ width='200px'
92
+ />
93
+ );
94
+
95
+ const fieldWrapper = container.querySelector('[data-testid="field-wrapper"]');
96
+ expect(fieldWrapper).toBeDefined();
97
+ });
98
+ });
99
+
100
+ describe('Value Changes', () => {
101
+ it('calls onChange when value is typed', () => {
102
+ const onChange = vi.fn();
103
+ render(
104
+ <AdvancedNumberField
105
+ {...defaultProps}
106
+ onChange={onChange}
107
+ />
108
+ );
109
+
110
+ const input = screen.getByRole('spinbutton');
111
+ fireEvent.change(input, { target: { value: '18' } });
112
+
113
+ expect(onChange).toHaveBeenCalledWith(18);
114
+ });
115
+
116
+ it('calls onChange with null when empty string is entered', () => {
117
+ const onChange = vi.fn();
118
+ render(
119
+ <AdvancedNumberField
120
+ {...defaultProps}
121
+ onChange={onChange}
122
+ />
123
+ );
124
+
125
+ const input = screen.getByRole('spinbutton');
126
+ fireEvent.change(input, { target: { value: '' } });
127
+
128
+ expect(onChange).toHaveBeenCalledWith(null);
129
+ });
130
+
131
+ it('parses float values correctly', () => {
132
+ const onChange = vi.fn();
133
+ render(
134
+ <AdvancedNumberField
135
+ {...defaultProps}
136
+ onChange={onChange}
137
+ />
138
+ );
139
+
140
+ const input = screen.getByRole('spinbutton');
141
+ fireEvent.change(input, { target: { value: '14.5' } });
142
+
143
+ expect(onChange).toHaveBeenCalledWith(14.5);
144
+ });
145
+ });
146
+
147
+ describe('Increment/Decrement Buttons', () => {
148
+ it('increments value when plus button is clicked', () => {
149
+ const onChange = vi.fn();
150
+ const { container } = render(
151
+ <AdvancedNumberField
152
+ {...defaultProps}
153
+ value={12}
154
+ onChange={onChange}
155
+ />
156
+ );
157
+
158
+ const incrementButton = container.querySelector('.add-font-size');
159
+ if (incrementButton) {
160
+ fireEvent.click(incrementButton);
161
+ }
162
+
163
+ expect(onChange).toHaveBeenCalledWith(13);
164
+ });
165
+
166
+ it('decrements value when minus button is clicked', () => {
167
+ const onChange = vi.fn();
168
+ const { container } = render(
169
+ <AdvancedNumberField
170
+ {...defaultProps}
171
+ value={12}
172
+ onChange={onChange}
173
+ />
174
+ );
175
+
176
+ const decrementButton = container.querySelector('.minus-font-size');
177
+ if (decrementButton) {
178
+ fireEvent.click(decrementButton);
179
+ }
180
+
181
+ expect(onChange).toHaveBeenCalledWith(11);
182
+ });
183
+
184
+ it('does not call onChange when incrementing would exceed max', () => {
185
+ const onChange = vi.fn();
186
+ const { container } = render(
187
+ <AdvancedNumberField
188
+ {...defaultProps}
189
+ value={72}
190
+ max={72}
191
+ onChange={onChange}
192
+ />
193
+ );
194
+
195
+ const incrementButton = container.querySelector('.add-font-size');
196
+ if (incrementButton) {
197
+ fireEvent.click(incrementButton);
198
+ }
199
+
200
+ expect(onChange).not.toHaveBeenCalled();
201
+ });
202
+
203
+ it('does not call onChange when decrementing would go below min', () => {
204
+ const onChange = vi.fn();
205
+ const { container } = render(
206
+ <AdvancedNumberField
207
+ {...defaultProps}
208
+ value={8}
209
+ min={8}
210
+ onChange={onChange}
211
+ />
212
+ );
213
+
214
+ const decrementButton = container.querySelector('.minus-font-size');
215
+ if (decrementButton) {
216
+ fireEvent.click(decrementButton);
217
+ }
218
+
219
+ expect(onChange).not.toHaveBeenCalled();
220
+ });
221
+ });
222
+
223
+ describe('Validation', () => {
224
+ it('shows error state when value is below minimum', () => {
225
+ const { container } = render(
226
+ <AdvancedNumberField
227
+ {...defaultProps}
228
+ value={5}
229
+ min={8}
230
+ />
231
+ );
232
+
233
+ const input = screen.getByRole('spinbutton');
234
+ fireEvent.change(input, { target: { value: '5' } });
235
+
236
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
237
+ expect(fieldWrapper).toBeDefined();
238
+ });
239
+
240
+ it('shows error state when value is above maximum', () => {
241
+ const { container } = render(
242
+ <AdvancedNumberField
243
+ {...defaultProps}
244
+ value={100}
245
+ max={72}
246
+ />
247
+ );
248
+
249
+ const input = screen.getByRole('spinbutton');
250
+ fireEvent.change(input, { target: { value: '100' } });
251
+
252
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
253
+ expect(fieldWrapper).toBeDefined();
254
+ });
255
+
256
+ it('shows correct error message for min constraint only', () => {
257
+ const { container } = render(
258
+ <AdvancedNumberField
259
+ {...defaultProps}
260
+ value={12}
261
+ min={8}
262
+ />
263
+ );
264
+
265
+ const input = screen.getByRole('spinbutton');
266
+ fireEvent.change(input, { target: { value: '5' } });
267
+
268
+ // Check for error class which indicates validation failed
269
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
270
+ expect(fieldWrapper).toBeDefined();
271
+ });
272
+
273
+ it('shows correct error message for max constraint only', () => {
274
+ const { container } = render(
275
+ <AdvancedNumberField
276
+ {...defaultProps}
277
+ value={12}
278
+ max={72}
279
+ />
280
+ );
281
+
282
+ const input = screen.getByRole('spinbutton');
283
+ fireEvent.change(input, { target: { value: '100' } });
284
+
285
+ // Check for error class which indicates validation failed
286
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
287
+ expect(fieldWrapper).toBeDefined();
288
+ });
289
+
290
+ it('shows correct error message for min and max constraints', () => {
291
+ const { container } = render(
292
+ <AdvancedNumberField
293
+ {...defaultProps}
294
+ value={12}
295
+ min={8}
296
+ max={72}
297
+ />
298
+ );
299
+
300
+ const input = screen.getByRole('spinbutton');
301
+ fireEvent.change(input, { target: { value: '100' } });
302
+
303
+ // Check for error class which indicates validation failed
304
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
305
+ expect(fieldWrapper).toBeDefined();
306
+ });
307
+
308
+ it('does not show error state when value is valid', () => {
309
+ const { container } = render(
310
+ <AdvancedNumberField
311
+ {...defaultProps}
312
+ value={24}
313
+ min={8}
314
+ max={72}
315
+ />
316
+ );
317
+
318
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
319
+ expect(fieldWrapper).toBeNull();
320
+ });
321
+
322
+ it('calls onChange when valid value is entered with constraints', () => {
323
+ const onChange = vi.fn();
324
+ render(
325
+ <AdvancedNumberField
326
+ {...defaultProps}
327
+ value={12}
328
+ min={8}
329
+ max={72}
330
+ onChange={onChange}
331
+ />
332
+ );
333
+
334
+ const input = screen.getByRole('spinbutton');
335
+ fireEvent.change(input, { target: { value: '24' } });
336
+
337
+ // Should call onChange with valid value
338
+ expect(onChange).toHaveBeenCalledWith(24);
339
+ });
340
+
341
+ it('clears error state when valid value is entered after error', () => {
342
+ const onChange = vi.fn();
343
+ const { container } = render(
344
+ <AdvancedNumberField
345
+ {...defaultProps}
346
+ value={12}
347
+ min={8}
348
+ max={72}
349
+ onChange={onChange}
350
+ />
351
+ );
352
+
353
+ const input = screen.getByRole('spinbutton');
354
+
355
+ // First enter invalid value
356
+ fireEvent.change(input, { target: { value: '100' } });
357
+ let fieldWrapper = container.querySelector('.advanced-number-field.invalid');
358
+ expect(fieldWrapper).toBeDefined();
359
+
360
+ // Then enter valid value
361
+ fireEvent.change(input, { target: { value: '24' } });
362
+ fieldWrapper = container.querySelector('.advanced-number-field.invalid');
363
+ expect(fieldWrapper).toBeNull();
364
+ expect(onChange).toHaveBeenCalledWith(24);
365
+ });
366
+ });
367
+
368
+ describe('Font Size Dropdown', () => {
369
+ it('shows dropdown when input is clicked', () => {
370
+ const { container } = render(<AdvancedNumberField {...defaultProps} />);
371
+
372
+ const input = screen.getByRole('spinbutton');
373
+ fireEvent.click(input);
374
+
375
+ const dropdown = container.querySelector('.font-size-list');
376
+ expect(dropdown).toBeDefined();
377
+ });
378
+
379
+ it('hides dropdown when input is clicked again', () => {
380
+ const { container } = render(<AdvancedNumberField {...defaultProps} />);
381
+
382
+ const input = screen.getByRole('spinbutton');
383
+
384
+ // Click to show dropdown
385
+ fireEvent.click(input);
386
+ let dropdown = container.querySelector('.font-size-list');
387
+ expect(dropdown).toBeDefined();
388
+
389
+ // Click again to hide dropdown
390
+ fireEvent.click(input);
391
+ dropdown = container.querySelector('.font-size-list');
392
+ expect(dropdown).toBeNull();
393
+ });
394
+
395
+ it('renders all font sizes in dropdown', () => {
396
+ const { container } = render(<AdvancedNumberField {...defaultProps} />);
397
+
398
+ const input = screen.getByRole('spinbutton');
399
+ fireEvent.click(input);
400
+
401
+ const fontSizeButtons = container.querySelectorAll('.font-size-btn');
402
+ expect(fontSizeButtons.length).toBe(13); // FONT_SIZES array length
403
+
404
+ // Check if all font sizes are present
405
+ const expectedSizes = [8, 9, 10, 12, 14, 18, 24, 30, 36, 48, 60, 72, 96];
406
+ expectedSizes.forEach((size, index) => {
407
+ expect(fontSizeButtons[index].textContent).toBe(size.toString());
408
+ });
409
+ });
410
+
411
+ it('selects font size when dropdown item is clicked', () => {
412
+ const onChange = vi.fn();
413
+ const { container } = render(
414
+ <AdvancedNumberField
415
+ {...defaultProps}
416
+ onChange={onChange}
417
+ />
418
+ );
419
+
420
+ const input = screen.getByRole('spinbutton');
421
+ fireEvent.click(input);
422
+
423
+ const fontSizeButton = container.querySelector('.font-size-btn');
424
+ if (fontSizeButton) {
425
+ fireEvent.click(fontSizeButton);
426
+ }
427
+
428
+ expect(onChange).toHaveBeenCalledWith(8); // First font size
429
+ });
430
+
431
+ it('hides dropdown after selecting a font size', () => {
432
+ const { container } = render(<AdvancedNumberField {...defaultProps} />);
433
+
434
+ const input = screen.getByRole('spinbutton');
435
+ fireEvent.click(input);
436
+
437
+ const fontSizeButton = container.querySelector('.font-size-btn');
438
+ if (fontSizeButton) {
439
+ fireEvent.click(fontSizeButton);
440
+ }
441
+
442
+ const dropdown = container.querySelector('.font-size-list');
443
+ expect(dropdown).toBeNull();
444
+ });
445
+
446
+ it('hides dropdown when key is pressed', () => {
447
+ const { container } = render(<AdvancedNumberField {...defaultProps} />);
448
+
449
+ const input = screen.getByRole('spinbutton');
450
+ fireEvent.click(input);
451
+
452
+ let dropdown = container.querySelector('.font-size-list');
453
+ expect(dropdown).toBeDefined();
454
+
455
+ fireEvent.keyDown(input, { key: 'Escape' });
456
+
457
+ dropdown = container.querySelector('.font-size-list');
458
+ expect(dropdown).toBeNull();
459
+ });
460
+ });
461
+
462
+ describe('Focus and Blur Events', () => {
463
+ it('handles focus event', () => {
464
+ render(<AdvancedNumberField {...defaultProps} />);
465
+
466
+ const input = screen.getByRole('spinbutton');
467
+ fireEvent.focus(input);
468
+
469
+ // Test that focus event was handled (no errors thrown)
470
+ expect(input).toBeDefined();
471
+ });
472
+
473
+ it('handles blur event', () => {
474
+ render(<AdvancedNumberField {...defaultProps} />);
475
+
476
+ const input = screen.getByRole('spinbutton');
477
+ fireEvent.blur(input);
478
+
479
+ // Test that blur event was handled (no errors thrown)
480
+ expect(input).toBeDefined();
481
+ });
482
+ });
483
+
484
+ describe('Component Lifecycle', () => {
485
+ it('initializes with correct state on mount', () => {
486
+ render(
487
+ <AdvancedNumberField
488
+ {...defaultProps}
489
+ value={24}
490
+ />
491
+ );
492
+
493
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
494
+ expect(input.value).toBe('24');
495
+ });
496
+
497
+ it('updates state when props.value changes', () => {
498
+ const { rerender } = render(
499
+ <AdvancedNumberField
500
+ {...defaultProps}
501
+ value={12}
502
+ />
503
+ );
504
+
505
+ rerender(
506
+ <AdvancedNumberField
507
+ {...defaultProps}
508
+ value={18}
509
+ />
510
+ );
511
+
512
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
513
+ expect(input.value).toBe('18');
514
+ });
515
+
516
+ it('clears error state when props.value changes', () => {
517
+ const { container, rerender } = render(
518
+ <AdvancedNumberField
519
+ {...defaultProps}
520
+ value={100}
521
+ max={72}
522
+ />
523
+ );
524
+
525
+ // Trigger error state
526
+ const input = screen.getByRole('spinbutton');
527
+ fireEvent.change(input, { target: { value: '100' } });
528
+
529
+ let fieldWrapper = container.querySelector('.advanced-number-field.invalid');
530
+ expect(fieldWrapper).toBeDefined();
531
+
532
+ // Update props to valid value
533
+ rerender(
534
+ <AdvancedNumberField
535
+ {...defaultProps}
536
+ value={24}
537
+ max={72}
538
+ />
539
+ );
540
+
541
+ fieldWrapper = container.querySelector('.advanced-number-field.invalid');
542
+ expect(fieldWrapper).toBeNull();
543
+ });
544
+ });
545
+
546
+ describe('Edge Cases', () => {
547
+ it('handles null value correctly', () => {
548
+ render(
549
+ <AdvancedNumberField
550
+ {...defaultProps}
551
+ value={undefined as any}
552
+ />
553
+ );
554
+
555
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
556
+ expect(input.value).toBe('');
557
+ });
558
+
559
+ it('handles zero value correctly', () => {
560
+ render(
561
+ <AdvancedNumberField
562
+ {...defaultProps}
563
+ value={0}
564
+ />
565
+ );
566
+
567
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
568
+ expect(input.value).toBe('0');
569
+ });
570
+
571
+ it('handles negative values correctly', () => {
572
+ render(
573
+ <AdvancedNumberField
574
+ {...defaultProps}
575
+ value={-5}
576
+ />
577
+ );
578
+
579
+ const input: HTMLInputElement = screen.getByRole('spinbutton');
580
+ expect(input.value).toBe('-5');
581
+ });
582
+
583
+ it('renders custom buttons when provided', () => {
584
+ const customButton = <button key='custom'>Custom</button>;
585
+ render(
586
+ <AdvancedNumberField
587
+ {...defaultProps}
588
+ buttons={customButton}
589
+ />
590
+ );
591
+
592
+ const button = screen.getByText('Custom');
593
+ expect(button).toBeDefined();
594
+ });
595
+
596
+ it('displays info message when no error', () => {
597
+ const { container } = render(
598
+ <AdvancedNumberField
599
+ {...defaultProps}
600
+ value={24}
601
+ info='This is helpful information'
602
+ />
603
+ );
604
+
605
+ // Check that no error class is present (component renders normally)
606
+ const fieldWrapper = container.querySelector('.advanced-number-field.invalid');
607
+ expect(fieldWrapper).toBeNull();
608
+
609
+ // Check that the component renders without errors
610
+ const input = screen.getByRole('spinbutton');
611
+ expect(input).toBeDefined();
612
+ });
613
+
614
+ it('handles validation without min/max constraints', () => {
615
+ const onChange = vi.fn();
616
+ render(
617
+ <AdvancedNumberField
618
+ {...defaultProps}
619
+ onChange={onChange}
620
+ />
621
+ );
622
+
623
+ const input = screen.getByRole('spinbutton');
624
+ fireEvent.change(input, { target: { value: '999' } });
625
+
626
+ expect(onChange).toHaveBeenCalledWith(999);
627
+ });
628
+
629
+ it('handles increment from zero', () => {
630
+ const onChange = vi.fn();
631
+ const { container } = render(
632
+ <AdvancedNumberField
633
+ {...defaultProps}
634
+ value={0}
635
+ onChange={onChange}
636
+ />
637
+ );
638
+
639
+ const incrementButton = container.querySelector('.add-font-size');
640
+ if (incrementButton) {
641
+ fireEvent.click(incrementButton);
642
+ }
643
+
644
+ expect(onChange).toHaveBeenCalledWith(1);
645
+ });
646
+
647
+ it('handles decrement to negative values', () => {
648
+ const onChange = vi.fn();
649
+ const { container } = render(
650
+ <AdvancedNumberField
651
+ {...defaultProps}
652
+ value={0}
653
+ onChange={onChange}
654
+ />
655
+ );
656
+
657
+ const decrementButton = container.querySelector('.minus-font-size');
658
+ if (decrementButton) {
659
+ fireEvent.click(decrementButton);
660
+ }
661
+
662
+ expect(onChange).toHaveBeenCalledWith(-1);
663
+ });
664
+ });
665
+
666
+ describe('Advanced Props', () => {
667
+ it('passes through FieldWrapper props correctly', () => {
668
+ const { container } = render(
669
+ <AdvancedNumberField
670
+ {...defaultProps}
671
+ propKey='fontSize'
672
+ showCopyPropIds={true}
673
+ activeOverride='test-override'
674
+ previewMultiplier={2}
675
+ />
676
+ );
677
+
678
+ const fieldWrapper = container.querySelector('[data-testid="field-wrapper"]');
679
+ expect(fieldWrapper).toBeDefined();
680
+ });
681
+
682
+ it('handles setOverride callback', () => {
683
+ const setOverride = vi.fn();
684
+ render(
685
+ <AdvancedNumberField
686
+ {...defaultProps}
687
+ setOverride={setOverride}
688
+ />
689
+ );
690
+
691
+ // Component should render without errors
692
+ const input = screen.getByRole('spinbutton');
693
+ expect(input).toBeDefined();
694
+ });
695
+
696
+ it('handles removeOverride callback', () => {
697
+ const removeOverride = vi.fn();
698
+ render(
699
+ <AdvancedNumberField
700
+ {...defaultProps}
701
+ removeOverride={removeOverride}
702
+ />
703
+ );
704
+
705
+ // Component should render without errors
706
+ const input = screen.getByRole('spinbutton');
707
+ expect(input).toBeDefined();
708
+ });
709
+
710
+ it('handles onClose callback', () => {
711
+ const onClose = vi.fn();
712
+ render(
713
+ <AdvancedNumberField
714
+ {...defaultProps}
715
+ onClose={onClose}
716
+ />
717
+ );
718
+
719
+ // Component should render without errors
720
+ const input = screen.getByRole('spinbutton');
721
+ expect(input).toBeDefined();
722
+ });
723
+ });
724
+ });