vueless 1.4.7-beta.1 → 1.4.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 (78) hide show
  1. package/icons/internal/apps.svg +1 -1
  2. package/icons/internal/calendar_month-fill.svg +1 -1
  3. package/icons/internal/error.svg +1 -1
  4. package/icons/internal/info.svg +1 -1
  5. package/icons/internal/progress_activity.svg +1 -1
  6. package/icons/internal/search.svg +1 -1
  7. package/icons/internal/visibility-fill.svg +1 -1
  8. package/icons/internal/warning.svg +1 -1
  9. package/icons/storybook/account_circle.svg +1 -1
  10. package/icons/storybook/cloud_sync.svg +1 -0
  11. package/icons/storybook/contact_mail.svg +1 -1
  12. package/icons/storybook/date_range.svg +1 -0
  13. package/icons/storybook/delivery_truck_speed.svg +1 -1
  14. package/icons/storybook/directions_bike.svg +1 -1
  15. package/icons/storybook/error.svg +1 -1
  16. package/icons/storybook/event.svg +1 -0
  17. package/icons/storybook/folder.svg +1 -0
  18. package/icons/storybook/format_quote.svg +1 -0
  19. package/icons/storybook/handyman.svg +1 -1
  20. package/icons/storybook/help.svg +1 -1
  21. package/icons/storybook/inbox.svg +1 -1
  22. package/icons/storybook/inbox_customize.svg +1 -1
  23. package/icons/storybook/info.svg +1 -1
  24. package/icons/storybook/key.svg +1 -1
  25. package/icons/storybook/local_shipping.svg +1 -0
  26. package/icons/storybook/location_city.svg +1 -0
  27. package/icons/storybook/lock.svg +1 -1
  28. package/icons/storybook/lock_open.svg +1 -1
  29. package/icons/storybook/mark_email_unread.svg +1 -0
  30. package/icons/storybook/notifications.svg +1 -0
  31. package/icons/storybook/palette.svg +1 -1
  32. package/icons/storybook/person.svg +1 -1
  33. package/icons/storybook/person_search.svg +1 -1
  34. package/icons/storybook/progress_activity.svg +1 -1
  35. package/icons/storybook/rocket_launch.svg +1 -1
  36. package/icons/storybook/sentiment_satisfied.svg +1 -1
  37. package/icons/storybook/shield.svg +1 -0
  38. package/icons/storybook/timer.svg +1 -1
  39. package/icons/storybook/travel_explore.svg +1 -1
  40. package/icons/storybook/workspace_premium.svg +1 -0
  41. package/package.json +32 -31
  42. package/plugin-vite.js +18 -7
  43. package/ui.container-accordion-item/tests/UAccordionItem.test.ts +4 -2
  44. package/ui.container-drawer/tests/UDrawer.test.ts +11 -29
  45. package/ui.container-modal/tests/UModal.test.ts +10 -22
  46. package/ui.data-table/UTableRow.vue +2 -2
  47. package/ui.form-calendar/tests/UCalendar.test.ts +14 -13
  48. package/ui.form-calendar/tests/UCalendarDayView.test.ts +15 -16
  49. package/ui.form-checkbox/UCheckbox.vue +0 -5
  50. package/ui.form-checkbox/storybook/stories.ts +56 -18
  51. package/ui.form-checkbox/tests/UCheckbox.test.ts +0 -17
  52. package/ui.form-checkbox-group/storybook/stories.ts +61 -0
  53. package/ui.form-date-picker/storybook/stories.ts +85 -37
  54. package/ui.form-date-picker-range/storybook/stories.ts +97 -38
  55. package/ui.form-input/storybook/stories.ts +75 -37
  56. package/ui.form-input-file/storybook/stories.ts +44 -5
  57. package/ui.form-input-number/storybook/stories.ts +66 -28
  58. package/ui.form-input-password/storybook/stories.ts +94 -50
  59. package/ui.form-input-search/storybook/stories.ts +40 -4
  60. package/ui.form-label/ULabel.vue +0 -6
  61. package/ui.form-label/storybook/stories.ts +46 -24
  62. package/ui.form-label/tests/ULabel.test.ts +0 -12
  63. package/ui.form-listbox/UListbox.vue +71 -72
  64. package/ui.form-listbox/tests/UListbox.test.ts +3 -1
  65. package/ui.form-radio/URadio.vue +0 -5
  66. package/ui.form-radio/storybook/stories.ts +65 -25
  67. package/ui.form-radio/tests/URadio.test.ts +0 -17
  68. package/ui.form-radio-group/storybook/stories.ts +67 -0
  69. package/ui.form-select/storybook/stories.ts +99 -44
  70. package/ui.form-switch/storybook/stories.ts +35 -11
  71. package/ui.form-textarea/storybook/stories.ts +61 -20
  72. package/ui.loader/tests/ULoader.test.ts +3 -3
  73. package/ui.navigation-progress/tests/UProgress.test.ts +2 -3
  74. package/ui.text-block/tests/UText.test.ts +3 -3
  75. package/ui.text-files/storybook/stories.ts +30 -13
  76. package/ui.text-header/tests/UHeader.test.ts +3 -3
  77. package/ui.text-notify/tests/UNotify.test.ts +14 -18
  78. package/utils/node/helper.js +8 -9
