vueless 1.2.10-beta.1 → 1.2.10-beta.11

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 (34) hide show
  1. package/composables/useRequestQueue.ts +47 -0
  2. package/constants.d.ts +1 -0
  3. package/constants.js +1 -0
  4. package/icons/storybook/arrow_forward_ios.svg +1 -0
  5. package/index.d.ts +4 -1
  6. package/index.ts +4 -1
  7. package/package.json +2 -2
  8. package/types.ts +2 -0
  9. package/ui.button/UButton.vue +2 -6
  10. package/ui.button/config.ts +5 -0
  11. package/ui.button/tests/UButton.test.ts +1 -1
  12. package/ui.container-drawer/UDrawer.vue +365 -0
  13. package/ui.container-drawer/config.ts +128 -0
  14. package/ui.container-drawer/constants.ts +5 -0
  15. package/ui.container-drawer/storybook/docs.mdx +16 -0
  16. package/ui.container-drawer/storybook/stories.ts +255 -0
  17. package/ui.container-drawer/tests/UDrawer.test.ts +680 -0
  18. package/ui.container-drawer/types.ts +67 -0
  19. package/ui.container-modal/storybook/stories.ts +4 -1
  20. package/ui.container-modal-confirm/storybook/stories.ts +29 -24
  21. package/ui.form-calendar/UCalendarMonthView.vue +4 -4
  22. package/ui.form-calendar/tests/UCalendar.test.ts +8 -8
  23. package/ui.form-calendar/tests/UCalendarMonthView.test.ts +1 -1
  24. package/ui.form-calendar/utilCalendar.ts +2 -2
  25. package/ui.form-date-picker-range/UDatePickerRangeInputs.vue +20 -19
  26. package/ui.loader-overlay/useLoaderOverlay.ts +4 -4
  27. package/ui.loader-progress/ULoaderProgress.vue +41 -46
  28. package/ui.loader-progress/storybook/docs.mdx +24 -13
  29. package/ui.loader-progress/storybook/stories.ts +0 -9
  30. package/ui.loader-progress/types.ts +2 -2
  31. package/ui.loader-progress/useLoaderProgress.ts +36 -26
  32. package/ui.loader-progress/utilLoaderProgress.ts +12 -18
  33. package/ui.navigation-tabs/UTabs.vue +0 -1
  34. package/utils/requestQueue.ts +21 -0
