@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,562 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { nextTick } from 'vue';
4
+ import Table from './Table.vue';
5
+
6
+ /**
7
+ * Integration tests for Table component
8
+ * Tests user interactions, real-world scenarios, and component integration
9
+ */
10
+ describe('Table integration tests', () => {
11
+ const mockColumns = [
12
+ { name: 'ID', value: 'id' },
13
+ { name: 'Name', value: 'name' },
14
+ { name: 'Email', value: 'email' },
15
+ { name: 'Status', value: 'status' }
16
+ ];
17
+
18
+ const mockItems = [
19
+ { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
20
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
21
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' },
22
+ { id: 4, name: 'Alice Williams', email: 'alice@example.com', status: 'pending' },
23
+ { id: 5, name: 'Charlie Brown', email: 'charlie@example.com', status: 'active' }
24
+ ];
25
+
26
+ const globalStubs = {
27
+ uiTable: true,
28
+ uiPagination: true,
29
+ uiDropdown: true,
30
+ uiSmartFilterMultipleV2: true,
31
+ uiNoResults: true
32
+ };
33
+
34
+ describe('User List Management', () => {
35
+ it('should display user list with pagination', async () => {
36
+ const wrapper = mount(Table, {
37
+ props: {
38
+ items: mockItems,
39
+ columns: mockColumns,
40
+ currentPage: 1,
41
+ totalPages: 2,
42
+ totalItems: 5,
43
+ itemsPerPage: 3,
44
+ showPagination: true
45
+ },
46
+ global: { stubs: globalStubs }
47
+ });
48
+
49
+ const vm = wrapper.vm as any;
50
+ expect(vm.tableItems).toHaveLength(5);
51
+ expect(vm.showTable).toBe(true);
52
+ expect(vm.showPagination).toBe(true);
53
+ });
54
+
55
+ it('should handle sorting by name', async () => {
56
+ const wrapper = mount(Table, {
57
+ props: {
58
+ items: mockItems,
59
+ columns: mockColumns,
60
+ orderedBy: '',
61
+ orderDirection: 'asc'
62
+ },
63
+ global: { stubs: globalStubs }
64
+ });
65
+
66
+ const vm = wrapper.vm as any;
67
+ vm.handleOrderBy({ value: 'name', orderDirection: 'desc' });
68
+ await nextTick();
69
+
70
+ expect(wrapper.emitted('update:orderedBy')?.[0]).toEqual(['name']);
71
+ expect(wrapper.emitted('update:orderDirection')?.[0]).toEqual(['desc']);
72
+ expect(wrapper.emitted('order-changed')?.[0]).toEqual(['name', 'desc']);
73
+ });
74
+
75
+ it('should handle page navigation', async () => {
76
+ const wrapper = mount(Table, {
77
+ props: {
78
+ items: mockItems,
79
+ columns: mockColumns,
80
+ currentPage: 1,
81
+ totalPages: 3
82
+ },
83
+ global: { stubs: globalStubs }
84
+ });
85
+
86
+ const vm = wrapper.vm as any;
87
+
88
+ // Navigate to page 2
89
+ vm.handleChangePage(2);
90
+ await nextTick();
91
+
92
+ expect(wrapper.emitted('update:currentPage')?.[0]).toEqual([2]);
93
+ expect(wrapper.emitted('page-changed')?.[0]).toEqual([2]);
94
+
95
+ // Navigate to page 3
96
+ vm.handleChangePage(3);
97
+ await nextTick();
98
+
99
+ expect(wrapper.emitted('update:currentPage')?.[1]).toEqual([3]);
100
+ });
101
+
102
+ it('should change page size and reset to page 1', async () => {
103
+ const wrapper = mount(Table, {
104
+ props: {
105
+ items: mockItems,
106
+ columns: mockColumns,
107
+ currentPage: 3,
108
+ itemsPerPage: 10
109
+ },
110
+ global: { stubs: globalStubs }
111
+ });
112
+
113
+ const vm = wrapper.vm as any;
114
+ vm.handleChangePageSize({ value: '25' });
115
+ await nextTick();
116
+
117
+ expect(wrapper.emitted('update:itemsPerPage')?.[0]).toEqual([25]);
118
+ expect(wrapper.emitted('update:currentPage')?.[0]).toEqual([1]);
119
+ expect(wrapper.emitted('items-per-page-changed')?.[0]).toEqual([25]);
120
+ });
121
+ });
122
+
123
+ describe('Selection Management', () => {
124
+ it('should select all items', async () => {
125
+ const wrapper = mount(Table, {
126
+ props: {
127
+ items: mockItems,
128
+ columns: mockColumns
129
+ },
130
+ global: { stubs: globalStubs }
131
+ });
132
+
133
+ const vm = wrapper.vm as any;
134
+ vm.handleSelectAllItems();
135
+ await nextTick();
136
+
137
+ expect(vm.selectedItemIds.size).toBe(5);
138
+ expect(wrapper.emitted('select-all-items')).toBeDefined();
139
+ expect(wrapper.emitted('update:selected')).toBeDefined();
140
+
141
+ const selectedItems = wrapper.emitted('update:selected')?.[0]?.[0] as any[];
142
+ expect(selectedItems).toHaveLength(5);
143
+ });
144
+
145
+ it('should deselect all items', async () => {
146
+ const wrapper = mount(Table, {
147
+ props: {
148
+ items: mockItems,
149
+ columns: mockColumns
150
+ },
151
+ global: { stubs: globalStubs }
152
+ });
153
+
154
+ const vm = wrapper.vm as any;
155
+
156
+ // First select all
157
+ vm.handleSelectAllItems();
158
+ await nextTick();
159
+
160
+ // Then deselect all
161
+ vm.handleDeselectAllItems();
162
+ await nextTick();
163
+
164
+ expect(vm.selectedItemIds.size).toBe(0);
165
+ expect(wrapper.emitted('deselect-all-items')).toBeDefined();
166
+
167
+ const selectedItems = wrapper.emitted('update:selected')?.slice(-1)[0]?.[0] as any[];
168
+ expect(selectedItems).toHaveLength(0);
169
+ });
170
+
171
+ it('should handle table action with selected items', async () => {
172
+ const wrapper = mount(Table, {
173
+ props: {
174
+ items: mockItems,
175
+ columns: mockColumns,
176
+ actions: [
177
+ { name: 'Delete', id: 'delete' },
178
+ { name: 'Export', id: 'export' }
179
+ ]
180
+ },
181
+ global: { stubs: globalStubs }
182
+ });
183
+
184
+ const vm = wrapper.vm as any;
185
+ vm.handleTableAction({ action: 'delete', items: [1, 3, 5] });
186
+ await nextTick();
187
+
188
+ expect(wrapper.emitted('table-action')?.[0]).toEqual([
189
+ { action: 'delete', items: [1, 3, 5] }
190
+ ]);
191
+ expect(vm.selectedItemIds.size).toBe(3);
192
+ });
193
+
194
+ it('should clear selection programmatically', async () => {
195
+ const wrapper = mount(Table, {
196
+ props: {
197
+ items: mockItems,
198
+ columns: mockColumns
199
+ },
200
+ global: { stubs: globalStubs }
201
+ });
202
+
203
+ const vm = wrapper.vm as any;
204
+
205
+ // Select some items
206
+ vm.selectedItemIds = new Set([1, 2, 3]);
207
+
208
+ // Clear selection
209
+ vm.clearSelection();
210
+ await nextTick();
211
+
212
+ expect(vm.selectedItemIds.size).toBe(0);
213
+ expect(vm.resetSelected).toBe(true);
214
+ });
215
+ });
216
+
217
+ describe('Filtering and Search', () => {
218
+ it('should handle smart filter application', async () => {
219
+ const availableCategories = [
220
+ {
221
+ name: 'name',
222
+ label: 'Name',
223
+ componentType: 'uiInput' as const,
224
+ defaultProps: {}
225
+ },
226
+ {
227
+ name: 'status',
228
+ label: 'Status',
229
+ componentType: 'uiSelect' as const,
230
+ defaultProps: {}
231
+ }
232
+ ];
233
+
234
+ const wrapper = mount(Table, {
235
+ props: {
236
+ items: mockItems,
237
+ columns: mockColumns,
238
+ availableCategories,
239
+ showFilters: true
240
+ },
241
+ global: { stubs: globalStubs }
242
+ });
243
+
244
+ const vm = wrapper.vm as any;
245
+ const filters = { name: 'John', status: 'active' };
246
+
247
+ vm.handleSmartFiltersSent(filters);
248
+ await nextTick();
249
+
250
+ expect(vm.hasUserSearched).toBe(true);
251
+ expect(wrapper.emitted('smart-filters-sent')?.[0]).toEqual([filters]);
252
+ expect(wrapper.emitted('multiple-filters-applied')?.[0]).toEqual([filters]);
253
+ });
254
+
255
+ it('should show search no results after filtering', async () => {
256
+ const wrapper = mount(Table, {
257
+ props: {
258
+ items: [],
259
+ columns: mockColumns,
260
+ loading: false
261
+ },
262
+ global: { stubs: globalStubs }
263
+ });
264
+
265
+ const vm = wrapper.vm as any;
266
+
267
+ // Simulate user search
268
+ vm.handleSmartFiltersSent({ name: 'NonExistent' });
269
+ await nextTick();
270
+
271
+ expect(vm.showSearchNoResults).toBe(true);
272
+ expect(vm.showInitialNoResults).toBe(false);
273
+ });
274
+
275
+ it('should clear filters', async () => {
276
+ const wrapper = mount(Table, {
277
+ props: {
278
+ items: mockItems,
279
+ columns: mockColumns
280
+ },
281
+ global: { stubs: globalStubs }
282
+ });
283
+
284
+ const vm = wrapper.vm as any;
285
+ vm.handleSmartFiltersCleared();
286
+ await nextTick();
287
+
288
+ expect(wrapper.emitted('smart-filters-cleared')).toBeDefined();
289
+ });
290
+
291
+ it('should delete individual filter', async () => {
292
+ const wrapper = mount(Table, {
293
+ props: {
294
+ items: mockItems,
295
+ columns: mockColumns
296
+ },
297
+ global: { stubs: globalStubs }
298
+ });
299
+
300
+ const vm = wrapper.vm as any;
301
+ vm.handleSmartFilterDeleted(0);
302
+ await nextTick();
303
+
304
+ expect(wrapper.emitted('smart-filter-deleted')?.[0]).toEqual([0]);
305
+ });
306
+ });
307
+
308
+ describe('Column Visibility', () => {
309
+ it('should hide and show columns', async () => {
310
+ const wrapper = mount(Table, {
311
+ props: {
312
+ items: mockItems,
313
+ columns: mockColumns
314
+ },
315
+ global: { stubs: globalStubs }
316
+ });
317
+
318
+ const vm = wrapper.vm as any;
319
+
320
+ // Hide column 1
321
+ vm.handleColumnsVisibilityChanged({ index: 1, hidden: true });
322
+ await nextTick();
323
+
324
+ expect(vm.hiddenColumns).toContain(1);
325
+ expect(vm.getHiddenColumns()).toContain(1);
326
+
327
+ // Show column 1 again
328
+ vm.handleColumnsVisibilityChanged({ index: 1, hidden: false });
329
+ await nextTick();
330
+
331
+ expect(vm.hiddenColumns).not.toContain(1);
332
+ });
333
+
334
+ it('should handle multiple hidden columns', async () => {
335
+ const wrapper = mount(Table, {
336
+ props: {
337
+ items: mockItems,
338
+ columns: mockColumns
339
+ },
340
+ global: { stubs: globalStubs }
341
+ });
342
+
343
+ const vm = wrapper.vm as any;
344
+
345
+ // Hide multiple columns
346
+ vm.handleColumnsVisibilityChanged({ index: 0, hidden: true });
347
+ vm.handleColumnsVisibilityChanged({ index: 2, hidden: true });
348
+ await nextTick();
349
+
350
+ expect(vm.hiddenColumns).toHaveLength(2);
351
+ expect(vm.hiddenColumns).toContain(0);
352
+ expect(vm.hiddenColumns).toContain(2);
353
+ });
354
+ });
355
+
356
+ describe('Empty States', () => {
357
+ it('should show initial no results when no data', async () => {
358
+ const wrapper = mount(Table, {
359
+ props: {
360
+ items: [],
361
+ columns: mockColumns,
362
+ loading: false
363
+ },
364
+ global: { stubs: globalStubs }
365
+ });
366
+
367
+ const vm = wrapper.vm as any;
368
+ expect(vm.showInitialNoResults).toBe(true);
369
+ expect(vm.showTable).toBe(false);
370
+ });
371
+
372
+ it('should show loading state', async () => {
373
+ const wrapper = mount(Table, {
374
+ props: {
375
+ items: [],
376
+ columns: mockColumns,
377
+ loading: true
378
+ },
379
+ global: { stubs: globalStubs }
380
+ });
381
+
382
+ const vm = wrapper.vm as any;
383
+ expect(vm.showTable).toBe(true);
384
+ expect(vm.showInitialNoResults).toBe(false);
385
+ });
386
+
387
+ it('should handle no results action', async () => {
388
+ const wrapper = mount(Table, {
389
+ props: {
390
+ items: [],
391
+ columns: mockColumns,
392
+ loading: false,
393
+ noResults: {
394
+ title: 'No users found',
395
+ message: 'Create your first user',
396
+ actions: [{ action: 'create', text: 'Create User' }],
397
+ select: { name: '', value: '' }
398
+ }
399
+ },
400
+ global: { stubs: globalStubs }
401
+ });
402
+
403
+ const vm = wrapper.vm as any;
404
+ vm.handleNoResultsAction('create');
405
+ await nextTick();
406
+
407
+ expect(wrapper.emitted('no-results-action')?.[0]).toEqual(['create']);
408
+ });
409
+ });
410
+
411
+ describe('Refresh and Actions', () => {
412
+ it('should handle refresh action', async () => {
413
+ const wrapper = mount(Table, {
414
+ props: {
415
+ items: mockItems,
416
+ columns: mockColumns,
417
+ showRefresh: true
418
+ },
419
+ global: { stubs: globalStubs }
420
+ });
421
+
422
+ const vm = wrapper.vm as any;
423
+ vm.handleDropdownAction({ value: 'refreshData' });
424
+ await nextTick();
425
+
426
+ expect(wrapper.emitted('refresh:data')).toBeDefined();
427
+ });
428
+
429
+ it('should handle table action button click', async () => {
430
+ const wrapper = mount(Table, {
431
+ props: {
432
+ items: mockItems,
433
+ columns: mockColumns,
434
+ tableActionButtons: [
435
+ { id: 'export', text: 'Export' },
436
+ { id: 'import', text: 'Import' }
437
+ ]
438
+ },
439
+ global: { stubs: globalStubs }
440
+ });
441
+
442
+ const vm = wrapper.vm as any;
443
+ vm.handleTableActionButtonClicked({ id: 'export', text: 'Export' });
444
+ await nextTick();
445
+
446
+ expect(wrapper.emitted('table-action-button-clicked')?.[0]).toEqual([
447
+ { id: 'export', text: 'Export' }
448
+ ]);
449
+ });
450
+
451
+ it('should handle modal action', async () => {
452
+ const wrapper = mount(Table, {
453
+ props: {
454
+ items: mockItems,
455
+ columns: mockColumns
456
+ },
457
+ global: { stubs: globalStubs }
458
+ });
459
+
460
+ const vm = wrapper.vm as any;
461
+ vm.handleModalAction({ modal: 'confirm-delete', action: 'confirm' });
462
+ await nextTick();
463
+
464
+ expect(wrapper.emitted('modal-action')?.[0]).toEqual([
465
+ { modal: 'confirm-delete', action: 'confirm' }
466
+ ]);
467
+ });
468
+ });
469
+
470
+ describe('State Management', () => {
471
+ it('should reset selection when items change', async () => {
472
+ const wrapper = mount(Table, {
473
+ props: {
474
+ items: mockItems,
475
+ columns: mockColumns
476
+ },
477
+ global: { stubs: globalStubs }
478
+ });
479
+
480
+ const vm = wrapper.vm as any;
481
+ vm.resetSelected = true;
482
+
483
+ // Change items
484
+ await wrapper.setProps({ items: mockItems.slice(0, 3) });
485
+ await nextTick();
486
+
487
+ expect(vm.resetSelected).toBe(false);
488
+ });
489
+
490
+ it('should maintain hidden columns across updates', async () => {
491
+ const wrapper = mount(Table, {
492
+ props: {
493
+ items: mockItems,
494
+ columns: mockColumns
495
+ },
496
+ global: { stubs: globalStubs }
497
+ });
498
+
499
+ const vm = wrapper.vm as any;
500
+ vm.handleColumnsVisibilityChanged({ index: 1, hidden: true });
501
+
502
+ // Update items
503
+ await wrapper.setProps({ items: mockItems.slice(0, 2) });
504
+ await nextTick();
505
+
506
+ expect(vm.hiddenColumns).toContain(1);
507
+ });
508
+ });
509
+
510
+ describe('Complex Workflows', () => {
511
+ it('should handle complete user management workflow', async () => {
512
+ const wrapper = mount(Table, {
513
+ props: {
514
+ items: mockItems,
515
+ columns: mockColumns,
516
+ currentPage: 1,
517
+ totalPages: 2,
518
+ itemsPerPage: 3,
519
+ showPagination: true,
520
+ showFilters: true,
521
+ availableCategories: [
522
+ {
523
+ name: 'status',
524
+ label: 'Status',
525
+ componentType: 'uiSelect' as const,
526
+ defaultProps: {}
527
+ }
528
+ ]
529
+ },
530
+ global: { stubs: globalStubs }
531
+ });
532
+
533
+ const vm = wrapper.vm as any;
534
+
535
+ // 1. Apply filter
536
+ vm.handleSmartFiltersSent({ status: 'active' });
537
+ await nextTick();
538
+ expect(wrapper.emitted('smart-filters-sent')).toBeDefined();
539
+
540
+ // 2. Sort by name
541
+ vm.handleOrderBy({ value: 'name', orderDirection: 'asc' });
542
+ await nextTick();
543
+ expect(wrapper.emitted('order-changed')).toBeDefined();
544
+
545
+ // 3. Select items
546
+ vm.handleSelectAllItems();
547
+ await nextTick();
548
+ expect(vm.selectedItemIds.size).toBeGreaterThan(0);
549
+
550
+ // 4. Perform action
551
+ vm.handleTableAction({ action: 'export', items: Array.from(vm.selectedItemIds) });
552
+ await nextTick();
553
+ expect(wrapper.emitted('table-action')).toBeDefined();
554
+
555
+ // 5. Change page
556
+ vm.handleChangePage(2);
557
+ await nextTick();
558
+ expect(wrapper.emitted('page-changed')).toBeDefined();
559
+ expect(vm.resetSelected).toBe(true);
560
+ });
561
+ });
562
+ });