@weni/unnnic-system 3.28.2-alpha.3 → 3.29.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 (40) hide show
  1. package/dist/index.d.ts +12 -324
  2. package/dist/style.css +1 -1
  3. package/dist/unnnic.mjs +7345 -7474
  4. package/dist/unnnic.umd.js +29 -29
  5. package/package.json +2 -2
  6. package/src/components/Button/__tests__/Button.spec.js +81 -0
  7. package/src/components/Button/__tests__/ButtonIcon.spec.js +56 -0
  8. package/src/components/Carousel/TagCarousel.vue +8 -2
  9. package/src/components/Carousel/__tests__/TagCarousel.spec.js +258 -0
  10. package/src/components/ChartLine/__tests__/ChartLine.spec.js +170 -0
  11. package/src/components/Checkbox/__tests__/Checkbox.spec.js +14 -0
  12. package/src/components/CheckboxGroup/__tests__/CheckboxGroup.spec.js +62 -0
  13. package/src/components/DatePicker/DatePicker.vue +1 -0
  14. package/src/components/DatePicker/__tests__/DatePicker.spec.js +364 -0
  15. package/src/components/Disclaimer/__tests__/Disclaimer.spec.js +16 -0
  16. package/src/components/DropArea/DropArea.vue +1 -2
  17. package/src/components/DropArea/__tests__/DropArea.spec.js +333 -0
  18. package/src/components/MultiSelect/__tests__/MultiSelect.spec.js +109 -2
  19. package/src/components/MultiSelect/index.vue +1 -1
  20. package/src/components/PageHeader/__tests__/PageHeader.spec.js +97 -0
  21. package/src/components/Pagination/__tests__/Pagination.spec.js +99 -0
  22. package/src/components/Radio/__test__/Radio.spec.js +67 -0
  23. package/src/components/RadioGroup/__tests__/RadioGroup.spec.js +100 -0
  24. package/src/components/Slider/__tests__/Slider.spec.js +321 -0
  25. package/src/components/Table/Table.vue +2 -21
  26. package/src/components/Table/TableRow.vue +3 -28
  27. package/src/components/Tag/DefaultTag.vue +4 -3
  28. package/src/components/Tag/__tests__/DefaultTag.spec.js +51 -0
  29. package/src/components/Tag/__tests__/Tag.spec.js +82 -0
  30. package/src/components/TextArea/__test__/TextArea.spec.js +19 -0
  31. package/src/components/ToolTip/__tests__/ToolTip.spec.js +82 -1
  32. package/src/components/index.ts +0 -13
  33. package/src/stories/Table.stories.js +2 -75
  34. package/src/components/ui/table/Table.vue +0 -29
  35. package/src/components/ui/table/TableBody.vue +0 -14
  36. package/src/components/ui/table/TableCell.vue +0 -28
  37. package/src/components/ui/table/TableHead.vue +0 -27
  38. package/src/components/ui/table/TableHeader.vue +0 -24
  39. package/src/components/ui/table/TableRow.vue +0 -47
  40. package/src/components/ui/table/index.ts +0 -6
@@ -198,6 +198,7 @@
198
198
  <div
199
199
  v-if="size !== 'small'"
200
200
  class="options-container"
201
+ :style="{ flexDirection: containerOptionsFlexDirection }"
201
202
  data-testid="date-picker-options"
202
203
  >
203
204
  <div
@@ -24,6 +24,11 @@ const findFirstSelectableDay = (wrapper) =>
24
24
  .findAll('[data-testid="date-picker-day"]')
25
25
  .find((day) => day.classes().includes('selectable'));
26
26
 
