@htlkg/components 0.0.1

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 (79) hide show
  1. package/dist/composables/index.js +388 -0
  2. package/dist/composables/index.js.map +1 -0
  3. package/package.json +41 -0
  4. package/src/composables/index.ts +6 -0
  5. package/src/composables/useForm.test.ts +229 -0
  6. package/src/composables/useForm.ts +130 -0
  7. package/src/composables/useFormValidation.test.ts +189 -0
  8. package/src/composables/useFormValidation.ts +83 -0
  9. package/src/composables/useModal.property.test.ts +164 -0
  10. package/src/composables/useModal.ts +43 -0
  11. package/src/composables/useNotifications.test.ts +166 -0
  12. package/src/composables/useNotifications.ts +81 -0
  13. package/src/composables/useTable.property.test.ts +198 -0
  14. package/src/composables/useTable.ts +134 -0
  15. package/src/composables/useTabs.property.test.ts +247 -0
  16. package/src/composables/useTabs.ts +101 -0
  17. package/src/data/Chart.demo.vue +340 -0
  18. package/src/data/Chart.md +525 -0
  19. package/src/data/Chart.vue +133 -0
  20. package/src/data/DataList.md +80 -0
  21. package/src/data/DataList.test.ts +69 -0
  22. package/src/data/DataList.vue +46 -0
  23. package/src/data/SearchableSelect.md +107 -0
  24. package/src/data/SearchableSelect.vue +124 -0
  25. package/src/data/Table.demo.vue +296 -0
  26. package/src/data/Table.md +588 -0
  27. package/src/data/Table.property.test.ts +548 -0
  28. package/src/data/Table.test.ts +562 -0
  29. package/src/data/Table.unit.test.ts +544 -0
  30. package/src/data/Table.vue +321 -0
  31. package/src/data/index.ts +5 -0
  32. package/src/domain/BrandCard.md +81 -0
  33. package/src/domain/BrandCard.vue +63 -0
  34. package/src/domain/BrandSelector.md +84 -0
  35. package/src/domain/BrandSelector.vue +65 -0
  36. package/src/domain/ProductBadge.md +60 -0
  37. package/src/domain/ProductBadge.vue +47 -0
  38. package/src/domain/UserAvatar.md +84 -0
  39. package/src/domain/UserAvatar.vue +60 -0
  40. package/src/domain/domain-components.property.test.ts +449 -0
  41. package/src/domain/index.ts +4 -0
  42. package/src/forms/DateRange.demo.vue +273 -0
  43. package/src/forms/DateRange.md +337 -0
  44. package/src/forms/DateRange.vue +110 -0
  45. package/src/forms/JsonSchemaForm.demo.vue +549 -0
  46. package/src/forms/JsonSchemaForm.md +112 -0
  47. package/src/forms/JsonSchemaForm.property.test.ts +817 -0
  48. package/src/forms/JsonSchemaForm.test.ts +601 -0
  49. package/src/forms/JsonSchemaForm.unit.test.ts +801 -0
  50. package/src/forms/JsonSchemaForm.vue +615 -0
  51. package/src/forms/index.ts +3 -0
  52. package/src/index.ts +17 -0
  53. package/src/navigation/Breadcrumbs.demo.vue +142 -0
  54. package/src/navigation/Breadcrumbs.md +102 -0
  55. package/src/navigation/Breadcrumbs.test.ts +69 -0
  56. package/src/navigation/Breadcrumbs.vue +58 -0
  57. package/src/navigation/Stepper.demo.vue +337 -0
  58. package/src/navigation/Stepper.md +174 -0
  59. package/src/navigation/Stepper.vue +146 -0
  60. package/src/navigation/Tabs.demo.vue +293 -0
  61. package/src/navigation/Tabs.md +163 -0
  62. package/src/navigation/Tabs.test.ts +176 -0
  63. package/src/navigation/Tabs.vue +104 -0
  64. package/src/navigation/index.ts +5 -0
  65. package/src/overlays/Alert.demo.vue +377 -0
  66. package/src/overlays/Alert.md +248 -0
  67. package/src/overlays/Alert.test.ts +166 -0
  68. package/src/overlays/Alert.vue +70 -0
  69. package/src/overlays/Drawer.md +140 -0
  70. package/src/overlays/Drawer.test.ts +92 -0
  71. package/src/overlays/Drawer.vue +76 -0
  72. package/src/overlays/Modal.demo.vue +149 -0
  73. package/src/overlays/Modal.md +385 -0
  74. package/src/overlays/Modal.test.ts +128 -0
  75. package/src/overlays/Modal.vue +86 -0
  76. package/src/overlays/Notification.md +150 -0
  77. package/src/overlays/Notification.test.ts +96 -0
  78. package/src/overlays/Notification.vue +58 -0
  79. package/src/overlays/index.ts +4 -0