@@ -0,0 +1,680 @@
1
+ import { mount } from "@vue/test-utils";
2
+ import { describe, it, expect, vi } from "vitest";
3
+
4
+ import UDrawer from "../UDrawer.vue";
5
+ import UHeader from "../../ui.text-header/UHeader.vue";
6
+
7
+ import type { Props } from "../types";
8
+
9
+ describe("UDrawer", () => {
10
+ const modelValue = true;
11
+
12
+ // Wait for an async component to load
13
+ function sleep(ms: number = 0) {
14
+ return new Promise((resolve) => setTimeout(resolve, ms));
15
+ }
16
+
17
+ // Props tests
18
+ describe("Props", () => {
19
+ it("ModelValue – renders when true", () => {
20
+ const component = mount(UDrawer, {
21
+ props: {
22
+ modelValue,
23
+ },
24
+ });
25
+
26
+ expect(component.isVisible()).toBe(modelValue);
27
+ });
28
+
29
+ it("ModelValue – does not render when false", () => {
30
+ const modelValue = false;
31
+
32
+ const component = mount(UDrawer, {
33
+ props: {
34
+ modelValue,
35
+ },
36
+ });
37
+
38
+ expect(component.find("[vl-key='overlay']").exists()).toBe(modelValue);
39
+ });
40
+
41
+ it("Title – renders with title prop", () => {
42
+ const title = "Drawer Title";
43
+
44
+ const component = mount(UDrawer, {
45
+ props: {
46
+ modelValue,
47
+ title,
48
+ },
49
+ });
50
+
51
+ const header = component.findComponent(UHeader);
52
+
53
+ expect(header.exists()).toBe(true);
54
+ expect(header.props("label")).toBe(title);
55
+ });
56
+
57
+ it("Description – renders with description prop", () => {
58
+ const title = "Drawer Title";
59
+ const description = "Drawer Description";
60
+
61
+ const component = mount(UDrawer, {
62
+ props: {
63
+ modelValue,
64
+ title,
65
+ description,
66
+ },
67
+ });
68
+
69
+ expect(component.text()).toContain(description);
70
+ });
71
+
72
+ it("Position – applies correct position classes", () => {
73
+ const positions = {
74
+ top: ["top-0", "w-full", "h-auto"],
75
+ bottom: ["bottom-0", "w-full", "h-auto"],
76
+ left: ["left-0", "w-max", "h-full"],
77
+ right: ["right-0", "w-max", "h-full"],
78
+ };
79
+
80
+ Object.entries(positions).forEach(([position, expectedClasses]) => {
81
+ const component = mount(UDrawer, {
82
+ props: {
83
+ modelValue,
84
+ position: position as Props["position"],
85
+ },
86
+ });
87
+
88
+ const drawerClasses = component.find("[vl-key='drawerWrapper']").attributes("class");
89
+
90
+ expectedClasses.forEach((expectedClass) => {
91
+ expect(drawerClasses).toContain(expectedClass);
92
+ });
93
+ });
94
+ });
95
+
96
+ it("Variant – applies correct variant classes", () => {
97
+ const variants = {
98
+ solid: "bg-default",
99
+ outlined: "bg-default",
100
+ subtle: "bg-muted",
101
+ soft: "bg-muted",
102
+ };
103
+
104
+ Object.entries(variants).forEach(([variant, expectedClasses]) => {
105
+ const component = mount(UDrawer, {
106
+ props: {
107
+ modelValue,
108
+ variant: variant as Props["variant"],
109
+ },
110
+ });
111
+
112
+ expect(component.find("[vl-key='drawerWrapper']").attributes("class")).toContain(
113
+ expectedClasses,
114
+ );
115
+ });
116
+ });
117
+
118
+ it("Handle – renders handle when prop is true", () => {
119
+ const handle = [true, false];
120
+
121
+ handle.forEach((value) => {
122
+ const component = mount(UDrawer, {
123
+ props: {
124
+ modelValue,
125
+ handle: value,
126
+ },
127
+ });
128
+
129
+ const handleWrapper = component.find("[vl-key='handleWrapper']");
130
+ const handleElement = component.find("[vl-key='handle']");
131
+
132
+ expect(handleWrapper.exists()).toBe(value);
133
+ expect(handleElement.exists()).toBe(value);
134
+ });
135
+ });
136
+
137
+ it("Inset – applies inset class when prop is true", () => {
138
+ const inset = true;
139
+ const expectedClass = "m-4";
140
+
141
+ const component = mount(UDrawer, {
142
+ props: {
143
+ modelValue,
144
+ inset,
145
+ },
146
+ });
147
+
148
+ const innerWrapper = component.find("[vl-key='innerWrapper']");
149
+
150
+ expect(innerWrapper.attributes("class")).toContain(expectedClass);
151
+ });
152
+
153
+ it("CloseOnOverlay – closes drawer when overlay is clicked", () => {
154
+ const closeOnOverlay = [true, false];
155
+
156
+ closeOnOverlay.forEach(async (value) => {
157
+ const component = mount(UDrawer, {
158
+ props: {
159
+ modelValue,
160
+ closeOnOverlay: value,
161
+ },
162
+ });
163
+
164
+ const innerWrapper = component.find('[vl-key="innerWrapper"]');
165
+
166
+ expect(innerWrapper.exists()).toBe(true);
167
+
168
+ await innerWrapper.trigger("click");
169
+ await sleep(500);
170
+
171
+ const drawer = component.find('[vl-key="drawer"]');
172
+
173
+ expect(drawer.exists()).toBe(!value);
174
+ });
175
+ });
176
+
177
+ it("CloseOnEsc – closes drawer when escape key is pressed", () => {
178
+ const closeOnEsc = [true, false];
179
+
180
+ closeOnEsc.forEach(async (value) => {
181
+ const component = mount(UDrawer, {
182
+ props: {
183
+ modelValue,
184
+ closeOnEsc: value,
185
+ },
186
+ });
187
+
188
+ const wrapper = component.find("[vl-key='wrapper']");
189
+
190
+ await wrapper.trigger("keydown", { key: "Escape" });
191
+ await sleep(500);
192
+
193
+ const drawer = component.find('[vl-key="drawer"]');
194
+
195
+ expect(drawer.exists()).toBe(!value);
196
+ });
197
+ });
198
+
199
+ it("DataTest – applies the correct data-test attribute", () => {
200
+ const dataTest = "drawer-test";
201
+
202
+ const component = mount(UDrawer, {
203
+ props: {
204
+ modelValue,
205
+ dataTest,
206
+ },
207
+ });
208
+
209
+ const drawerWrapper = component.find("[vl-key='wrapper']");
210
+
211
+ expect(drawerWrapper.attributes("data-test")).toBe(dataTest);
212
+ });
213
+ });
214
+
215
+ // Slots tests
216
+ describe("Slots", () => {
217
+ it("Default – renders content in default slot", () => {
218
+ const slotClass = "default-content";
219
+ const slotContent = "Default Content";
220
+
221
+ const component = mount(UDrawer, {
222
+ props: { modelValue: true },
223
+ slots: {
224
+ default: `<div class="${slotClass}">${slotContent}</div>`,
225
+ },
226
+ });
227
+
228
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
229
+ expect(component.text()).toContain(slotContent);
230
+ });
231
+
232
+ it("Before Title – renders content in slot and shows header", () => {
233
+ const slotClass = "before-title";
234
+ const slotContent = "Before Title";
235
+
236
+ const component = mount(UDrawer, {
237
+ props: {
238
+ modelValue: true,
239
+ title: "Drawer Title",
240
+ },
241
+ slots: {
242
+ "before-title": `<div class="${slotClass}">${slotContent}</div>`,
243
+ },
244
+ });
245
+
246
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
247
+ expect(component.text()).toContain(slotContent);
248
+
249
+ // Check that header is shown when before-title slot is provided
250
+ const header = component.find("[vl-key='header']");
251
+
252
+ expect(header.exists()).toBe(true);
253
+ expect(header.text()).toContain(slotContent);
254
+ });
255
+
256
+ it("Title – renders custom content in slot and shows header", () => {
257
+ const slotClass = "custom-title";
258
+ const slotContent = "Custom Title";
259
+
260
+ const component = mount(UDrawer, {
261
+ props: {
262
+ modelValue: true,
263
+ title: "Drawer Title",
264
+ },
265
+ slots: {
266
+ title: `<div class="${slotClass}">${slotContent}</div>`,
267
+ },
268
+ });
269
+
270
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
271
+ expect(component.text()).toContain(slotContent);
272
+ expect(component.findComponent(UHeader).exists()).toBe(false);
273
+
274
+ // Check that header is shown when title slot is provided
275
+ const header = component.find("[vl-key='header']");
276
+
277
+ expect(header.exists()).toBe(true);
278
+ expect(header.text()).toContain(slotContent);
279
+ });
280
+
281
+ it("After Title – renders content in slot and shows header", () => {
282
+ const slotClass = "after-title";
283
+ const slotContent = "After Title";
284
+
285
+ const component = mount(UDrawer, {
286
+ props: {
287
+ modelValue: true,
288
+ title: "Drawer Title",
289
+ },
290
+ slots: {
291
+ "after-title": `<div class="${slotClass}">${slotContent}</div>`,
292
+ },
293
+ });
294
+
295
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
296
+ expect(component.text()).toContain(slotContent);
297
+
298
+ // Check that header is shown when after-title slot is provided
299
+ const header = component.find("[vl-key='header']");
300
+
301
+ expect(header.exists()).toBe(true);
302
+ expect(header.text()).toContain(slotContent);
303
+ });
304
+
305
+ it("Actions – renders custom content in slot and shows header", () => {
306
+ const slotClass = "actions";
307
+ const slotContent = "Actions";
308
+
309
+ const component = mount(UDrawer, {
310
+ props: {
311
+ modelValue: true,
312
+ title: "Drawer Title",
313
+ },
314
+ slots: {
315
+ actions: `<div class="${slotClass}">${slotContent}</div>`,
316
+ },
317
+ });
318
+
319
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
320
+ expect(component.text()).toContain(slotContent);
321
+
322
+ // Check that header is shown when actions slot is provided
323
+ const header = component.find("[vl-key='header']");
324
+
325
+ expect(header.exists()).toBe(true);
326
+ expect(header.text()).toContain(slotContent);
327
+ });
328
+
329
+ it("Header – does not show when no title or slots are provided", () => {
330
+ const component = mount(UDrawer, {
331
+ props: { modelValue },
332
+ });
333
+
334
+ const header = component.find("[vl-key='header']");
335
+
336
+ expect(header.exists()).toBe(false);
337
+ });
338
+
339
+ it("Actions – provides close binding to slot", () => {
340
+ const component = mount(UDrawer, {
341
+ props: {
342
+ modelValue: true,
343
+ title: "Drawer Title",
344
+ },
345
+ slots: {
346
+ actions: `
347
+ <template #default="{ close }">
348
+ <button class="custom-close" @click="close">Close</button>
349
+ </template>
350
+ `,
351
+ },
352
+ });
353
+
354
+ const closeButton = component.find(".custom-close");
355
+
356
+ expect(closeButton.exists()).toBe(true);
357
+
358
+ // Click the close button
359
+ closeButton.trigger("click");
360
+
361
+ // Check if the drawer emitted the update:modelValue event with false
362
+ expect(component.emitted("update:modelValue")).toBeTruthy();
363
+ expect(component.emitted("update:modelValue")?.[0]).toEqual([false]);
364
+ });
365
+
366
+ it("Handle – renders custom content in slot", () => {
367
+ const slotClass = "custom-handle";
368
+ const slotContent = "Custom Handle";
369
+
370
+ const component = mount(UDrawer, {
371
+ props: {
372
+ modelValue: true,
373
+ handle: true,
374
+ },
375
+ slots: {
376
+ handle: `<div class="${slotClass}">${slotContent}</div>`,
377
+ },
378
+ });
379
+
380
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
381
+ expect(component.text()).toContain(slotContent);
382
+
383
+ // Check that default handle element is not rendered when slot is used
384
+ const defaultHandle = component.find("[vl-key='handle']");
385
+
386
+ expect(defaultHandle.exists()).toBe(false);
387
+ });
388
+
389
+ it("Footer Left – renders content in slot and shows footer", () => {
390
+ const slotClass = "footer-left";
391
+ const slotContent = "Footer Left";
392
+
393
+ const component = mount(UDrawer, {
394
+ props: { modelValue: true },
395
+ slots: {
396
+ "footer-left": `<div class="${slotClass}">${slotContent}</div>`,
397
+ },
398
+ });
399
+
400
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
401
+ expect(component.text()).toContain(slotContent);
402
+
403
+ // Check that footer is shown when footer-left slot is provided
404
+ const footer = component.find("[vl-key='footer']");
405
+
406
+ expect(footer.exists()).toBe(true);
407
+ expect(footer.text()).toContain(slotContent);
408
+ });
409
+
410
+ it("Footer Right – renders content in slot and shows footer", () => {
411
+ const slotClass = "footer-right";
412
+ const slotContent = "Footer Right";
413
+
414
+ const component = mount(UDrawer, {
415
+ props: { modelValue: true },
416
+ slots: {
417
+ "footer-right": `<div class="${slotClass}">${slotContent}</div>`,
418
+ },
419
+ });
420
+
421
+ expect(component.find(`.${slotClass}`).exists()).toBe(true);
422
+ expect(component.text()).toContain(slotContent);
423
+
424
+ // Check that footer is shown when footer-right slot is provided
425
+ const footer = component.find("[vl-key='footer']");
426
+
427
+ expect(footer.exists()).toBe(true);
428
+ expect(footer.text()).toContain(slotContent);
429
+ });
430
+
431
+ it("Footer – does not show when no footer slots are provided", () => {
432
+ const component = mount(UDrawer, {
433
+ props: { modelValue },
434
+ });
435
+
436
+ const footer = component.find("[vl-key='footer']");
437
+
438
+ expect(footer.exists()).toBe(false);
439
+ });
440
+ });
441
+
442
+ // Events tests
443
+ describe("Events", () => {
444
+ it("Update:modelValue – emits event when drawer is closed", async () => {
445
+ const title = "Drawer Title";
446
+
447
+ const component = mount(UDrawer, {
448
+ props: {
449
+ modelValue,
450
+ title,
451
+ },
452
+ slots: {
453
+ actions: `
454
+ <template #default="{ close }">
455
+ <button class="close-btn" @click="close">Close</button>
456
+ </template>
457
+ `,
458
+ },
459
+ });
460
+
461
+ const closeButton = component.find(".close-btn");
462
+
463
+ await closeButton.trigger("click");
464
+
465
+ expect(component.emitted("update:modelValue")).toBeTruthy();
466
+ expect(component.emitted("update:modelValue")?.[0]).toEqual([false]);
467
+ });
468
+
469
+ it("Close – emits event when drawer is closed", async () => {
470
+ const title = "Drawer Title";
471
+
472
+ const component = mount(UDrawer, {
473
+ props: {
474
+ modelValue,
475
+ title,
476
+ },
477
+ slots: {
478
+ actions: `
479
+ <template #default="{ close }">
480
+ <button class="close-btn" @click="close">Close</button>
481
+ </template>
482
+ `,
483
+ },
484
+ });
485
+
486
+ const closeButton = component.find(".close-btn");
487
+
488
+ await closeButton.trigger("click");
489
+
490
+ expect(component.emitted("close")).toBeTruthy();
491
+ });
492
+
493
+ it("CloseOnOverlay – emits events when overlay is clicked based on prop", () => {
494
+ const closeOnOverlay = [true, false];
495
+
496
+ closeOnOverlay.forEach(async (value) => {
497
+ const component = mount(UDrawer, {
498
+ props: {
499
+ modelValue,
500
+ closeOnOverlay: value,
501
+ },
502
+ });
503
+
504
+ const innerWrapper = component.find("[vl-key='innerWrapper']");
505
+
506
+ await innerWrapper.trigger("click");
507
+
508
+ if (value) {
509
+ expect(component.emitted("update:modelValue")).toBeTruthy();
510
+ expect(component.emitted("update:modelValue")?.[0]).toEqual([false]);
511
+ expect(component.emitted("close")).toBeTruthy();
512
+ } else {
513
+ expect(component.emitted("update:modelValue")).toBeFalsy();
514
+ expect(component.emitted("close")).toBeFalsy();
515
+ }
516
+ });
517
+ });
518
+
519
+ it("CloseOnEsc – emits events when escape key is pressed based on prop", () => {
520
+ const closeOnEsc = [true, false];
521
+
522
+ closeOnEsc.forEach(async (value) => {
523
+ const component = mount(UDrawer, {
524
+ props: {
525
+ modelValue,
526
+ closeOnEsc: value,
527
+ },
528
+ });
529
+
530
+ const wrapper = component.find("[vl-key='wrapper']");
531
+
532
+ await wrapper.trigger("keydown", { key: "Escape" });
533
+
534
+ if (value) {
535
+ expect(component.emitted("update:modelValue")).toBeTruthy();
536
+ expect(component.emitted("update:modelValue")?.[0]).toEqual([false]);
537
+ expect(component.emitted("close")).toBeTruthy();
538
+ } else {
539
+ expect(component.emitted("update:modelValue")).toBeFalsy();
540
+ expect(component.emitted("close")).toBeFalsy();
541
+ }
542
+ });
543
+ });
544
+ });
545
+
546
+ // Drag functionality tests
547
+ describe("Drag Functionality", () => {
548
+ it("Cursor – applies drag cursor classes when drawer is draggable", () => {
549
+ const component = mount(UDrawer, {
550
+ props: {
551
+ modelValue: true,
552
+ },
553
+ });
554
+
555
+ const drawer = component.find("[vl-key='drawerWrapper']");
556
+
557
+ expect(drawer.attributes("class")).toContain("cursor-grab");
558
+ });
559
+
560
+ it("Mouse Drag – handles drag start", async () => {
561
+ const component = mount(UDrawer, {
562
+ props: {
563
+ modelValue: true,
564
+ },
565
+ });
566
+
567
+ const drawer = component.find("[vl-key='drawerWrapper']");
568
+
569
+ // Mock getBoundingClientRect
570
+ const mockRect = {
571
+ width: 300,
572
+ height: 400,
573
+ top: 0,
574
+ left: 0,
575
+ right: 300,
576
+ bottom: 400,
577
+ };
578
+
579
+ vi.spyOn(drawer.element, "getBoundingClientRect").mockReturnValue(mockRect as DOMRect);
580
+
581
+ await drawer.trigger("mousedown", {
582
+ clientX: 100,
583
+ clientY: 100,
584
+ });
585
+
586
+ // Check if drag state is initialized (drag should not start until minimum distance)
587
+ expect(drawer.attributes("class")).toContain("cursor-grab");
588
+ });
589
+
590
+ it("Touch Drag – handles drag start", async () => {
591
+ const component = mount(UDrawer, {
592
+ props: {
593
+ modelValue: true,
594
+ },
595
+ });
596
+
597
+ const drawer = component.find("[vl-key='drawerWrapper']");
598
+
599
+ // Mock getBoundingClientRect
600
+ const mockRect = {
601
+ width: 300,
602
+ height: 400,
603
+ top: 0,
604
+ left: 0,
605
+ right: 300,
606
+ bottom: 400,
607
+ };
608
+
609
+ vi.spyOn(drawer.element, "getBoundingClientRect").mockReturnValue(mockRect as DOMRect);
610
+
611
+ await drawer.trigger("touchstart", {
612
+ touches: [{ clientX: 100, clientY: 100 }],
613
+ });
614
+
615
+ // Check if drag state is initialized (drag should not start until minimum distance)
616
+ expect(drawer.attributes("class")).toContain("cursor-grab");
617
+ });
618
+
619
+ it("Transform – applies drag transform styles during drag", async () => {
620
+ const component = mount(UDrawer, {
621
+ props: {
622
+ modelValue: true,
623
+ position: "left",
624
+ },
625
+ });
626
+
627
+ const drawer = component.find("[vl-key='drawerWrapper']");
628
+
629
+ // Mock getBoundingClientRect
630
+ const mockRect = {
631
+ width: 300,
632
+ height: 400,
633
+ top: 0,
634
+ left: 0,
635
+ right: 300,
636
+ bottom: 400,
637
+ };
638
+
639
+ vi.spyOn(drawer.element, "getBoundingClientRect").mockReturnValue(mockRect as DOMRect);
640
+
641
+ // Start drag
642
+ await drawer.trigger("mousedown", {
643
+ clientX: 100,
644
+ clientY: 100,
645
+ });
646
+
647
+ // Simulate drag movement
648
+ const mouseMoveEvent = new MouseEvent("mousemove", {
649
+ clientX: 150, // 50px movement
650
+ clientY: 100,
651
+ });
652
+
653
+ document.dispatchEvent(mouseMoveEvent);
654
+
655
+ // Check if transform is applied
656
+ const drawerElement = component.find("[vl-key='drawerWrapper']");
657
+ const style = drawerElement.attributes("style");
658
+
659
+ // Should contain transform when dragging (if style exists)
660
+ if (style) {
661
+ expect(style).toContain("transform");
662
+ } else {
663
+ // If no style attribute, that's also valid (transform might be applied via CSS classes)
664
+ expect(drawerElement.exists()).toBe(true);
665
+ }
666
+ });
667
+ });
668
+
669
+ // Exposed refs tests
670
+ describe("Exposed refs", () => {
671
+ it("WrapperRef – exposes wrapper element ref", () => {
672
+ const component = mount(UDrawer, {
673
+ props: { modelValue },
674
+ });
675
+
676
+ expect(component.vm.wrapperRef).toBeDefined();
677
+ expect(component.vm.wrapperRef instanceof HTMLDivElement).toBe(true);
678
+ });
679
+ });
680
+ });