27
+ const findSelectableDays = (wrapper) =>
28
+ wrapper
29
+ .findAll('[data-testid="date-picker-day"]')
30
+ .filter((day) => day.classes().includes('selectable'));
31
+
27
32
  describe('DatePicker.vue', () => {
28
33
  let wrapper;
29
34
 
@@ -224,4 +229,363 @@ describe('DatePicker.vue', () => {
224
229
 
225
230
  expect(hasOutOfRange).toBe(true);
226
231
  });
232
+
233
+ it('selects range when start and end differ and a new date is clicked', async () => {
234
+ const first = findFirstSelectableDay(wrapper);
235
+ const days = wrapper
236
+ .findAll('[data-testid="date-picker-day"]')
237
+ .filter((day) => day.classes().includes('selectable'));
238
+
239
+ await first.trigger('click');
240
+ await days[5].trigger('click');
241
+ await days[10].trigger('click');
242
+
243
+ expect(wrapper.vm.startDate).toBeTruthy();
244
+ expect(wrapper.vm.endDate).toBe(wrapper.vm.startDate);
245
+ });
246
+
247
+ it('extends end date when selecting a later day from a single-day range', async () => {
248
+ const days = wrapper
249
+ .findAll('[data-testid="date-picker-day"]')
250
+ .filter((day) => day.classes().includes('selectable'));
251
+
252
+ await days[0].trigger('click');
253
+ await days[4].trigger('click');
254
+
255
+ expect(wrapper.vm.endDate).not.toBe(wrapper.vm.startDate);
256
+ });
257
+
258
+ it('moves start date when selecting an earlier day from a single-day range', async () => {
259
+ const days = wrapper
260
+ .findAll('[data-testid="date-picker-day"]')
261
+ .filter((day) => day.classes().includes('selectable'));
262
+
263
+ await days[10].trigger('click');
264
+ const earlierDay = days[2];
265
+ await earlierDay.trigger('click');
266
+
267
+ expect(wrapper.vm.startDate).toBeTruthy();
268
+ expect(wrapper.vm.startDate).toContain(String(earlierDay.text()));
269
+ });
270
+
271
+ it('resets range when clicking the same selected day twice', async () => {
272
+ const day = findFirstSelectableDay(wrapper);
273
+ const dayText = day.text();
274
+
275
+ await day.trigger('click');
276
+ await day.trigger('click');
277
+
278
+ expect(wrapper.vm.startDate).toContain(dayText);
279
+ expect(wrapper.vm.endDate).toBe(wrapper.vm.startDate);
280
+ });
281
+
282
+ it('computes last-N-months period correctly', () => {
283
+ wrapper = factory({
284
+ periodBaseDate: '2025-06-15',
285
+ options: [{ name: 'Last 3 months', id: 'last-3-months' }],
286
+ });
287
+
288
+ const { startDate, endDate } =
289
+ wrapper.vm.getStartAndEndDateByPeriod('last-3-months');
290
+
291
+ expect(startDate).toBeTruthy();
292
+ expect(endDate).toBeTruthy();
293
+ });
294
+
295
+ it('computes current-month period correctly', () => {
296
+ wrapper = factory({
297
+ periodBaseDate: '2025-06-15',
298
+ options: [{ name: 'Current month', id: 'current-month' }],
299
+ });
300
+
301
+ const { startDate, endDate } =
302
+ wrapper.vm.getStartAndEndDateByPeriod('current-month');
303
+
304
+ expect(startDate).toContain('1 2025');
305
+ expect(endDate).toContain('15 2025');
306
+ });
307
+
308
+ it('parses non-ISO periodBaseDate format', () => {
309
+ wrapper = factory({
310
+ periodBaseDate: 'June 15, 2025',
311
+ options: [{ name: 'Last 7 days', id: 'last-7-days' }],
312
+ });
313
+
314
+ const { startDate, endDate } =
315
+ wrapper.vm.getStartAndEndDateByPeriod('last-7-days');
316
+
317
+ expect(startDate).toBeTruthy();
318
+ expect(endDate).toBeTruthy();
319
+ });
320
+
321
+ it('falls back to current date for invalid periodBaseDate', () => {
322
+ wrapper = factory({
323
+ periodBaseDate: 'invalid-date',
324
+ options: [{ name: 'Last 7 days', id: 'last-7-days' }],
325
+ });
326
+
327
+ const { startDate, endDate } =
328
+ wrapper.vm.getStartAndEndDateByPeriod('last-7-days');
329
+
330
+ expect(startDate).toBeTruthy();
331
+ expect(endDate).toBeTruthy();
332
+ });
333
+
334
+ it('uses i18n pluralization with three forms', () => {
335
+ wrapper = factory({
336
+ translations: {
337
+ items: {
338
+ en: 'one|few|many',
339
+ },
340
+ },
341
+ });
342
+
343
+ expect(wrapper.vm.i18n('items', 0)).toBe('one');
344
+ expect(wrapper.vm.i18n('items', 1)).toBe('few');
345
+ expect(wrapper.vm.i18n('items', 2)).toBe('many');
346
+ });
347
+
348
+ it('uses i18n pluralization with two forms', () => {
349
+ wrapper = factory({
350
+ translations: {
351
+ items: {
352
+ en: 'singular|plural',
353
+ },
354
+ },
355
+ });
356
+
357
+ expect(wrapper.vm.i18n('items', 1)).toBe('singular');
358
+ expect(wrapper.vm.i18n('items', 2)).toBe('plural');
359
+ });
360
+
361
+ it('replaces variables in i18n text', () => {
362
+ wrapper = factory({
363
+ translations: {
364
+ greeting: {
365
+ en: 'Hello {name}',
366
+ },
367
+ },
368
+ });
369
+
370
+ expect(wrapper.vm.i18n('greeting', { name: 'Weni' })).toBe('Hello Weni');
371
+ });
372
+
373
+ it('renders popover variant class', () => {
374
+ wrapper = factory({ variant: 'popover' });
375
+ expect(wrapper.classes()).toContain('unnnic-date-picker--popover');
376
+ });
377
+
378
+ it('hides preset options when hideOptions is true', () => {
379
+ wrapper = factory({
380
+ hideOptions: true,
381
+ options: [{ name: 'Last 7 days', id: 'last-7-days' }],
382
+ });
383
+
384
+ expect(wrapper.find('[data-testid="date-picker-option"]').exists()).toBe(
385
+ false,
386
+ );
387
+ expect(wrapper.find('[data-testid="date-picker-options"]').exists()).toBe(
388
+ true,
389
+ );
390
+ });
391
+
392
+ it('navigates month view with navigation buttons', async () => {
393
+ wrapper = factory({
394
+ type: 'month',
395
+ options: [{ name: 'Last 12 months', id: 'last-12-months' }],
396
+ });
397
+
398
+ const initialReference = wrapper.vm.referenceDate;
399
+ await wrapper
400
+ .find('[data-testid="date-picker-month-nav-right"]')
401
+ .trigger('click');
402
+
403
+ expect(wrapper.vm.referenceDate).not.toBe(initialReference);
404
+ });
405
+
406
+ it('navigates year view with navigation buttons', async () => {
407
+ wrapper = factory({
408
+ type: 'year',
409
+ options: [{ name: 'Previous year', id: 'previous-year' }],
410
+ });
411
+
412
+ const initialReference = wrapper.vm.referenceDate;
413
+ await wrapper
414
+ .find('[data-testid="date-picker-year-nav-left"]')
415
+ .trigger('click');
416
+
417
+ expect(wrapper.vm.referenceDate).not.toBe(initialReference);
418
+ });
419
+
420
+ it('updates equivalent option name on submit for matched period', async () => {
421
+ await wrapper.vm.autoSelect('last-7-days');
422
+ await wrapper.find('[data-testid="date-picker-submit"]').trigger('click');
423
+
424
+ expect(wrapper.emitted('update:equivalentOption')[0][0]).toBe(
425
+ 'Last 7 days',
426
+ );
427
+ });
428
+
429
+ it('marks custom selection when dates do not match a preset', async () => {
430
+ const day = findFirstSelectableDay(wrapper);
431
+ await day.trigger('click');
432
+
433
+ expect(wrapper.vm.optionSelected).toBe('custom');
434
+ });
435
+
436
+ it('does nothing when clicking a custom preset option', async () => {
437
+ wrapper = factory({
438
+ options: [
439
+ { name: 'Last 7 days', id: 'last-7-days' },
440
+ { name: 'Custom', id: 'custom' },
441
+ ],
442
+ });
443
+
444
+ const customOption = wrapper
445
+ .findAll('[data-testid="date-picker-option"]')
446
+ .find((option) => option.text() === 'Custom');
447
+
448
+ await customOption.trigger('click');
449
+ expect(wrapper.vm.optionSelected).not.toBe('last-7-days');
450
+ });
451
+
452
+ it('highlights dates when only start date is set', async () => {
453
+ wrapper = factory();
454
+ wrapper.vm.startDate = wrapper.vm.referenceDate;
455
+ wrapper.vm.endDate = '';
456
+ await wrapper.vm.$nextTick();
457
+
458
+ const dates = wrapper.vm.getDatesOfTheMonth(wrapper.vm.referenceDate);
459
+ expect(dates.some((date) => date.properties.includes('highlighted'))).toBe(
460
+ true,
461
+ );
462
+ });
463
+
464
+ it('highlights dates when only end date is set', async () => {
465
+ wrapper = factory();
466
+ wrapper.vm.startDate = '';
467
+ wrapper.vm.endDate = wrapper.vm.referenceDate;
468
+ await wrapper.vm.$nextTick();
469
+
470
+ const dates = wrapper.vm.getDatesOfTheMonth(wrapper.vm.referenceDate);
471
+ expect(dates.some((date) => date.properties.includes('highlighted'))).toBe(
472
+ true,
473
+ );
474
+ });
475
+
476
+ it('applies column-reverse layout when hideOptions is true', () => {
477
+ wrapper = factory({
478
+ hideOptions: true,
479
+ size: 'large',
480
+ options: [{ name: 'Last 7 days', id: 'last-7-days' }],
481
+ });
482
+
483
+ expect(wrapper.find('[data-testid="date-picker-options"]').exists()).toBe(
484
+ true,
485
+ );
486
+ });
487
+
488
+ it('highlights year range with only start year selected', async () => {
489
+ wrapper = factory({ type: 'year' });
490
+ wrapper.vm.startDate = wrapper.vm.referenceDate;
491
+ wrapper.vm.endDate = '';
492
+ await wrapper.vm.$nextTick();
493
+
494
+ const highlighted = wrapper
495
+ .findAll('[data-testid="date-picker-year-cell"]')
496
+ .filter((cell) => cell.classes().includes('highlighted'));
497
+
498
+ expect(highlighted.length).toBeGreaterThan(0);
499
+ });
500
+
501
+ it('highlights year range with only end year selected', async () => {
502
+ wrapper = factory({ type: 'year' });
503
+ wrapper.vm.startDate = '';
504
+ wrapper.vm.endDate = wrapper.vm.referenceDate;
505
+ await wrapper.vm.$nextTick();
506
+
507
+ const highlighted = wrapper
508
+ .findAll('[data-testid="date-picker-year-cell"]')
509
+ .filter((cell) => cell.classes().includes('highlighted'));
510
+
511
+ expect(highlighted.length).toBeGreaterThan(0);
512
+ });
513
+
514
+ it('moves range start when selecting an interior day closer to start', async () => {
515
+ const days = findSelectableDays(wrapper);
516
+
517
+ await days[2].trigger('click');
518
+ await days[10].trigger('click');
519
+ await days[4].trigger('click');
520
+
521
+ expect(wrapper.vm.startDate).toBeTruthy();
522
+ expect(wrapper.vm.endDate).toBeTruthy();
523
+ expect(wrapper.emitted('change')).toBeTruthy();
524
+ });
525
+
526
+ it('moves range end when selecting an interior day closer to end', async () => {
527
+ const days = findSelectableDays(wrapper);
528
+
529
+ await days[2].trigger('click');
530
+ await days[10].trigger('click');
531
+ await days[9].trigger('click');
532
+
533
+ expect(wrapper.vm.startDate).toBeTruthy();
534
+ expect(wrapper.vm.endDate).toBeTruthy();
535
+ expect(wrapper.emitted('change')).toBeTruthy();
536
+ });
537
+
538
+ it('highlights month range with only start month selected', async () => {
539
+ wrapper = factory({ type: 'month' });
540
+ wrapper.vm.startDate = '3 1 2026';
541
+ wrapper.vm.endDate = '';
542
+ await wrapper.vm.$nextTick();
543
+
544
+ const highlighted = wrapper
545
+ .findAll('[data-testid="date-picker-month-cell"]')
546
+ .filter((cell) => cell.classes().includes('highlighted'));
547
+
548
+ expect(highlighted.length).toBeGreaterThan(0);
549
+ });
550
+
551
+ it('highlights month range with only end month selected', async () => {
552
+ wrapper = factory({ type: 'month' });
553
+ wrapper.vm.startDate = '';
554
+ wrapper.vm.endDate = '6 1 2026';
555
+ await wrapper.vm.$nextTick();
556
+
557
+ const highlighted = wrapper
558
+ .findAll('[data-testid="date-picker-month-cell"]')
559
+ .filter((cell) => cell.classes().includes('highlighted'));
560
+
561
+ expect(highlighted.length).toBeGreaterThan(0);
562
+ });
563
+
564
+ it('navigates month view to previous year', async () => {
565
+ wrapper = factory({
566
+ type: 'month',
567
+ options: [{ name: 'Last 12 months', id: 'last-12-months' }],
568
+ });
569
+
570
+ const initialReference = wrapper.vm.referenceDate;
571
+ await wrapper
572
+ .find('[data-testid="date-picker-month-nav-left"]')
573
+ .trigger('click');
574
+
575
+ expect(wrapper.vm.referenceDate).not.toBe(initialReference);
576
+ });
577
+
578
+ it('navigates year view to next range', async () => {
579
+ wrapper = factory({
580
+ type: 'year',
581
+ options: [{ name: 'Previous year', id: 'previous-year' }],
582
+ });
583
+
584
+ const initialReference = wrapper.vm.referenceDate;
585
+ await wrapper
586
+ .find('[data-testid="date-picker-year-nav-right"]')
587
+ .trigger('click');
588
+
589
+ expect(wrapper.vm.referenceDate).not.toBe(initialReference);
590
+ });
227
591
  });