@@ -0,0 +1,601 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { mount, flushPromises } from '@vue/test-utils';
3
+ import { nextTick } from 'vue';
4
+ import JsonSchemaForm from './JsonSchemaForm.vue';
5
+
6
+ /**
7
+ * Integration tests for JsonSchemaForm
8
+ * Tests user interactions, real-world scenarios, and component integration
9
+ */
10
+ describe('JsonSchemaForm integration tests', () => {
11
+ describe('User Registration Form', () => {
12
+ const registrationSchema = {
13
+ type: 'object',
14
+ title: 'User Registration',
15
+ properties: {
16
+ username: { type: 'string', title: 'Username', minLength: 3, maxLength: 20 },
17
+ email: { type: 'string', title: 'Email', format: 'email', minLength: 1 },
18
+ password: { type: 'string', title: 'Password', minLength: 8 },
19
+ age: { type: 'number', title: 'Age', minimum: 18, maximum: 120 },
20
+ terms: { type: 'boolean', title: 'Accept Terms' }
21
+ },
22
+ required: ['username', 'email', 'password', 'terms']
23
+ };
24
+
25
+ it('should allow user to fill out and submit valid registration form', async () => {
26
+ const wrapper = mount(JsonSchemaForm, {
27
+ props: {
28
+ schema: registrationSchema,
29
+ modelValue: {}
30
+ }
31
+ });
32
+
33
+ // User fills out form by updating modelValue
34
+ const vm = wrapper.vm as any;
35
+ vm.updateField('username', 'johndoe');
36
+ vm.updateField('email', 'john@example.com');
37
+ vm.updateField('password', 'SecurePass123');
38
+ vm.updateField('age', 25);
39
+ vm.updateField('terms', true);
40
+
41
+ await nextTick();
42
+
43
+ // Update props to reflect the changes
44
+ await wrapper.setProps({
45
+ modelValue: {
46
+ username: 'johndoe',
47
+ email: 'john@example.com',
48
+ password: 'SecurePass123',
49
+ age: 25,
50
+ terms: true
51
+ }
52
+ });
53
+
54
+ // Submit form
55
+ await wrapper.find('form').trigger('submit');
56
+ await nextTick();
57
+
58
+ // Should emit submit event with form data
59
+ expect(wrapper.emitted('submit')).toBeDefined();
60
+ const submitData = wrapper.emitted('submit')?.[0]?.[0] as any;
61
+ expect(submitData.username).toBe('johndoe');
62
+ expect(submitData.email).toBe('john@example.com');
63
+ expect(submitData.password).toBe('SecurePass123');
64
+ expect(submitData.age).toBe(25);
65
+ expect(submitData.terms).toBe(true);
66
+ });
67
+
68
+ it('should show validation errors when submitting incomplete form', async () => {
69
+ const wrapper = mount(JsonSchemaForm, {
70
+ props: {
71
+ schema: registrationSchema,
72
+ modelValue: {}
73
+ }
74
+ });
75
+
76
+ // Try to submit empty form
77
+ await wrapper.find('form').trigger('submit');
78
+ await nextTick();
79
+
80
+ // Should emit validation-error event
81
+ expect(wrapper.emitted('validation-error')).toBeDefined();
82
+ const errors = wrapper.emitted('validation-error')?.[0]?.[0] as any[];
83
+
84
+ // Should have errors for all required fields
85
+ expect(errors.length).toBeGreaterThan(0);
86
+ expect(errors.some(e => e.field === 'username')).toBe(true);
87
+ expect(errors.some(e => e.field === 'email')).toBe(true);
88
+ expect(errors.some(e => e.field === 'password')).toBe(true);
89
+ expect(errors.some(e => e.field === 'terms')).toBe(true);
90
+
91
+ // Should NOT emit submit event
92
+ expect(wrapper.emitted('submit')).toBeUndefined();
93
+ });
94
+
95
+ it('should validate fields as user types', async () => {
96
+ const wrapper = mount(JsonSchemaForm, {
97
+ props: {
98
+ schema: registrationSchema,
99
+ modelValue: {}
100
+ }
101
+ });
102
+
103
+ const vm = wrapper.vm as any;
104
+
105
+ // User types short username (less than minLength of 3)
106
+ vm.updateField('username', 'ab');
107
+ await wrapper.setProps({ modelValue: { username: 'ab' } });
108
+ await nextTick();
109
+
110
+ // Should show validation error for minLength
111
+ expect(vm.errors.username).toBeDefined();
112
+ expect(vm.errors.username).toContain('at least 3');
113
+
114
+ // User corrects username
115
+ vm.updateField('username', 'validuser');
116
+ await wrapper.setProps({ modelValue: { username: 'validuser' } });
117
+ await nextTick();
118
+
119
+ // Error should be cleared
120
+ expect(vm.errors.username).toBeUndefined();
121
+ });
122
+
123
+ it('should validate email format in real-time', async () => {
124
+ const wrapper = mount(JsonSchemaForm, {
125
+ props: {
126
+ schema: registrationSchema,
127
+ modelValue: {}
128
+ }
129
+ });
130
+
131
+ const vm = wrapper.vm as any;
132
+
133
+ // Invalid email (not matching email format)
134
+ vm.updateField('email', 'notanemail');
135
+ await wrapper.setProps({ modelValue: { email: 'notanemail' } });
136
+ await nextTick();
137
+
138
+ expect(vm.errors.email).toBeDefined();
139
+ expect(vm.errors.email).toContain('valid email');
140
+
141
+ // Valid email
142
+ vm.updateField('email', 'valid@email.com');
143
+ await wrapper.setProps({ modelValue: { email: 'valid@email.com' } });
144
+ await nextTick();
145
+
146
+ expect(vm.errors.email).toBeUndefined();
147
+ });
148
+ });
149
+
150
+ describe('API Configuration Form', () => {
151
+ const apiSchema = {
152
+ type: 'object',
153
+ title: 'API Configuration',
154
+ properties: {
155
+ apiUrl: { type: 'string', title: 'API URL', format: 'uri', minLength: 1 },
156
+ apiKey: { type: 'string', title: 'API Key', minLength: 1 },
157
+ timeout: { type: 'integer', title: 'Timeout', minimum: 5, maximum: 300 },
158
+ retries: { type: 'integer', title: 'Retries', minimum: 0, maximum: 10 },
159
+ logLevel: {
160
+ type: 'string',
161
+ title: 'Log Level',
162
+ enum: ['debug', 'info', 'warn', 'error']
163
+ }
164
+ },
165
+ required: ['apiUrl', 'apiKey']
166
+ };
167
+
168
+ const apiUiSchema = {
169
+ timeout: { 'ui:widget': 'slider' },
170
+ retries: { 'ui:widget': 'slider' }
171
+ };
172
+
173
+ it('should validate URL format', async () => {
174
+ const wrapper = mount(JsonSchemaForm, {
175
+ props: {
176
+ schema: apiSchema,
177
+ uiSchema: apiUiSchema,
178
+ modelValue: {}
179
+ }
180
+ });
181
+
182
+ const vm = wrapper.vm as any;
183
+
184
+ // Invalid URL (not matching uri format)
185
+ vm.updateField('apiUrl', 'not-a-url');
186
+ await wrapper.setProps({ modelValue: { apiUrl: 'not-a-url' } });
187
+ await nextTick();
188
+
189
+ expect(vm.errors.apiUrl).toBeDefined();
190
+ expect(vm.errors.apiUrl).toContain('valid uri');
191
+
192
+ // Valid URL
193
+ vm.updateField('apiUrl', 'https://api.example.com');
194
+ await wrapper.setProps({ modelValue: { apiUrl: 'https://api.example.com' } });
195
+ await nextTick();
196
+
197
+ expect(vm.errors.apiUrl).toBeUndefined();
198
+ });
199
+
200
+ it('should enforce number ranges', async () => {
201
+ const wrapper = mount(JsonSchemaForm, {
202
+ props: {
203
+ schema: apiSchema,
204
+ uiSchema: apiUiSchema,
205
+ modelValue: {
206
+ apiUrl: 'https://api.example.com',
207
+ apiKey: 'test-key'
208
+ }
209
+ }
210
+ });
211
+
212
+ const vm = wrapper.vm as any;
213
+
214
+ // Timeout too low (minimum is 5)
215
+ vm.updateField('timeout', 2);
216
+ await wrapper.setProps({
217
+ modelValue: {
218
+ apiUrl: 'https://api.example.com',
219
+ apiKey: 'test-key',
220
+ timeout: 2
221
+ }
222
+ });
223
+ await nextTick();
224
+
225
+ expect(vm.errors.timeout).toBeDefined();
226
+ expect(vm.errors.timeout).toContain('at least 5');
227
+
228
+ // Timeout too high (maximum is 300)
229
+ vm.updateField('timeout', 500);
230
+ await wrapper.setProps({
231
+ modelValue: {
232
+ apiUrl: 'https://api.example.com',
233
+ apiKey: 'test-key',
234
+ timeout: 500
235
+ }
236
+ });
237
+ await nextTick();
238
+
239
+ expect(vm.errors.timeout).toBeDefined();
240
+ expect(vm.errors.timeout).toContain('at most 300');
241
+
242
+ // Valid timeout
243
+ vm.updateField('timeout', 30);
244
+ await wrapper.setProps({
245
+ modelValue: {
246
+ apiUrl: 'https://api.example.com',
247
+ apiKey: 'test-key',
248
+ timeout: 30
249
+ }
250
+ });
251
+ await nextTick();
252
+
253
+ expect(vm.errors.timeout).toBeUndefined();
254
+ });
255
+
256
+ it('should validate enum values', async () => {
257
+ const wrapper = mount(JsonSchemaForm, {
258
+ props: {
259
+ schema: apiSchema,
260
+ uiSchema: apiUiSchema,
261
+ modelValue: {
262
+ apiUrl: 'https://api.example.com',
263
+ apiKey: 'test-key',
264
+ logLevel: 'invalid-level'
265
+ }
266
+ }
267
+ });
268
+
269
+ // Submit to trigger validation
270
+ await wrapper.find('form').trigger('submit');
271
+ await nextTick();
272
+
273
+ const errors = wrapper.emitted('validation-error')?.[0]?.[0] as any[];
274
+ const logLevelError = errors.find(e => e.field === 'logLevel');
275
+
276
+ expect(logLevelError).toBeDefined();
277
+ expect(logLevelError.message).toContain('one of');
278
+ });
279
+
280
+ it('should not submit with empty required fields', async () => {
281
+ const wrapper = mount(JsonSchemaForm, {
282
+ props: {
283
+ schema: apiSchema,
284
+ uiSchema: apiUiSchema,
285
+ modelValue: {
286
+ apiUrl: '',
287
+ apiKey: ''
288
+ }
289
+ }
290
+ });
291
+
292
+ await wrapper.find('form').trigger('submit');
293
+ await nextTick();
294
+
295
+ // Should have validation errors
296
+ expect(wrapper.emitted('validation-error')).toBeDefined();
297
+ const errors = wrapper.emitted('validation-error')?.[0]?.[0] as any[];
298
+
299
+ expect(errors.some(e => e.field === 'apiUrl')).toBe(true);
300
+ expect(errors.some(e => e.field === 'apiKey')).toBe(true);
301
+
302
+ // Should not submit
303
+ expect(wrapper.emitted('submit')).toBeUndefined();
304
+ });
305
+ });
306
+
307
+ describe('Product Form with Complex Validation', () => {
308
+ const productSchema = {
309
+ type: 'object',
310
+ properties: {
311
+ name: { type: 'string', title: 'Product Name', minLength: 3, maxLength: 100 },
312
+ sku: { type: 'string', title: 'SKU', minLength: 1 },
313
+ price: { type: 'number', title: 'Price', minimum: 0, maximum: 999999 },
314
+ quantity: { type: 'integer', title: 'Quantity', minimum: 0, maximum: 10000 },
315
+ category: {
316
+ type: 'string',
317
+ title: 'Category',
318
+ enum: ['Electronics', 'Clothing', 'Food', 'Books']
319
+ },
320
+ description: { type: 'string', title: 'Description', minLength: 10, maxLength: 500 },
321
+ active: { type: 'boolean', title: 'Active' }
322
+ },
323
+ required: ['name', 'sku', 'price', 'category']
324
+ };
325
+
326
+ it('should handle complete product creation workflow', async () => {
327
+ const wrapper = mount(JsonSchemaForm, {
328
+ props: {
329
+ schema: productSchema,
330
+ modelValue: {}
331
+ }
332
+ });
333
+
334
+ const vm = wrapper.vm as any;
335
+
336
+ // Fill required fields
337
+ vm.updateField('name', 'Wireless Headphones');
338
+ vm.updateField('sku', 'WH-001');
339
+ vm.updateField('price', 299.99);
340
+ vm.updateField('category', 'Electronics');
341
+
342
+ // Fill optional fields
343
+ vm.updateField('quantity', 50);
344
+ vm.updateField('description', 'High-quality wireless headphones with noise cancellation');
345
+ vm.updateField('active', true);
346
+
347
+ await nextTick();
348
+
349
+ // Update props to reflect the changes
350
+ await wrapper.setProps({
351
+ modelValue: {
352
+ name: 'Wireless Headphones',
353
+ sku: 'WH-001',
354
+ price: 299.99,
355
+ category: 'Electronics',
356
+ quantity: 50,
357
+ description: 'High-quality wireless headphones with noise cancellation',
358
+ active: true
359
+ }
360
+ });
361
+
362
+ // Submit
363
+ await wrapper.find('form').trigger('submit');
364
+ await nextTick();
365
+
366
+ // Should submit successfully
367
+ expect(wrapper.emitted('submit')).toBeDefined();
368
+ const submitData = wrapper.emitted('submit')?.[0]?.[0] as any;
369
+
370
+ expect(submitData.name).toBe('Wireless Headphones');
371
+ expect(submitData.sku).toBe('WH-001');
372
+ expect(submitData.price).toBe(299.99);
373
+ expect(submitData.category).toBe('Electronics');
374
+ expect(submitData.quantity).toBe(50);
375
+ expect(submitData.active).toBe(true);
376
+ });
377
+
378
+ it('should validate multiple fields simultaneously', async () => {
379
+ const wrapper = mount(JsonSchemaForm, {
380
+ props: {
381
+ schema: productSchema,
382
+ modelValue: {
383
+ name: 'AB', // Too short
384
+ sku: '', // Empty
385
+ price: -10, // Negative
386
+ category: 'InvalidCategory', // Not in enum
387
+ description: 'Short' // Too short
388
+ }
389
+ }
390
+ });
391
+
392
+ await wrapper.find('form').trigger('submit');
393
+ await nextTick();
394
+
395
+ const errors = wrapper.emitted('validation-error')?.[0]?.[0] as any[];
396
+
397
+ // Should have multiple errors
398
+ expect(errors.length).toBeGreaterThan(3);
399
+ expect(errors.some(e => e.field === 'name')).toBe(true);
400
+ expect(errors.some(e => e.field === 'sku')).toBe(true);
401
+ expect(errors.some(e => e.field === 'price')).toBe(true);
402
+ expect(errors.some(e => e.field === 'category')).toBe(true);
403
+ expect(errors.some(e => e.field === 'description')).toBe(true);
404
+ });
405
+ });
406
+
407
+ describe('Form Reset and State Management', () => {
408
+ const simpleSchema = {
409
+ type: 'object',
410
+ properties: {
411
+ field1: { type: 'string', title: 'Field 1', minLength: 1 },
412
+ field2: { type: 'number', title: 'Field 2' }
413
+ },
414
+ required: ['field1']
415
+ };
416
+
417
+ it('should reset form data and errors', async () => {
418
+ const wrapper = mount(JsonSchemaForm, {
419
+ props: {
420
+ schema: simpleSchema,
421
+ modelValue: { field1: 'test', field2: 42 }
422
+ }
423
+ });
424
+
425
+ const vm = wrapper.vm as any;
426
+
427
+ // Trigger validation error by submitting with invalid data
428
+ await wrapper.setProps({ modelValue: { field1: '', field2: 42 } });
429
+ await wrapper.find('form').trigger('submit');
430
+ await nextTick();
431
+
432
+ expect(vm.errors.field1).toBeDefined();
433
+
434
+ // Reset form
435
+ vm.reset();
436
+ await nextTick();
437
+
438
+ // Should emit empty modelValue
439
+ const updateEvents = wrapper.emitted('update:modelValue');
440
+ expect(updateEvents).toBeDefined();
441
+ const lastUpdate = updateEvents?.[updateEvents.length - 1]?.[0] as any;
442
+ expect(Object.keys(lastUpdate).length).toBe(0);
443
+
444
+ // Errors should be cleared
445
+ expect(Object.keys(vm.errors).length).toBe(0);
446
+ expect(Object.keys(vm.touched).length).toBe(0);
447
+ });
448
+
449
+ it('should track touched fields', async () => {
450
+ const wrapper = mount(JsonSchemaForm, {
451
+ props: {
452
+ schema: simpleSchema,
453
+ modelValue: {}
454
+ }
455
+ });
456
+
457
+ const vm = wrapper.vm as any;
458
+
459
+ // Initially no fields touched
460
+ expect(vm.touched.field1).toBeUndefined();
461
+
462
+ // User interacts with field
463
+ vm.updateField('field1', 'test');
464
+ await nextTick();
465
+
466
+ // Field should be marked as touched
467
+ expect(vm.touched.field1).toBe(true);
468
+ });
469
+ });
470
+
471
+ describe('v-model Integration', () => {
472
+ const schema = {
473
+ type: 'object',
474
+ properties: {
475
+ name: { type: 'string', title: 'Name' },
476
+ age: { type: 'number', title: 'Age' }
477
+ }
478
+ };
479
+
480
+ it('should sync with v-model', async () => {
481
+ const wrapper = mount(JsonSchemaForm, {
482
+ props: {
483
+ schema,
484
+ modelValue: { name: 'John', age: 30 }
485
+ }
486
+ });
487
+
488
+ const vm = wrapper.vm as any;
489
+
490
+ // Change field value
491
+ vm.updateField('name', 'Jane');
492
+ await nextTick();
493
+
494
+ // Should emit update:modelValue
495
+ const updateEvents = wrapper.emitted('update:modelValue');
496
+ expect(updateEvents).toBeDefined();
497
+
498
+ const lastUpdate = updateEvents?.[updateEvents.length - 1]?.[0] as any;
499
+ expect(lastUpdate.name).toBe('Jane');
500
+ expect(lastUpdate.age).toBe(30);
501
+ });
502
+
503
+ it('should react to external modelValue changes', async () => {
504
+ const wrapper = mount(JsonSchemaForm, {
505
+ props: {
506
+ schema,
507
+ modelValue: { name: 'John', age: 30 }
508
+ }
509
+ });
510
+
511
+ // Update modelValue externally
512
+ await wrapper.setProps({
513
+ modelValue: { name: 'Jane', age: 25 }
514
+ });
515
+ await nextTick();
516
+
517
+ // Form should reflect new values in computed property
518
+ const vm = wrapper.vm as any;
519
+ expect(vm.formData.name).toBe('Jane');
520
+ expect(vm.formData.age).toBe(25);
521
+ });
522
+ });
523
+
524
+ describe('Loading State', () => {
525
+ const schema = {
526
+ type: 'object',
527
+ properties: {
528
+ field: { type: 'string', title: 'Field' }
529
+ }
530
+ };
531
+
532
+ it('should pass loading prop to components', async () => {
533
+ const wrapper = mount(JsonSchemaForm, {
534
+ props: {
535
+ schema,
536
+ modelValue: {},
537
+ loading: true
538
+ }
539
+ });
540
+
541
+ // Loading prop should be passed to form
542
+ expect(wrapper.props('loading')).toBe(true);
543
+ });
544
+
545
+ it('should not be loading by default', async () => {
546
+ const wrapper = mount(JsonSchemaForm, {
547
+ props: {
548
+ schema,
549
+ modelValue: {}
550
+ }
551
+ });
552
+
553
+ expect(wrapper.props('loading')).toBe(false);
554
+ });
555
+ });
556
+
557
+ describe('Custom UI Schema', () => {
558
+ const schema = {
559
+ type: 'object',
560
+ properties: {
561
+ password: { type: 'string', title: 'Password' },
562
+ volume: { type: 'integer', title: 'Volume', minimum: 0, maximum: 100 }
563
+ }
564
+ };
565
+
566
+ it('should render password field when specified in uiSchema', async () => {
567
+ const uiSchema = {
568
+ password: { 'ui:widget': 'password' }
569
+ };
570
+
571
+ const wrapper = mount(JsonSchemaForm, {
572
+ props: {
573
+ schema,
574
+ uiSchema,
575
+ modelValue: {}
576
+ }
577
+ });
578
+
579
+ const vm = wrapper.vm as any;
580
+ expect(vm.getWidget('password')).toBe('password');
581
+ expect(vm.getInputType('password')).toBe('password');
582
+ });
583
+
584
+ it('should render slider when specified in uiSchema', async () => {
585
+ const uiSchema = {
586
+ volume: { 'ui:widget': 'slider' }
587
+ };
588
+
589
+ const wrapper = mount(JsonSchemaForm, {
590
+ props: {
591
+ schema,
592
+ uiSchema,
593
+ modelValue: {}
594
+ }
595
+ });
596
+
597
+ const vm = wrapper.vm as any;
598
+ expect(vm.getWidget('volume')).toBe('slider');
599
+ });
600
+ });
601
+ });