@@ -578,10 +578,10 @@ describe("UModal", () => {
578
578
  });
579
579
 
580
580
  // CloseOnOverlay events
581
- it("emits events when overlay is clicked based on closeOnOverlay prop", () => {
581
+ it("emits events when overlay is clicked based on closeOnOverlay prop", async () => {
582
582
  const closeOnOverlay = [true, false];
583
583
 
584
- closeOnOverlay.forEach(async (value) => {
584
+ for (const value of closeOnOverlay) {
585
585
  const component = mount(UModal, {
586
586
  props: {
587
587
  modelValue,
@@ -594,15 +594,9 @@ describe("UModal", () => {
594
594
  await innerWrapper.trigger("mousedown");
595
595
  await innerWrapper.trigger("click");
596
596
 
597
- if (value) {
598
- expect(component.emitted("update:modelValue")).toBeTruthy();
599
- expect(component.emitted("update:modelValue")?.[0]).toEqual([false]);
600
- expect(component.emitted("close")).toBeTruthy();
601
- } else {
602
- expect(component.emitted("update:modelValue")).toBeFalsy();
603
- expect(component.emitted("close")).toBeFalsy();
604
- }
605
- });
597
+ expect(component.emitted("update:modelValue")).toEqual(value ? [[false]] : undefined);
598
+ expect(Boolean(component.emitted("close")?.length)).toBe(value);
599
+ }
606
600
  });
607
601
 
608
602
  it("does not close when mousedown on modal and mouseup on overlay", async () => {
@@ -627,10 +621,10 @@ describe("UModal", () => {
627
621
  });
628
622
 
629
623
  // CloseOnEsc events
630
- it("emits events when escape key is pressed based on closeOnEsc prop", () => {
624
+ it("emits events when escape key is pressed based on closeOnEsc prop", async () => {
631
625
  const closeOnEsc = [true, false];
632
626
 
633
- closeOnEsc.forEach(async (value) => {
627
+ for (const value of closeOnEsc) {
634
628
  const component = mount(UModal, {
635
629
  props: {
636
630
  modelValue,
@@ -642,15 +636,9 @@ describe("UModal", () => {
642
636
 
643
637
  await wrapper.trigger("keydown", { key: "Escape" });
644
638
 
645
- if (value) {
646
- expect(component.emitted("update:modelValue")).toBeTruthy();
647
- expect(component.emitted("update:modelValue")?.[0]).toEqual([false]);
648
- expect(component.emitted("close")).toBeTruthy();
649
- } else {
650
- expect(component.emitted("update:modelValue")).toBeFalsy();
651
- expect(component.emitted("close")).toBeFalsy();
652
- }
653
- });
639
+ expect(component.emitted("update:modelValue")).toEqual(value ? [[false]] : undefined);
640
+ expect(Boolean(component.emitted("close")?.length)).toBe(value);
641
+ }
654
642
  });
655
643
  });
656
644
 
@@ -284,8 +284,8 @@ function resolveSlotContent(slot: Slot | undefined, slotParams: Record<string, u
284
284
  function shouldRenderCellWrapper(row: Row, key: string): boolean {
285
285
  return Boolean(
286
286
  props.textEllipsis ||
287
- (props.search && isCellSearchMatch(key)) ||
288
- getCellContentClass(row, String(key)),
287
+ (props.search && isCellSearchMatch(key)) ||
288
+ getCellContentClass(row, String(key)),
289
289
  );
290
290
  }
291
291
 
@@ -62,6 +62,15 @@ describe("UCalendar.vue", () => {
62
62
  it("View – sets the correct view variant", () => {
63
63
  const viewCases = [View.Day, View.Month, View.Year];
64
64
 
65
+ const viewExpectations: Record<
66
+ (typeof viewCases)[number],
67
+ { day: boolean; month: boolean; year: boolean }
68
+ > = {
69
+ [View.Day]: { day: true, month: false, year: false },
70
+ [View.Month]: { day: false, month: true, year: false },
71
+ [View.Year]: { day: false, month: false, year: true },
72
+ };
73
+
65
74
  viewCases.forEach((view) => {
66
75
  const component = mount(UCalendar, {
67
76
  props: {
@@ -70,19 +79,11 @@ describe("UCalendar.vue", () => {
70
79
  },
71
80
  });
72
81
 
73
- if (view === View.Day) {
74
- expect(component.findComponent(DayView).exists()).toBe(true);
75
- expect(component.findComponent(MonthView).exists()).toBe(false);
76
- expect(component.findComponent(YearView).exists()).toBe(false);
77
- } else if (view === View.Month) {
78
- expect(component.findComponent(DayView).exists()).toBe(false);
79
- expect(component.findComponent(MonthView).exists()).toBe(true);
80
- expect(component.findComponent(YearView).exists()).toBe(false);
81
- } else if (view === View.Year) {
82
- expect(component.findComponent(DayView).exists()).toBe(false);
83
- expect(component.findComponent(MonthView).exists()).toBe(false);
84
- expect(component.findComponent(YearView).exists()).toBe(true);
85
- }
82
+ const expected = viewExpectations[view];
83
+
84
+ expect(component.findComponent(DayView).exists()).toBe(expected.day);
85
+ expect(component.findComponent(MonthView).exists()).toBe(expected.month);
86
+ expect(component.findComponent(YearView).exists()).toBe(expected.year);
86
87
  });
87
88
  });
88
89
 
@@ -332,13 +332,12 @@ describe("UCalendarDayView.vue", () => {
332
332
  const dayButtons = component.findAll("button");
333
333
  const targetDay = dayButtons.find((button) => button.text() === "15");
334
334
 
335
- if (targetDay) {
336
- await targetDay.trigger("mouseover");
335
+ expect(targetDay, "day 15 should exist in the grid").toBeTruthy();
336
+ await targetDay!.trigger("mouseover");
337
337
 
338
- const lastRangePreview = component.find('[vl-key="lastDayInRange"]');
338
+ const lastRangePreview = component.find('[vl-key="lastDayInRange"]');
339
339
 
340
- expect(lastRangePreview.exists()).toBe(true);
341
- }
340
+ expect(lastRangePreview.exists()).toBe(true);
342
341
  });
343
342
 
344
343
  it("applies another month range preview classes when hovering over another month day", async () => {
@@ -354,13 +353,14 @@ describe("UCalendarDayView.vue", () => {
354
353
 
355
354
  const anotherMonthDays = component.findAll('[vl-key="anotherMonthDay"]');
356
355
 
357
- if (anotherMonthDays.length > 0) {
358
- await anotherMonthDays[0].trigger("mouseover");
356
+ expect(anotherMonthDays.length, "need another-month cells for hover preview").toBeGreaterThan(
357
+ 0,
358
+ );
359
+ await anotherMonthDays[0].trigger("mouseover");
359
360
 
360
- const anotherMonthPreview = component.findAll('[vl-key="anotherMonthDayInRange"]');
361
+ const anotherMonthPreview = component.findAll('[vl-key="anotherMonthDayInRange"]');
361
362
 
362
- expect(anotherMonthPreview.length).toBeGreaterThanOrEqual(0);
363
- }
363
+ expect(anotherMonthPreview.length).toBeGreaterThanOrEqual(0);
364
364
  });
365
365
 
366
366
  it("clears range preview when mouse leaves day view", async () => {
@@ -376,14 +376,13 @@ describe("UCalendarDayView.vue", () => {
376
376
  const dayButtons = component.findAll("button");
377
377
  const targetDay = dayButtons.find((button) => button.text() === "15");
378
378
 
379
- if (targetDay) {
380
- await targetDay.trigger("mouseover");
381
- await component.trigger("mouseleave");
379
+ expect(targetDay, "day 15 should exist in the grid").toBeTruthy();
380
+ await targetDay!.trigger("mouseover");
381
+ await component.trigger("mouseleave");
382
382
 
383
- const lastRangePreview = component.find('[vl-key="lastDayInRange"]');
383
+ const lastRangePreview = component.find('[vl-key="lastDayInRange"]');
384
384
 
385
- expect(lastRangePreview.exists()).toBe(false);
386
- }
385
+ expect(lastRangePreview.exists()).toBe(false);
387
386
  });
388
387
  });
389
388
 
@@ -211,10 +211,5 @@ const {
211
211
 
212
212
  <UIcon v-else :name="config.defaults.checkedIcon" color="inherit" v-bind="checkedIconAttrs" />
213
213
  </label>
214
-
215
- <template #bottom>
216
- <!-- @slot Use it to add something below the checkbox. -->
217
- <slot name="bottom" />
218
- </template>
219
214
  </ULabel>
220
215
  </template>
@@ -17,6 +17,7 @@ import UChip from "../../ui.other-chip/UChip.vue";
17
17
 
18
18
  import type { Meta, StoryFn } from "@storybook/vue3-vite";
19
19
  import type { Props } from "../types";
20
+ import { ref } from "vue";
20
21
 
21
22
  interface UCheckboxArgs extends Props {
22
23
  slotTemplate?: string;
@@ -142,23 +143,60 @@ Partial.parameters = {
142
143
  },
143
144
  };
144
145
 
145
- export const LabelSlot = DefaultTemplate.bind({});
146
- LabelSlot.args = {
147
- slotTemplate: `
148
- <template #label>
149
- <URow gap="2xs" align="center">
150
- <UText>I agree to the <ULink label="Privacy Policy" /></UText>
151
- <UIcon name="contract" size="xs" />
152
- </URow>
153
- </template>
154
- `,
155
- };
146
+ export const Slots: StoryFn<UCheckboxArgs> = (args) => ({
147
+ components: { UCheckbox, UCol, UText, URow, ULink, UIcon },
148
+ setup: () => ({
149
+ args,
150
+ labelSlotValue: ref(false),
151
+ descriptionSlotValue: ref(false),
152
+ errorSlotValue: ref(false),
153
+ }),
154
+ template: `
155
+ <UCol gap="3xl">
156
+ <UCheckbox v-bind="args" v-model="labelSlotValue">
157
+ <template #label>
158
+ <URow gap="2xs" align="center">
159
+ <UText>I agree to the Privacy Policy</UText>
160
+ <UIcon name="contract" size="xs" />
161
+ </URow>
162
+ </template>
163
+ </UCheckbox>
164
+
165
+ <UCheckbox
166
+ v-bind="args"
167
+ v-model="descriptionSlotValue"
168
+ label="Email me product updates"
169
+ >
170
+ <template #description>
171
+ <URow align="center" gap="2xs" class="text-neutral">
172
+ <UIcon name="mail" size="xs" class="mt-0.5" color="primary" />
173
+ <UText size="sm">
174
+ Unsubscribe anytime.
175
+ <ULink label="Privacy policy" underlined size="sm" />.
176
+ </UText>
177
+ </URow>
178
+ </template>
179
+ </UCheckbox>
156
180
 
157
- export const BottomSlot = DefaultTemplate.bind({});
158
- BottomSlot.args = {
159
- slotTemplate: `
160
- <template #bottom>
161
- <ULink label="Learn more" size="sm" class="mr-1.5" />
162
- </template>
181
+ <UCheckbox
182
+ v-bind="args"
183
+ v-model="errorSlotValue"
184
+ label="Accept terms"
185
+ :error="true"
186
+ >
187
+ <template #error>
188
+ <URow align="center" gap="2xs">
189
+ <UIcon name="error" size="xs" color="error" />
190
+ <UText size="sm" color="error">
191
+ <ul>
192
+ <li>You must accept the terms to continue</li>
193
+ <li>This checkbox is required for registration</li>
194
+ <li>Clear the error by checking the box above</li>
195
+ </ul>
196
+ </UText>
197
+ </URow>
198
+ </template>
199
+ </UCheckbox>
200
+ </UCol>
163
201
  `,
164
- };
202
+ });
@@ -319,23 +319,6 @@ describe("UCheckbox.vue", () => {
319
319
 
320
320
  expect(errorElement.text()).toBe(customError);
321
321
  });
322
-
323
- it("Bottom – renders custom content from bottom slot", () => {
324
- const customBottomContent = "Custom Bottom Content";
325
-
326
- const component = mount(UCheckbox, {
327
- props: {
328
- label: "Test Label",
329
- },
330
- slots: {
331
- bottom: customBottomContent,
332
- },
333
- });
334
-
335
- const labelComponent = component.getComponent(ULabel);
336
-
337
- expect(labelComponent.text()).toContain(customBottomContent);
338
- });
339
322
  });
340
323
 
341
324
  describe("Events", () => {
@@ -189,3 +189,64 @@ CustomKeys.parameters = {
189
189
  },
190
190
  },
191
191
  };
192
+
193
+ export const Slots: StoryFn<UCheckboxGroupArgs> = (args) => ({
194
+ components: { UCheckboxGroup, UCol, UText, URow, ULink, UIcon },
195
+ setup: () => ({
196
+ args,
197
+ modelValueLabel: ref(""),
198
+ modelValueDescription: ref(""),
199
+ modelValueError: ref(""),
200
+ }),
201
+ template: `
202
+ <UCol gap="3xl">
203
+ <UCheckboxGroup
204
+ v-bind="args"
205
+ v-model="modelValueLabel"
206
+ name="LabelSlot"
207
+ >
208
+ <template #label>
209
+ <URow align="center" gap="2xs">
210
+ <UText>Choose how we notify you</UText>
211
+ <UIcon name="notifications" size="xs" color="neutral" />
212
+ </URow>
213
+ </template>
214
+ </UCheckboxGroup>
215
+
216
+ <UCheckboxGroup
217
+ v-bind="args"
218
+ v-model="modelValueDescription"
219
+ name="SlotsDescription"
220
+ label="Notification channels"
221
+ >
222
+ <template #description>
223
+ <URow align="center" gap="2xs" class="text-neutral">
224
+ <UIcon name="notifications" size="xs" color="primary" />
225
+ <UText size="sm">
226
+ You can change this later.
227
+ <ULink label="Notification settings" underlined size="sm" />.
228
+ </UText>
229
+ </URow>
230
+ </template>
231
+ </UCheckboxGroup>
232
+
233
+ <UCheckboxGroup
234
+ v-bind="args"
235
+ v-model="modelValueError"
236
+ name="SlotsError"
237
+ label="Select options"
238
+ :error="true"
239
+ >
240
+ <template #error>
241
+ <UText size="sm" color="error">
242
+ <ul>
243
+ <li>Invalid selection</li>
244
+ <li>At least one channel is required</li>
245
+ <li>Choose at least one option before submitting</li>
246
+ </ul>
247
+ </UText>
248
+ </template>
249
+ </UCheckboxGroup>
250
+ </UCol>
251
+ `,
252
+ });
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryFn } from "@storybook/vue3-vite";
2
+ import { ref } from "vue";
2
3
  import {
3
4
  getArgs,
4
5
  getArgTypes,
@@ -13,6 +14,7 @@ import URow from "../../ui.container-row/URow.vue";
13
14
  import UCol from "../../ui.container-col/UCol.vue";
14
15
  import UButton from "../../ui.button/UButton.vue";
15
16
  import UText from "../../ui.text-block/UText.vue";
17
+ import ULink from "../../ui.button-link/ULink.vue";
16
18
 
17
19
  import { COMPONENT_NAME } from "../constants";
18
20
 
@@ -364,43 +366,89 @@ export const IconProps: StoryFn<DefaultUDatePickerArgs> = (args) => ({
364
366
  });
365
367
 
366
368
  export const Slots: StoryFn<DefaultUDatePickerArgs> = (args) => ({
367
- components: { UDatePicker, URow, UButton },
368
- setup: () => ({ args }),
369
+ components: { UDatePicker, URow, UCol, UButton, UText, ULink, UIcon },
370
+ setup: () => ({
371
+ args,
372
+ descriptionSlotValue: ref(dateValue),
373
+ errorSlotValue: ref<Date | null>(null),
374
+ }),
369
375
  template: `
370
- <URow align="stretch">
371
- <UDatePicker
372
- v-bind="args"
373
- v-model="args.leftModel"
374
- class="w-full"
375
- :config="{ datepickerInput: { wrapper: 'pl-0' } }"
376
- >
377
- <template #left>
378
- <UButton
379
- label="Today"
380
- size="sm"
381
- variant="soft"
382
- class="h-full rounded-r-none"
383
- @click="args.leftModel = new Date()"
384
- />
385
- </template>
386
- </UDatePicker>
387
-
388
- <UDatePicker
389
- v-bind="args"
390
- v-model="args.modelValue"
391
- class="w-full"
392
- :config="{ datepickerInput: { wrapper: 'pr-0' } }"
393
- >
394
- <template #right>
395
- <UButton
396
- label="Clear"
397
- size="sm"
398
- variant="ghost"
399
- class="h-full rounded-l-none"
400
- @click="args.modelValue = null"
401
- />
402
- </template>
403
- </UDatePicker>
404
- </URow>
376
+ <UCol gap="3xl">
377
+ <URow block>
378
+ <UDatePicker
379
+ v-bind="args"
380
+ v-model="args.leftModel"
381
+ class="w-full"
382
+ :config="{ datepickerInput: { wrapper: 'pl-0' } }"
383
+ >
384
+ <template #left>
385
+ <UButton
386
+ label="Today"
387
+ size="sm"
388
+ variant="soft"
389
+ class="h-full rounded-r-none"
390
+ @click="args.leftModel = new Date()"
391
+ />
392
+ </template>
393
+ </UDatePicker>
394
+
395
+ <UDatePicker
396
+ v-bind="args"
397
+ v-model="args.modelValue"
398
+ class="w-full"
399
+ :config="{ datepickerInput: { wrapper: 'pr-0' } }"
400
+ >
401
+ <template #right>
402
+ <UButton
403
+ label="Clear"
404
+ size="sm"
405
+ variant="ghost"
406
+ class="h-full rounded-l-none"
407
+ @click="args.modelValue = null"
408
+ />
409
+ </template>
410
+ </UDatePicker>
411
+ </URow>
412
+
413
+ <URow block>
414
+ <UDatePicker
415
+ v-bind="args"
416
+ v-model="descriptionSlotValue"
417
+ label="Event date"
418
+ class="w-full"
419
+ >
420
+ <template #description>
421
+ <URow align="center" gap="2xs" class="text-neutral">
422
+ <UIcon name="event" size="xs" color="primary" />
423
+ <UText size="sm">
424
+ Uses your local timezone.
425
+ <ULink label="Date policy" underlined size="sm" />.
426
+ </UText>
427
+ </URow>
428
+ </template>
429
+ </UDatePicker>
430
+
431
+ <UDatePicker
432
+ v-bind="args"
433
+ v-model="errorSlotValue"
434
+ label="Due date"
435
+ :error="true"
436
+ class="w-full"
437
+ >
438
+ <template #error>
439
+ <URow align="center" gap="2xs">
440
+ <UIcon name="error" size="xs" color="error" />
441
+ <UText size="sm" color="error">
442
+ <ul>
443
+ <li>The selected date is not available</li>
444
+ <li>Pick a day within the allowed range</li>
445
+ <li>Weekends and holidays are excluded</li>
446
+ </ul>
447
+ </UText>
448
+ </URow>
449
+ </template>
450
+ </UDatePicker>
451
+ </URow>
452
+ </UCol>
405
453
  `,
406
454
  });
@@ -15,6 +15,7 @@ import UCol from "../../ui.container-col/UCol.vue";
15
15
  import UIcon from "../../ui.image-icon/UIcon.vue";
16
16
  import UButton from "../../ui.button/UButton.vue";
17
17
  import UText from "../../ui.text-block/UText.vue";
18
+ import ULink from "../../ui.button-link/ULink.vue";
18
19
 
19
20
  import { addDays } from "../../ui.form-calendar/utilDate";
20
21
 
@@ -296,44 +297,102 @@ export const IconProps: StoryFn<DefaultUDatePickerRangeArgs> = (args) => ({
296
297
  });
297
298
 
298
299
  export const Slots: StoryFn<DefaultUDatePickerRangeArgs> = (args) => ({
299
- components: { UDatePickerRange, URow, UButton },
300
- setup: () => ({ args, fromDate, toDate, leftModel: ref({ from: null, to: null }) }),
300
+ components: { UDatePickerRange, URow, UCol, UButton, UText, ULink, UIcon },
301
+ setup: () => ({
302
+ args,
303
+ fromDate,
304
+ toDate,
305
+ leftModel: ref({ from: null, to: null }),
306
+ descriptionSlotValue: ref({ from: fromDate, to: toDate }),
307
+ errorSlotValue: ref({ from: null, to: null }),
308
+ }),
301
309
  template: `
302
- <URow align="stretch">
303
- <UDatePickerRange
304
- label="Select date range"
305
- v-model="leftModel"
306
- variant="input"
307
- class="w-full"
308
- :config="{ datepickerInput: { wrapper: 'pl-0' } }"
309
- >
310
- <template #left>
311
- <UButton
312
- label="2 weeks"
313
- size="sm"
314
- variant="soft"
315
- class="h-full rounded-r-none"
316
- @click="leftModel = { from: fromDate, to: toDate }"
317
- />
318
- </template>
319
- </UDatePickerRange>
320
- <UDatePickerRange
321
- label="Select date range"
322
- v-model="args.modelValue"
323
- variant="input"
324
- class="w-full"
325
- :config="{ datepickerInput: { wrapper: 'pr-0' } }"
326
- >
327
- <template #right>
328
- <UButton
329
- label="Clear"
330
- size="sm"
331
- variant="ghost"
332
- class="h-full rounded-l-none"
333
- @click="args.modelValue = { from: null, to: null }"
334
- />
335
- </template>
336
- </UDatePickerRange>
337
- </URow>
310
+ <UCol gap="3xl">
311
+ <URow block>
312
+ <UDatePickerRange
313
+ label="Select date range"
314
+ v-model="leftModel"
315
+ variant="input"
316
+ class="w-full"
317
+ :config="{ datepickerInput: { wrapper: 'pl-0' } }"
318
+ >
319
+ <template #left>
320
+ <UButton
321
+ label="2 weeks"
322
+ size="sm"
323
+ variant="soft"
324
+ class="h-full rounded-r-none"
325
+ @click="leftModel = { from: fromDate, to: toDate }"
326
+ />
327
+ </template>
328
+ </UDatePickerRange>
329
+ <UDatePickerRange
330
+ label="Select date range"
331
+ v-model="args.modelValue"
332
+ variant="input"
333
+ class="w-full"
334
+ :config="{ datepickerInput: { wrapper: 'pr-0' } }"
335
+ >
336
+ <template #right>
337
+ <UButton
338
+ label="Clear"
339
+ size="sm"
340
+ variant="ghost"
341
+ class="h-full rounded-l-none"
342
+ @click="args.modelValue = { from: null, to: null }"
343
+ />
344
+ </template>
345
+ </UDatePickerRange>
346
+ </URow>
347
+
348
+ <URow block>
349
+ <UDatePickerRange
350
+ v-bind="args"
351
+ v-model="descriptionSlotValue"
352
+ variant="input"
353
+ label="Report period"
354
+ class="w-full"
355
+ >
356
+ <template #description>
357
+ <URow align="center" gap="2xs" class="text-neutral">
358
+ <UIcon name="date_range" size="xs" color="primary" />
359
+ <UText size="sm">
360
+ Inclusive of start and end.
361
+ <ULink label="Fiscal calendar" underlined size="sm" />.
362
+ </UText>
363
+ </URow>
364
+ </template>
365
+ </UDatePickerRange>
366
+
367
+ <UDatePickerRange
368
+ v-bind="args"
369
+ v-model="errorSlotValue"
370
+ variant="input"
371
+ label="Stay dates"
372
+ :error="true"
373
+ class="w-full"
374
+ >
375
+ <template #error>
376
+ <URow align="center" gap="2xs">
377
+ <UIcon name="error" size="xs" color="error" />
378
+ <UText size="sm" color="error">
379
+ <ul>
380
+ <li>End date must be after the start date</li>
381
+ <li>Range cannot span more than 30 days</li>
382
+ <li>Select both dates from the calendar</li>
383
+ </ul>
384
+ </UText>
385
+ </URow>
386
+ </template>
387
+ </UDatePickerRange>
388
+ </URow>
389
+ </UCol>
338
390
  `,
339
391
  });
392
+ Slots.parameters = {
393
+ docs: {
394
+ story: {
395
+ height: "700px",
396
+ },
397
+ },
398
+ };