@@ -14,6 +14,22 @@ const mountComponent = (props = {}, slots = {}) =>
14
14
  });
15
15
 
16
16
  describe('Disclaimer', () => {
17
+ it('renders title when provided', () => {
18
+ const wrapper = mountComponent({ title: 'Disclaimer title' });
19
+
20
+ expect(wrapper.find('[data-testid="disclaimer-title"]').text()).toBe(
21
+ 'Disclaimer title',
22
+ );
23
+ });
24
+
25
+ it('uses explicit type when legacy icon props are absent', () => {
26
+ const wrapper = mountComponent({ type: 'success', title: 'Success' });
27
+
28
+ expect(
29
+ wrapper.findComponent('[data-testid="disclaimer-icon"]').props('icon'),
30
+ ).toBe('check_circle');
31
+ });
32
+
17
33
  it('hides title when no title is provided', () => {
18
34
  const wrapper = mountComponent({ title: '' });
19
35
 
@@ -72,7 +72,6 @@ import UnnnicIcon from '../Icon.vue';
72
72
  const isDragging = ref(false);
73
73
  const hasError = ref(false);
74
74
  const dragEnterCounter = ref(0);
75
- const file = ref();
76
75
  const fileRef = useTemplateRef('file');
77
76
 
78
77
  const props = defineProps({
@@ -195,7 +194,7 @@ function handleFileChange(event) {
195
194
  addFiles(files);
196
195
  }
197
196
 
198
- file.value.value = '';
197
+ fileRef.value.value = '';
199
198
  }
200
199
 
201
200
  function setErrorState() {