vueless 1.3.7-beta.2 → 1.3.7-beta.3

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 (32) hide show
  1. package/components.d.ts +1 -0
  2. package/components.ts +1 -0
  3. package/constants.d.ts +2 -0
  4. package/constants.js +2 -0
  5. package/package.json +2 -2
  6. package/ui.container-col/UCol.vue +0 -1
  7. package/ui.container-collapsible/UCollapsible.vue +190 -0
  8. package/ui.container-collapsible/config.ts +45 -0
  9. package/ui.container-collapsible/constants.ts +1 -0
  10. package/ui.container-collapsible/storybook/docs.mdx +17 -0
  11. package/ui.container-collapsible/storybook/stories.ts +261 -0
  12. package/ui.container-collapsible/tests/UCollapsible.test.ts +571 -0
  13. package/ui.container-collapsible/types.ts +57 -0
  14. package/ui.dropdown/UDropdown.vue +324 -0
  15. package/ui.dropdown/config.ts +27 -0
  16. package/ui.dropdown/constants.ts +1 -0
  17. package/ui.dropdown/storybook/docs.mdx +17 -0
  18. package/ui.dropdown/storybook/stories.ts +286 -0
  19. package/ui.dropdown/tests/UDropdown.test.ts +631 -0
  20. package/ui.dropdown/types.ts +127 -0
  21. package/ui.dropdown-badge/UDropdownBadge.vue +119 -227
  22. package/ui.dropdown-badge/config.ts +18 -15
  23. package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +201 -67
  24. package/ui.dropdown-button/UDropdownButton.vue +121 -226
  25. package/ui.dropdown-button/config.ts +32 -28
  26. package/ui.dropdown-button/tests/UDropdownButton.test.ts +189 -73
  27. package/ui.dropdown-link/UDropdownLink.vue +123 -233
  28. package/ui.dropdown-link/config.ts +15 -18
  29. package/ui.dropdown-link/tests/UDropdownLink.test.ts +190 -71
  30. package/ui.form-listbox/UListbox.vue +2 -3
  31. package/ui.form-listbox/config.ts +2 -2
  32. package/ui.form-select/config.ts +1 -1
@@ -15,10 +15,8 @@ describe("UDropdownLink.vue", () => {
15
15
  { value: 3, label: "Option 3" },
16
16
  ];
17
17
 
18
- // Props tests
19
18
  describe("Props", () => {
20
- // Label prop
21
- it("renders the correct label text", () => {
19
+ it("Label – renders the correct label text", () => {
22
20
  const label = "Dropdown Link";
23
21
 
24
22
  const component = mount(UDropdownLink, {
@@ -31,8 +29,7 @@ describe("UDropdownLink.vue", () => {
31
29
  expect(component.findComponent(ULink).props("label")).toBe(label);
32
30
  });
33
31
 
34
- // ModelValue prop
35
- it("selects the correct option based on modelValue", async () => {
32
+ it("ModelValue – selects the correct option based on modelValue", async () => {
36
33
  const modelValue = 2;
37
34
 
38
35
  const component = mount(UDropdownLink, {
@@ -48,8 +45,7 @@ describe("UDropdownLink.vue", () => {
48
45
  expect(component.findComponent(ULink).props("label")).toBe(selectedOption?.label);
49
46
  });
50
47
 
51
- // Multiple prop with modelValue
52
- it("handles multiple selections correctly", async () => {
48
+ it("Multiple handles multiple selections correctly", async () => {
53
49
  const modelValue = [1, 3];
54
50
 
55
51
  const component = mount(UDropdownLink, {
@@ -67,8 +63,7 @@ describe("UDropdownLink.vue", () => {
67
63
  expect(component.findComponent(ULink).props("label")).toBe(expectedLabel);
68
64
  });
69
65
 
70
- // LabelDisplayCount prop
71
- it("limits displayed labels based on labelDisplayCount", async () => {
66
+ it("LabelDisplayCount – limits displayed labels based on labelDisplayCount", async () => {
72
67
  const modelValue = [1, 2, 3];
73
68
  const labelDisplayCount = 1;
74
69
 
@@ -87,8 +82,7 @@ describe("UDropdownLink.vue", () => {
87
82
  expect(component.findComponent(ULink).props("label")).toBe(expectedLabel);
88
83
  });
89
84
 
90
- // LabelDisplayCount prop with single value
91
- it("correctly displays label when labelDisplayCount is 1 and only one value is selected", async () => {
85
+ it("LabelDisplayCount displays label correctly with single value", async () => {
92
86
  const modelValue = [1];
93
87
  const labelDisplayCount = 1;
94
88
 
@@ -107,8 +101,7 @@ describe("UDropdownLink.vue", () => {
107
101
  expect(component.findComponent(ULink).props("label")).toBe(expectedLabel);
108
102
  });
109
103
 
110
- // Color prop
111
- it("applies the correct color class", async () => {
104
+ it("Color – applies the correct color class", async () => {
112
105
  const colors = [
113
106
  "primary",
114
107
  "secondary",
@@ -133,8 +126,7 @@ describe("UDropdownLink.vue", () => {
133
126
  });
134
127
  });
135
128
 
136
- // Size prop
137
- it("applies the correct size class", async () => {
129
+ it("Size – applies the correct size class", async () => {
138
130
  const sizes = ["sm", "md", "lg"];
139
131
 
140
132
  sizes.forEach((size) => {
@@ -149,8 +141,7 @@ describe("UDropdownLink.vue", () => {
149
141
  });
150
142
  });
151
143
 
152
- // Underlined prop
153
- it("applies underlined class when underlined prop is true", () => {
144
+ it("Underlined – applies underlined class when underlined prop is true", () => {
154
145
  const underlined = true;
155
146
 
156
147
  const component = mount(UDropdownLink, {
@@ -163,8 +154,7 @@ describe("UDropdownLink.vue", () => {
163
154
  expect(component.findComponent(ULink).props("underlined")).toBe(underlined);
164
155
  });
165
156
 
166
- // Dashed prop
167
- it("applies dashed class when dashed prop is true", () => {
157
+ it("Dashed – applies dashed class when dashed prop is true", () => {
168
158
  const dashed = true;
169
159
 
170
160
  const component = mount(UDropdownLink, {
@@ -177,8 +167,7 @@ describe("UDropdownLink.vue", () => {
177
167
  expect(component.findComponent(ULink).props("dashed")).toBe(dashed);
178
168
  });
179
169
 
180
- // Disabled prop
181
- it("applies disabled attribute when disabled prop is true", () => {
170
+ it("Disabled – applies disabled attribute when disabled prop is true", () => {
182
171
  const disabled = true;
183
172
 
184
173
  const component = mount(UDropdownLink, {
@@ -191,8 +180,7 @@ describe("UDropdownLink.vue", () => {
191
180
  expect(component.findComponent(ULink).props("disabled")).toBe(disabled);
192
181
  });
193
182
 
194
- // ToggleIcon prop (boolean: true)
195
- it("shows default toggle icon when toggleIcon is true", () => {
183
+ it("ToggleIcon shows default toggle icon when toggleIcon is true", () => {
196
184
  const toggleIcon = true;
197
185
 
198
186
  const component = mount(UDropdownLink, {
@@ -208,8 +196,7 @@ describe("UDropdownLink.vue", () => {
208
196
  expect(iconComponent.props("name")).toBe("keyboard_arrow_down");
209
197
  });
210
198
 
211
- // ToggleIcon prop (boolean: false)
212
- it("hides toggle icon when toggleIcon is false", () => {
199
+ it("ToggleIcon hides toggle icon when toggleIcon is false", () => {
213
200
  const toggleIcon = false;
214
201
 
215
202
  const component = mount(UDropdownLink, {
@@ -224,8 +211,7 @@ describe("UDropdownLink.vue", () => {
224
211
  expect(iconComponent.exists()).toBe(false);
225
212
  });
226
213
 
227
- // ToggleIcon prop (string)
228
- it("shows custom toggle icon when toggleIcon is a string", () => {
214
+ it("ToggleIcon shows custom toggle icon when toggleIcon is a string", () => {
229
215
  const toggleIcon = "custom_icon";
230
216
 
231
217
  const component = mount(UDropdownLink, {
@@ -241,8 +227,7 @@ describe("UDropdownLink.vue", () => {
241
227
  expect(iconComponent.props("name")).toBe(toggleIcon);
242
228
  });
243
229
 
244
- // ID prop
245
- it("applies the correct id attribute", () => {
230
+ it("ID – applies the correct id attribute", () => {
246
231
  const id = "test-dropdown-id";
247
232
 
248
233
  const component = mount(UDropdownLink, {
@@ -252,11 +237,12 @@ describe("UDropdownLink.vue", () => {
252
237
  },
253
238
  });
254
239
 
255
- expect(component.findComponent(ULink).attributes("id")).toBe(id);
240
+ const dropdown = component.findComponent({ name: "UDropdown" });
241
+
242
+ expect(dropdown.props("id")).toBe(id);
256
243
  });
257
244
 
258
- // DataTest prop
259
- it("applies the correct data-test attribute", () => {
245
+ it("DataTest – applies the correct data-test attribute", () => {
260
246
  const dataTest = "test-dropdown";
261
247
 
262
248
  const component = mount(UDropdownLink, {
@@ -269,8 +255,7 @@ describe("UDropdownLink.vue", () => {
269
255
  expect(component.findComponent(ULink).attributes("data-test")).toBe(dataTest);
270
256
  });
271
257
 
272
- // OptionsLimit prop
273
- it("passes optionsLimit prop to UListbox component", async () => {
258
+ it("OptionsLimit – passes optionsLimit prop to UListbox component", async () => {
274
259
  const optionsLimit = 2;
275
260
 
276
261
  const component = mount(UDropdownLink, {
@@ -285,8 +270,7 @@ describe("UDropdownLink.vue", () => {
285
270
  expect(component.findComponent(UListbox).props("optionsLimit")).toBe(optionsLimit);
286
271
  });
287
272
 
288
- // VisibleOptions prop
289
- it("passes visibleOptions prop to UListbox component", async () => {
273
+ it("VisibleOptions – passes visibleOptions prop to UListbox component", async () => {
290
274
  const visibleOptions = 5;
291
275
 
292
276
  const component = mount(UDropdownLink, {
@@ -301,8 +285,7 @@ describe("UDropdownLink.vue", () => {
301
285
  expect(component.findComponent(UListbox).props("visibleOptions")).toBe(visibleOptions);
302
286
  });
303
287
 
304
- // GroupLabelKey prop
305
- it("passes groupLabelKey prop to UListbox component", async () => {
288
+ it("GroupLabelKey – passes groupLabelKey prop to UListbox component", async () => {
306
289
  const groupLabelKey = "category";
307
290
  const groupedOptions = [
308
291
  { groupLabel: "Group 1", category: "group1" },
@@ -341,8 +324,7 @@ describe("UDropdownLink.vue", () => {
341
324
  expect(options[0].text()).toBe("Option 3");
342
325
  });
343
326
 
344
- // CloseOnSelect prop
345
- it("keeps dropdown open when closeOnSelect is false", async () => {
327
+ it("CloseOnSelect – keeps dropdown open when closeOnSelect is false", async () => {
346
328
  const component = mount(UDropdownLink, {
347
329
  props: {
348
330
  options: defaultOptions,
@@ -363,12 +345,100 @@ describe("UDropdownLink.vue", () => {
363
345
  // Dropdown should remain open
364
346
  expect(component.findComponent(UListbox).exists()).toBe(true);
365
347
  });
348
+
349
+ it("XPosition – passes xPosition prop to dropdown", () => {
350
+ const component = mount(UDropdownLink, {
351
+ props: {
352
+ xPosition: "right",
353
+ options: defaultOptions,
354
+ },
355
+ });
356
+
357
+ const dropdown = component.findComponent({ name: "UDropdown" });
358
+
359
+ expect(dropdown.props("xPosition")).toBe("right");
360
+ });
361
+
362
+ it("YPosition – passes yPosition prop to dropdown", () => {
363
+ const component = mount(UDropdownLink, {
364
+ props: {
365
+ yPosition: "top",
366
+ options: defaultOptions,
367
+ },
368
+ });
369
+
370
+ const dropdown = component.findComponent({ name: "UDropdown" });
371
+
372
+ expect(dropdown.props("yPosition")).toBe("top");
373
+ });
374
+
375
+ it("LabelKey – uses custom label key for options", () => {
376
+ const customOptions = [
377
+ { id: 1, name: "First" },
378
+ { id: 2, name: "Second" },
379
+ ];
380
+
381
+ const component = mount(UDropdownLink, {
382
+ props: {
383
+ options: customOptions,
384
+ labelKey: "name",
385
+ valueKey: "id",
386
+ },
387
+ });
388
+
389
+ const dropdown = component.findComponent({ name: "UDropdown" });
390
+
391
+ expect(dropdown.props("labelKey")).toBe("name");
392
+ });
393
+
394
+ it("ValueKey – uses custom value key for options", () => {
395
+ const customOptions = [
396
+ { id: 1, name: "First" },
397
+ { id: 2, name: "Second" },
398
+ ];
399
+
400
+ const component = mount(UDropdownLink, {
401
+ props: {
402
+ options: customOptions,
403
+ labelKey: "name",
404
+ valueKey: "id",
405
+ },
406
+ });
407
+
408
+ const dropdown = component.findComponent({ name: "UDropdown" });
409
+
410
+ expect(dropdown.props("valueKey")).toBe("id");
411
+ });
412
+
413
+ it("GroupValueKey – passes groupValueKey prop to dropdown", () => {
414
+ const component = mount(UDropdownLink, {
415
+ props: {
416
+ groupValueKey: "items",
417
+ options: defaultOptions,
418
+ },
419
+ });
420
+
421
+ const dropdown = component.findComponent({ name: "UDropdown" });
422
+
423
+ expect(dropdown.props("groupValueKey")).toBe("items");
424
+ });
425
+
426
+ it("Searchable – passes searchable prop to dropdown", () => {
427
+ const component = mount(UDropdownLink, {
428
+ props: {
429
+ searchable: true,
430
+ options: defaultOptions,
431
+ },
432
+ });
433
+
434
+ const dropdown = component.findComponent({ name: "UDropdown" });
435
+
436
+ expect(dropdown.props("searchable")).toBe(true);
437
+ });
366
438
  });
367
439
 
368
- // Slots tests
369
440
  describe("Slots", () => {
370
- // Default slot
371
- it("renders content from default slot", () => {
441
+ it("Default – renders content from default slot", () => {
372
442
  const slotContent = "Custom Content";
373
443
  const label = "Dropdown Link";
374
444
 
@@ -385,8 +455,7 @@ describe("UDropdownLink.vue", () => {
385
455
  expect(component.text()).toContain(slotContent);
386
456
  });
387
457
 
388
- // Left slot
389
- it("renders content from left slot", () => {
458
+ it("Left – renders content from left slot", () => {
390
459
  const label = "Dropdown Link";
391
460
  const slotText = "Left";
392
461
  const slotClass = "left-content";
@@ -405,8 +474,7 @@ describe("UDropdownLink.vue", () => {
405
474
  expect(component.find(`.${slotClass}`).text()).toBe(slotText);
406
475
  });
407
476
 
408
- // Toggle slot
409
- it("renders content from toggle slot", () => {
477
+ it("Toggle – renders content from toggle slot", () => {
410
478
  const label = "Dropdown Link";
411
479
  const slotText = "Toggle";
412
480
  const slotClass = "toggle-content";
@@ -425,8 +493,7 @@ describe("UDropdownLink.vue", () => {
425
493
  expect(component.find(`.${slotClass}`).text()).toBe(slotText);
426
494
  });
427
495
 
428
- // Before-option slot
429
- it("renders content from before-option slot", async () => {
496
+ it("Before – renders content from before-option slot", async () => {
430
497
  const label = "Dropdown Link";
431
498
  const slotText = "Before";
432
499
  const slotClass = "before-option-content";
@@ -450,8 +517,7 @@ describe("UDropdownLink.vue", () => {
450
517
  expect(beforeOptionSlot.text()).toBe(slotText);
451
518
  });
452
519
 
453
- // Option slot
454
- it("renders custom content from option slot", async () => {
520
+ it("Option – renders custom content from option slot", async () => {
455
521
  const label = "Dropdown Link";
456
522
  const slotClass = "custom-option-content";
457
523
 
@@ -474,8 +540,7 @@ describe("UDropdownLink.vue", () => {
474
540
  expect(customOptionSlot.text()).toBe("Custom Option 1");
475
541
  });
476
542
 
477
- // After-option slot
478
- it("renders content from after-option slot", async () => {
543
+ it("After – renders content from after-option slot", async () => {
479
544
  const label = "Dropdown Link";
480
545
  const slotText = "After";
481
546
  const slotClass = "after-option-content";
@@ -499,8 +564,7 @@ describe("UDropdownLink.vue", () => {
499
564
  expect(afterOptionSlot.text()).toBe(slotText);
500
565
  });
501
566
 
502
- // Empty slot
503
- it("renders custom content from empty slot", async () => {
567
+ it("Empty – renders custom content from empty slot", async () => {
504
568
  const label = "Dropdown Link";
505
569
  const slotContent = "No options available";
506
570
  const slotClass = "custom-empty";
@@ -525,10 +589,8 @@ describe("UDropdownLink.vue", () => {
525
589
  });
526
590
  });
527
591
 
528
- // Events tests
529
592
  describe("Events", () => {
530
- // Click event to open dropdown
531
- it("opens dropdown when link is clicked", async () => {
593
+ it("Click opens dropdown when link is clicked", async () => {
532
594
  const component = mount(UDropdownLink, {
533
595
  props: {
534
596
  options: defaultOptions,
@@ -545,8 +607,7 @@ describe("UDropdownLink.vue", () => {
545
607
  expect(component.findComponent(UListbox).exists()).toBe(true);
546
608
  });
547
609
 
548
- // update:modelValue event
549
- it("emits update:modelValue event when an option is selected", async () => {
610
+ it("update:modelValue – emits update:modelValue event when an option is selected", async () => {
550
611
  const component = mount(UDropdownLink, {
551
612
  props: {
552
613
  options: defaultOptions,
@@ -567,8 +628,7 @@ describe("UDropdownLink.vue", () => {
567
628
  expect(component.emitted("update:modelValue")?.[0]).toEqual([2]);
568
629
  });
569
630
 
570
- // clickOption event
571
- it("emits clickOption event when an option is clicked", async () => {
631
+ it("clickOption – emits clickOption event when an option is clicked", async () => {
572
632
  const component = mount(UDropdownLink, {
573
633
  props: {
574
634
  options: defaultOptions,
@@ -591,8 +651,7 @@ describe("UDropdownLink.vue", () => {
591
651
  expect(component.emitted("clickOption")?.[0]).toEqual([option]);
592
652
  });
593
653
 
594
- // Close dropdown when clicking outside
595
- it("closes dropdown when clicking outside", async () => {
654
+ it("Close – closes dropdown when clicking outside", async () => {
596
655
  const component = mount(UDropdownLink, {
597
656
  props: {
598
657
  options: defaultOptions,
@@ -603,17 +662,16 @@ describe("UDropdownLink.vue", () => {
603
662
  await component.findComponent(ULink).trigger("click");
604
663
  expect(component.findComponent(UListbox).exists()).toBe(true);
605
664
 
606
- // Directly call the hideOptions function
665
+ // Directly call the hide function
607
666
  // This is equivalent to what happens when clicking outside
608
- component.vm.hideOptions();
667
+ component.vm.hide();
609
668
  await component.vm.$nextTick();
610
669
 
611
670
  // Dropdown should be closed
612
671
  expect(component.findComponent(UListbox).exists()).toBe(false);
613
672
  });
614
673
 
615
- // No dropdown toggle when disabled
616
- it("does not toggle dropdown when disabled", async () => {
674
+ it("No does not toggle dropdown when disabled", async () => {
617
675
  const component = mount(UDropdownLink, {
618
676
  props: {
619
677
  disabled: true,
@@ -630,12 +688,73 @@ describe("UDropdownLink.vue", () => {
630
688
  // Dropdown should still be closed
631
689
  expect(component.findComponent(UListbox).exists()).toBe(false);
632
690
  });
691
+
692
+ it("Open – emits open event when dropdown is opened", async () => {
693
+ const component = mount(UDropdownLink, {
694
+ props: {
695
+ options: defaultOptions,
696
+ },
697
+ });
698
+
699
+ await component.findComponent(ULink).trigger("click");
700
+
701
+ expect(component.emitted("open")).toBeTruthy();
702
+ });
703
+
704
+ it("Close – emits close event when dropdown is closed", async () => {
705
+ const component = mount(UDropdownLink, {
706
+ props: {
707
+ options: defaultOptions,
708
+ },
709
+ });
710
+
711
+ await component.findComponent(ULink).trigger("click");
712
+
713
+ component.vm.hide();
714
+ await component.vm.$nextTick();
715
+
716
+ expect(component.emitted("close")).toBeTruthy();
717
+ });
718
+
719
+ it("SearchChange – emits searchChange event when search value changes", async () => {
720
+ const component = mount(UDropdownLink, {
721
+ props: {
722
+ searchable: true,
723
+ options: defaultOptions,
724
+ },
725
+ });
726
+
727
+ await component.findComponent(ULink).trigger("click");
728
+
729
+ const dropdown = component.findComponent({ name: "UDropdown" });
730
+
731
+ dropdown.vm.$emit("searchChange", "test query");
732
+
733
+ expect(component.emitted("searchChange")).toBeTruthy();
734
+ expect(component.emitted("searchChange")?.[0]).toEqual(["test query"]);
735
+ });
736
+
737
+ it("Update:search – emits update:search event when search value updates", async () => {
738
+ const component = mount(UDropdownLink, {
739
+ props: {
740
+ searchable: true,
741
+ options: defaultOptions,
742
+ },
743
+ });
744
+
745
+ await component.findComponent(ULink).trigger("click");
746
+
747
+ const dropdown = component.findComponent({ name: "UDropdown" });
748
+
749
+ dropdown.vm.$emit("update:search", "new search");
750
+
751
+ expect(component.emitted("update:search")).toBeTruthy();
752
+ expect(component.emitted("update:search")?.[0]).toEqual(["new search"]);
753
+ });
633
754
  });
634
755
 
635
- // Exposed refs tests
636
756
  describe("Exposed refs", () => {
637
- // wrapperRef
638
- it("exposes wrapperRef", () => {
757
+ it("wrapperRef – exposes wrapperRef", () => {
639
758
  const component = mount(UDropdownLink, {
640
759
  props: {
641
760
  options: defaultOptions,
@@ -158,13 +158,12 @@ watch(
158
158
  const maxHeight = options
159
159
  .slice(0, props.visibleOptions)
160
160
  .map((el) => {
161
- const elHeight = el.getBoundingClientRect().height;
162
-
163
161
  const styles = window.getComputedStyle(el);
162
+ const height = parseFloat(styles.height || "0");
164
163
  const marginTop = parseFloat(styles.marginTop || "0");
165
164
  const marginBottom = parseFloat(styles.marginBottom || "0");
166
165
 
167
- return elHeight + marginTop + marginBottom;
166
+ return height + marginTop + marginBottom;
168
167
  })
169
168
  .reduce((acc, cur) => acc + cur, 0);
170
169
 
@@ -8,14 +8,14 @@ export default /*tw*/ {
8
8
  `,
9
9
  variants: {
10
10
  searchable: {
11
- true: "pt-0",
11
+ true: "pt-0 min-w-64",
12
12
  },
13
13
  },
14
14
  },
15
15
  listboxInput: {
16
16
  base: "{UInputSearch} sticky top-0 pt-1 bg-default z-10",
17
17
  searchInput: {
18
- wrapper: "px-2 rounded-small focus-within:outline-0 hover:focus-within:border-lifted focus-within:border-lifted",
18
+ wrapper: "pl-2 pr-1 rounded-none border-0 border-b focus-within:outline-0 !border-default",
19
19
  },
20
20
  },
21
21
  selectIcon: {
@@ -127,7 +127,7 @@ export default /*tw*/ {
127
127
  },
128
128
  },
129
129
  },
130
- listbox: "{UListbox} group-[*]/top:bottom-full group-[*]/top:top-auto top-full w-full",
130
+ listbox: "{UListbox} my-1.5 group-[*]/top:bottom-full group-[*]/top:top-auto top-full w-full",
131
131
  i18n: {
132
132
  listIsEmpty: "List is empty.",
133
133
  noDataToShow: "No data to show.",