vueless 1.4.5 → 1.4.6-beta.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 (45) hide show
  1. package/package.json +1 -1
  2. package/ui.form-checkbox/UCheckbox.vue +8 -0
  3. package/ui.form-checkbox/tests/UCheckbox.test.ts +18 -0
  4. package/ui.form-checkbox/types.ts +1 -1
  5. package/ui.form-checkbox-group/UCheckboxGroup.vue +8 -0
  6. package/ui.form-checkbox-group/tests/UCheckboxGroup.test.ts +18 -0
  7. package/ui.form-checkbox-group/types.ts +1 -1
  8. package/ui.form-date-picker/UDatePicker.vue +8 -0
  9. package/ui.form-date-picker/tests/UDatePicker.test.ts +20 -0
  10. package/ui.form-date-picker/types.ts +1 -1
  11. package/ui.form-date-picker-range/UDatePickerRange.vue +8 -0
  12. package/ui.form-date-picker-range/tests/UDatePickerRange.test.ts +21 -0
  13. package/ui.form-date-picker-range/types.ts +1 -1
  14. package/ui.form-input/types.ts +1 -1
  15. package/ui.form-input-file/UInputFile.vue +8 -0
  16. package/ui.form-input-file/tests/UInputFile.test.ts +18 -0
  17. package/ui.form-input-file/types.ts +1 -1
  18. package/ui.form-input-number/UInputNumber.vue +8 -0
  19. package/ui.form-input-number/tests/UInputNumber.test.ts +19 -0
  20. package/ui.form-input-number/types.ts +1 -1
  21. package/ui.form-input-password/UInputPassword.vue +8 -0
  22. package/ui.form-input-password/tests/UInputPassword.test.ts +19 -0
  23. package/ui.form-input-password/types.ts +1 -1
  24. package/ui.form-input-search/UInputSearch.vue +8 -0
  25. package/ui.form-input-search/tests/UInputSearch.test.ts +19 -0
  26. package/ui.form-input-search/types.ts +1 -1
  27. package/ui.form-label/ULabel.vue +17 -6
  28. package/ui.form-label/tests/ULabel.test.ts +14 -0
  29. package/ui.form-label/types.ts +2 -2
  30. package/ui.form-radio/URadio.vue +8 -0
  31. package/ui.form-radio/tests/URadio.test.ts +18 -0
  32. package/ui.form-radio/types.ts +1 -1
  33. package/ui.form-radio-group/URadioGroup.vue +8 -0
  34. package/ui.form-radio-group/tests/URadioGroup.test.ts +19 -0
  35. package/ui.form-radio-group/types.ts +1 -1
  36. package/ui.form-select/USelect.vue +8 -0
  37. package/ui.form-select/tests/USelect.test.ts +19 -0
  38. package/ui.form-select/types.ts +1 -1
  39. package/ui.form-switch/USwitch.vue +8 -0
  40. package/ui.form-switch/tests/USwitch.test.ts +18 -0
  41. package/ui.form-textarea/UTextarea.vue +8 -0
  42. package/ui.form-textarea/tests/UTextarea.test.ts +18 -0
  43. package/ui.form-textarea/types.ts +1 -1
  44. package/ui.text-files/UFiles.vue +8 -0
  45. package/ui.text-files/tests/UFiles.test.ts +19 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.4.5",
3
+ "version": "1.4.6-beta.0",
4
4
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
5
5
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
6
6
  "homepage": "https://vueless.com",
@@ -165,6 +165,14 @@ const {
165
165
  <slot name="label" :label="label" />
166
166
  </template>
167
167
 
168
+ <template #description>
169
+ <!--
170
+ @slot Use this to add custom content instead of the description.
171
+ @binding {string} description
172
+ -->
173
+ <slot name="description" :description="description" />
174
+ </template>
175
+
168
176
  <input
169
177
  :id="elementId"
170
178
  type="checkbox"
@@ -284,6 +284,24 @@ describe("UCheckbox.vue", () => {
284
284
  expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
285
285
  });
286
286
 
287
+ it("Description – renders custom content from description slot", () => {
288
+ const customDescription = "Custom description content";
289
+
290
+ const component = mount(UCheckbox, {
291
+ props: {
292
+ description: "Default description",
293
+ },
294
+ slots: {
295
+ description: customDescription,
296
+ },
297
+ });
298
+
299
+ const labelComponent = component.getComponent(ULabel);
300
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
301
+
302
+ expect(descriptionElement.text()).toBe(customDescription);
303
+ });
304
+
287
305
  it("Bottom – renders custom content from bottom slot", () => {
288
306
  const customBottomContent = "Custom Bottom Content";
289
307
 
@@ -53,7 +53,7 @@ export interface Props {
53
53
  /**
54
54
  * Error message.
55
55
  */
56
- error?: string;
56
+ error?: string | boolean;
57
57
 
58
58
  /**
59
59
  * Label placement.
@@ -96,6 +96,14 @@ const { getDataTest, groupLabelAttrs, groupCheckboxAttrs, listAttrs } =
96
96
  <slot name="label" :label="label" />
97
97
  </template>
98
98
 
99
+ <template #description>
100
+ <!--
101
+ @slot Use this to add custom content instead of the description.
102
+ @binding {string} description
103
+ -->
104
+ <slot name="description" :description="description" />
105
+ </template>
106
+
99
107
  <div ref="list" v-bind="listAttrs">
100
108
  <!-- @slot Use it to add UCheckbox directly. -->
101
109
  <slot>
@@ -289,6 +289,24 @@ describe("UCheckboxGroup.vue", () => {
289
289
  expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
290
290
  });
291
291
 
292
+ it("Description – renders custom content from description slot", () => {
293
+ const customDescription = "Custom description content";
294
+
295
+ const component = mount(UCheckboxGroup, {
296
+ props: {
297
+ description: "Default description",
298
+ },
299
+ slots: {
300
+ description: customDescription,
301
+ },
302
+ });
303
+
304
+ const labelComponent = component.getComponent(ULabel);
305
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
306
+
307
+ expect(descriptionElement.text()).toBe(customDescription);
308
+ });
309
+
292
310
  it("Default slot – renders custom UCheckbox components", () => {
293
311
  const component = mount(UCheckboxGroup, {
294
312
  props: {
@@ -39,7 +39,7 @@ export interface Props {
39
39
  /**
40
40
  * Checkbox group error message.
41
41
  */
42
- error?: string;
42
+ error?: string | boolean;
43
43
 
44
44
  /**
45
45
  * Checkbox group size.
@@ -304,6 +304,14 @@ watchEffect(() => {
304
304
  @keydown.esc="deactivate"
305
305
  @keydown="onTextInputKeyDown"
306
306
  >
307
+ <template #description>
308
+ <!--
309
+ @slot Use this to add custom content instead of the description.
310
+ @binding {string} description
311
+ -->
312
+ <slot name="description" :description="description" />
313
+ </template>
314
+
307
315
  <template #left="{ iconName }">
308
316
  <!--
309
317
  @slot Use it add something before the date.
@@ -4,6 +4,7 @@ import { nextTick } from "vue";
4
4
 
5
5
  import UDatePicker from "../UDatePicker.vue";
6
6
  import UInput from "../../ui.form-input/UInput.vue";
7
+ import ULabel from "../../ui.form-label/ULabel.vue";
7
8
  import UIcon from "../../ui.image-icon/UIcon.vue";
8
9
 
9
10
  import type { Props } from "../types";
@@ -476,6 +477,25 @@ describe("UDatePicker.vue", () => {
476
477
 
477
478
  expect(component.find(`.${slotClass}`).exists()).toBe(true);
478
479
  });
480
+
481
+ it("Description – renders custom content from description slot", () => {
482
+ const customDescription = "Custom description content";
483
+
484
+ const component = mount(UDatePicker, {
485
+ props: {
486
+ modelValue: new Date(),
487
+ description: "Default description",
488
+ },
489
+ slots: {
490
+ description: customDescription,
491
+ },
492
+ });
493
+
494
+ const labelComponent = component.getComponent(UInput).getComponent(ULabel);
495
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
496
+
497
+ expect(descriptionElement.text()).toBe(customDescription);
498
+ });
479
499
  });
480
500
 
481
501
  describe("Exposed Properties", () => {
@@ -34,7 +34,7 @@ export interface Props<TModelValue> {
34
34
  /**
35
35
  * Datepicker error message.
36
36
  */
37
- error?: string;
37
+ error?: string | boolean;
38
38
 
39
39
  /**
40
40
  * Make datepicker disabled.
@@ -639,6 +639,14 @@ watchEffect(() => {
639
639
  @focus="activate"
640
640
  @keydown.esc="deactivate"
641
641
  >
642
+ <template #description>
643
+ <!--
644
+ @slot Use this to add custom content instead of the description.
645
+ @binding {string} description
646
+ -->
647
+ <slot name="description" :description="description" />
648
+ </template>
649
+
642
650
  <template #left="{ iconName }">
643
651
  <!--
644
652
  @slot Use it to add something before the date.
@@ -3,6 +3,7 @@ import { describe, it, expect } from "vitest";
3
3
 
4
4
  import UDatePickerRange from "../UDatePickerRange.vue";
5
5
  import UInput from "../../ui.form-input/UInput.vue";
6
+ import ULabel from "../../ui.form-label/ULabel.vue";
6
7
  import UButton from "../../ui.button/UButton.vue";
7
8
  import UDatePickerRangePeriodMenu from "../UDatePickerRangePeriodMenu.vue";
8
9
 
@@ -583,6 +584,26 @@ describe("UDatePickerRange.vue", () => {
583
584
 
584
585
  expect(component.text()).toContain(`Icon: ${rightIcon}`);
585
586
  });
587
+
588
+ it("Description – renders custom content from description slot", () => {
589
+ const customDescription = "Custom description content";
590
+
591
+ const component = mount(UDatePickerRange, {
592
+ props: {
593
+ variant: "input",
594
+ modelValue: { from: null, to: null },
595
+ description: "Default description",
596
+ },
597
+ slots: {
598
+ description: customDescription,
599
+ },
600
+ });
601
+
602
+ const labelComponent = component.getComponent(UInput).getComponent(ULabel);
603
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
604
+
605
+ expect(descriptionElement.text()).toBe(customDescription);
606
+ });
586
607
  });
587
608
 
588
609
  describe("Events", () => {
@@ -110,7 +110,7 @@ export interface Props<TModelValue> {
110
110
  /**
111
111
  * Datepicker error message.
112
112
  */
113
- error?: string;
113
+ error?: string | boolean;
114
114
 
115
115
  /**
116
116
  * Make datepicker disabled.
@@ -33,7 +33,7 @@ export interface Props {
33
33
  /**
34
34
  * Error message.
35
35
  */
36
- error?: string;
36
+ error?: string | boolean;
37
37
 
38
38
  /**
39
39
  * Input size.
@@ -313,6 +313,14 @@ const {
313
313
  <slot name="label" :label="label" />
314
314
  </template>
315
315
 
316
+ <template #description>
317
+ <!--
318
+ @slot Use this to add custom content instead of the description.
319
+ @binding {string} description
320
+ -->
321
+ <slot name="description" :description="description" />
322
+ </template>
323
+
316
324
  <div ref="dropZone" :ondrop="onDrop" v-bind="dropzoneAttrs">
317
325
  <UText v-if="hasSlotContent($slots['top'])" :size="size" v-bind="descriptionTopAttrs">
318
326
  <!-- @slot Use it to add something above the component content. -->
@@ -292,6 +292,24 @@ describe("UInputFile.vue", () => {
292
292
  expect(labelElement.text()).toBe(`Custom ${defaultLabel}`);
293
293
  });
294
294
 
295
+ it("Description – renders custom content from description slot", () => {
296
+ const customDescription = "Custom description content";
297
+
298
+ const component = mount(UInputFile, {
299
+ props: {
300
+ description: "Default description",
301
+ },
302
+ slots: {
303
+ description: customDescription,
304
+ },
305
+ });
306
+
307
+ const labelComponent = component.getComponent(ULabel);
308
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
309
+
310
+ expect(descriptionElement.text()).toBe(customDescription);
311
+ });
312
+
295
313
  it("Top – renders custom content from top slot", () => {
296
314
  const testClass = "custom-top";
297
315
 
@@ -23,7 +23,7 @@ export interface Props {
23
23
  /**
24
24
  * Error message.
25
25
  */
26
- error?: string;
26
+ error?: string | boolean;
27
27
 
28
28
  /**
29
29
  * Label placement.
@@ -179,6 +179,14 @@ const { getDataTest, numberInputAttrs } = useUI<Config>(defaultConfig);
179
179
  @blur="onBlur"
180
180
  @input="onInput"
181
181
  >
182
+ <template #description>
183
+ <!--
184
+ @slot Use this to add custom content instead of the description.
185
+ @binding {string} description
186
+ -->
187
+ <slot name="description" :description="description" />
188
+ </template>
189
+
182
190
  <template #left>
183
191
  <!--
184
192
  @slot Use it to add something left.
@@ -3,6 +3,7 @@ import { describe, it, expect } from "vitest";
3
3
 
4
4
  import UInputNumber from "../UInputNumber.vue";
5
5
  import UInput from "../../ui.form-input/UInput.vue";
6
+ import ULabel from "../../ui.form-label/ULabel.vue";
6
7
  import UIcon from "../../ui.image-icon/UIcon.vue";
7
8
 
8
9
  import type { Props } from "../types";
@@ -412,6 +413,24 @@ describe("UInputNumber.vue", () => {
412
413
  expect(component.find(`.${slotClass}`).exists()).toBe(true);
413
414
  expect(component.findComponent(UIcon).exists()).toBe(false);
414
415
  });
416
+
417
+ it("Description – renders custom content from description slot", () => {
418
+ const customDescription = "Custom description content";
419
+
420
+ const component = mount(UInputNumber, {
421
+ props: {
422
+ description: "Default description",
423
+ },
424
+ slots: {
425
+ description: customDescription,
426
+ },
427
+ });
428
+
429
+ const labelComponent = component.getComponent(UInput).getComponent(ULabel);
430
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
431
+
432
+ expect(descriptionElement.text()).toBe(customDescription);
433
+ });
415
434
  });
416
435
 
417
436
  describe("Exposed properties", () => {
@@ -52,7 +52,7 @@ export interface Props {
52
52
  /**
53
53
  * Error message.
54
54
  */
55
- error?: string;
55
+ error?: string | boolean;
56
56
 
57
57
  /**
58
58
  * Input size.
@@ -125,6 +125,14 @@ const { getDataTest, config, passwordInputAttrs, passwordIconAttrs, passwordIcon
125
125
  <slot name="label" :label="label" />
126
126
  </template>
127
127
 
128
+ <template #description>
129
+ <!--
130
+ @slot Use this to add custom content instead of the description.
131
+ @binding {string} description
132
+ -->
133
+ <slot name="description" :description="description" />
134
+ </template>
135
+
128
136
  <template #right>
129
137
  <div v-bind="passwordIconWrapperAttrs" @click="onClickShowPassword">
130
138
  <!--
@@ -3,6 +3,7 @@ import { describe, it, expect } from "vitest";
3
3
 
4
4
  import UInputPassword from "../UInputPassword.vue";
5
5
  import UInput from "../../ui.form-input/UInput.vue";
6
+ import ULabel from "../../ui.form-label/ULabel.vue";
6
7
  import UIcon from "../../ui.image-icon/UIcon.vue";
7
8
 
8
9
  import type { Props } from "../types";
@@ -299,5 +300,23 @@ describe("UInputPassword.vue", () => {
299
300
 
300
301
  expect(component.html()).toContain(`Icon: ${visibilityIcon}`);
301
302
  });
303
+
304
+ it("Description – renders custom content from description slot", () => {
305
+ const customDescription = "Custom description content";
306
+
307
+ const component = mount(UInputPassword, {
308
+ props: {
309
+ description: "Default description",
310
+ },
311
+ slots: {
312
+ description: customDescription,
313
+ },
314
+ });
315
+
316
+ const labelComponent = component.getComponent(UInput).getComponent(ULabel);
317
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
318
+
319
+ expect(descriptionElement.text()).toBe(customDescription);
320
+ });
302
321
  });
303
322
  });
@@ -33,7 +33,7 @@ export interface Props {
33
33
  /**
34
34
  * Error message.
35
35
  */
36
- error?: string;
36
+ error?: string | boolean;
37
37
 
38
38
  /**
39
39
  * Input size.
@@ -144,6 +144,14 @@ const {
144
144
  @update:model-value="onUpdateValue"
145
145
  @keyup.enter="onKeyupEnter"
146
146
  >
147
+ <template #description>
148
+ <!--
149
+ @slot Use this to add custom content instead of the description.
150
+ @binding {string} description
151
+ -->
152
+ <slot name="description" :description="description" />
153
+ </template>
154
+
147
155
  <template #left>
148
156
  <!-- @slot Use it to add something before the text. -->
149
157
  <slot name="left" />
@@ -2,6 +2,7 @@ import { flushPromises, mount } from "@vue/test-utils";
2
2
  import { describe, it, expect, vi } from "vitest";
3
3
 
4
4
  import UInputSearch from "../UInputSearch.vue";
5
+ import ULabel from "../../ui.form-label/ULabel.vue";
5
6
  import UInput from "../../ui.form-input/UInput.vue";
6
7
  import UIcon from "../../ui.image-icon/UIcon.vue";
7
8
  import UButton from "../../ui.button/UButton.vue";
@@ -301,6 +302,24 @@ describe("UInputSearch.vue", () => {
301
302
 
302
303
  expect(component.getComponent(UButton).props("label")).toBe(searchButtonLabel);
303
304
  });
305
+
306
+ it("Description – renders custom content from description slot", () => {
307
+ const customDescription = "Custom description content";
308
+
309
+ const component = mount(UInputSearch, {
310
+ props: {
311
+ description: "Default description",
312
+ },
313
+ slots: {
314
+ description: customDescription,
315
+ },
316
+ });
317
+
318
+ const labelComponent = component.getComponent(UInput).getComponent(ULabel);
319
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
320
+
321
+ expect(descriptionElement.text()).toBe(customDescription);
322
+ });
304
323
  });
305
324
 
306
325
  describe("Events", () => {
@@ -38,7 +38,7 @@ export interface Props {
38
38
  /**
39
39
  * Error message.
40
40
  */
41
- error?: string;
41
+ error?: string | boolean;
42
42
 
43
43
  /**
44
44
  * Minimum character length for search.
@@ -51,10 +51,14 @@ const wrapperElement = computed(() => {
51
51
  return wrapperRef.value;
52
52
  });
53
53
 
54
- const isShownError = computed(() => {
54
+ const hasErrorState = computed(() => {
55
55
  return Boolean(props.error) && !props.disabled;
56
56
  });
57
57
 
58
+ const showErrorMessage = computed(() => {
59
+ return hasErrorState.value && typeof props.error !== "boolean";
60
+ });
61
+
58
62
  function onClick(event: MouseEvent) {
59
63
  emit("click", event);
60
64
  }
@@ -78,7 +82,7 @@ defineExpose({
78
82
  * Applies: `class`, `config`, redefined default `props` and dev `vl-...` attributes.
79
83
  */
80
84
  const mutatedProps = computed(() => ({
81
- error: Boolean(props.error) && !props.disabled,
85
+ error: hasErrorState.value,
82
86
  label: Boolean(props.label) || hasSlotContent(slots["label"], { label: props.label }),
83
87
  description:
84
88
  Boolean(props.description) ||
@@ -132,7 +136,7 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
132
136
  </component>
133
137
 
134
138
  <div
135
- v-if="isShownError"
139
+ v-if="showErrorMessage"
136
140
  v-bind="errorAttrs"
137
141
  :data-test="getDataTest('error')"
138
142
  v-text="error"
@@ -140,7 +144,7 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
140
144
 
141
145
  <div
142
146
  v-if="
143
- (description || hasSlotContent(slots['description'], { description })) && !isShownError
147
+ (description || hasSlotContent(slots['description'], { description })) && !hasErrorState
144
148
  "
145
149
  v-bind="descriptionAttrs"
146
150
  :data-test="getDataTest('description')"
@@ -184,10 +188,17 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
184
188
  <slot />
185
189
  </div>
186
190
 
187
- <div v-if="isShownError" v-bind="errorAttrs" :data-test="getDataTest('error')" v-text="error" />
191
+ <div
192
+ v-if="showErrorMessage"
193
+ v-bind="errorAttrs"
194
+ :data-test="getDataTest('error')"
195
+ v-text="error"
196
+ />
188
197
 
189
198
  <div
190
- v-if="(description || hasSlotContent(slots['description'], { description })) && !isShownError"
199
+ v-if="
200
+ (description || hasSlotContent(slots['description'], { description })) && !hasErrorState
201
+ "
191
202
  v-bind="descriptionAttrs"
192
203
  :data-test="getDataTest('description')"
193
204
  >
@@ -97,6 +97,20 @@ describe("ULabel.vue", () => {
97
97
  expect(errorElement.text()).toBe(error);
98
98
  });
99
99
 
100
+ it("Error – boolean error applies error styling without rendering error text", () => {
101
+ const errorClasses = "text-error";
102
+
103
+ const component = mount(ULabel, {
104
+ props: {
105
+ error: true,
106
+ label: "Label",
107
+ },
108
+ });
109
+
110
+ expect(component.find("[vl-key='label']").attributes("class")).toContain(errorClasses);
111
+ expect(component.find("[vl-key='error']").exists()).toBe(false);
112
+ });
113
+
100
114
  it("Align – applies correct classes for align prop", () => {
101
115
  const alignCases = {
102
116
  top: "flex-col",
@@ -26,9 +26,9 @@ export interface Props {
26
26
  description?: string;
27
27
 
28
28
  /**
29
- * Label error message.
29
+ * Label error message, or `true` for error styling without showing message text.
30
30
  */
31
- error?: string;
31
+ error?: string | boolean;
32
32
 
33
33
  /**
34
34
  * Label align.
@@ -124,6 +124,14 @@ const { getDataTest, radioLabelAttrs, radioAttrs } = useUI<Config>(defaultConfig
124
124
  <slot name="label" :label="label" />
125
125
  </template>
126
126
 
127
+ <template #description>
128
+ <!--
129
+ @slot Use this to add custom content instead of the description.
130
+ @binding {string} description
131
+ -->
132
+ <slot name="description" :description="description" />
133
+ </template>
134
+
127
135
  <input
128
136
  :id="elementId"
129
137
  type="radio"
@@ -206,6 +206,24 @@ describe("URadio.vue", () => {
206
206
  expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
207
207
  });
208
208
 
209
+ it("Description – renders custom content from description slot", () => {
210
+ const customDescription = "Custom description content";
211
+
212
+ const component = mount(URadio, {
213
+ props: {
214
+ description: "Default description",
215
+ },
216
+ slots: {
217
+ description: customDescription,
218
+ },
219
+ });
220
+
221
+ const labelComponent = component.getComponent(ULabel);
222
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
223
+
224
+ expect(descriptionElement.text()).toBe(customDescription);
225
+ });
226
+
209
227
  it("Bottom – renders custom content from bottom slot", () => {
210
228
  const customBottomContent = "Custom Bottom Content";
211
229
 
@@ -35,7 +35,7 @@ export interface Props {
35
35
  /**
36
36
  * Error message.
37
37
  */
38
- error?: string;
38
+ error?: string | boolean;
39
39
 
40
40
  /**
41
41
  * Radio name.
@@ -79,6 +79,14 @@ const { getDataTest, groupLabelAttrs, listAttrs, groupRadioAttrs } = useUI<Confi
79
79
  <slot name="label" :label="label" />
80
80
  </template>
81
81
 
82
+ <template #description>
83
+ <!--
84
+ @slot Use this to add custom content instead of the description.
85
+ @binding {string} description
86
+ -->
87
+ <slot name="description" :description="description" />
88
+ </template>
89
+
82
90
  <div ref="list" v-bind="listAttrs">
83
91
  <!-- @slot Use it to add URadio directly. -->
84
92
  <slot>
@@ -303,6 +303,25 @@ describe("URadioGroup.vue", () => {
303
303
  expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
304
304
  });
305
305
 
306
+ it("Description – renders custom content from description slot", () => {
307
+ const customDescription = "Custom description content";
308
+
309
+ const component = mount(URadioGroup, {
310
+ props: {
311
+ description: "Default description",
312
+ name: defaultName,
313
+ },
314
+ slots: {
315
+ description: customDescription,
316
+ },
317
+ });
318
+
319
+ const labelComponent = component.getComponent(ULabel);
320
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
321
+
322
+ expect(descriptionElement.text()).toBe(customDescription);
323
+ });
324
+
306
325
  it("Default slot – renders custom URadio components", () => {
307
326
  const component = mount(URadioGroup, {
308
327
  props: {
@@ -52,7 +52,7 @@ export interface Props {
52
52
  /**
53
53
  * Radio group error message.
54
54
  */
55
- error?: string;
55
+ error?: string | boolean;
56
56
 
57
57
  /**
58
58
  * Radio size.
@@ -538,6 +538,14 @@ const {
538
538
  </div>
539
539
  </template>
540
540
 
541
+ <template #description>
542
+ <!--
543
+ @slot Use this to add custom content instead of the description.
544
+ @binding {string} description
545
+ -->
546
+ <slot name="description" :description="description" />
547
+ </template>
548
+
541
549
  <div
542
550
  ref="wrapper"
543
551
  :tabindex="searchable || disabled ? -1 : 0"
@@ -587,6 +587,25 @@ describe("USelect.vue", () => {
587
587
  expect(component.text()).toContain(`Modified ${defaultLabel}`);
588
588
  });
589
589
 
590
+ it("Description – renders custom content from description slot", () => {
591
+ const customDescription = "Custom description content";
592
+
593
+ const component = mount(USelect, {
594
+ props: {
595
+ description: "Default description",
596
+ options: defaultOptions,
597
+ },
598
+ slots: {
599
+ description: customDescription,
600
+ },
601
+ });
602
+
603
+ const labelComponent = component.getComponent(ULabel);
604
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
605
+
606
+ expect(descriptionElement.text()).toBe(customDescription);
607
+ });
608
+
590
609
  it("Left – renders custom content from left slot", () => {
591
610
  const slotContent = "Left Slot Content";
592
611
 
@@ -39,7 +39,7 @@ export interface Props {
39
39
  /**
40
40
  * Select error message.
41
41
  */
42
- error?: string;
42
+ error?: string | boolean;
43
43
 
44
44
  /**
45
45
  * Select size.
@@ -129,6 +129,14 @@ const {
129
129
  <slot name="label" :label="label" />
130
130
  </template>
131
131
 
132
+ <template #description>
133
+ <!--
134
+ @slot Use this to add custom content instead of the description.
135
+ @binding {string} description
136
+ -->
137
+ <slot name="description" :description="description" />
138
+ </template>
139
+
132
140
  <div
133
141
  ref="wrapper"
134
142
  tabindex="0"
@@ -229,6 +229,24 @@ describe("USwitch.vue", () => {
229
229
 
230
230
  expect(component.find("label").text()).toBe(`Modified ${defaultLabel}`);
231
231
  });
232
+
233
+ it("Description – renders custom content from description slot", () => {
234
+ const customDescription = "Custom description content";
235
+
236
+ const component = mount(USwitch, {
237
+ props: {
238
+ description: "Default description",
239
+ },
240
+ slots: {
241
+ description: customDescription,
242
+ },
243
+ });
244
+
245
+ const labelComponent = component.getComponent(ULabel);
246
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
247
+
248
+ expect(descriptionElement.text()).toBe(customDescription);
249
+ });
232
250
  });
233
251
 
234
252
  describe("Exposed properties", () => {
@@ -250,6 +250,14 @@ const {
250
250
  <slot name="label" :label="label" />
251
251
  </template>
252
252
 
253
+ <template #description>
254
+ <!--
255
+ @slot Use this to add custom content instead of the description.
256
+ @binding {string} description
257
+ -->
258
+ <slot name="description" :description="description" />
259
+ </template>
260
+
253
261
  <div ref="wrapper" v-bind="wrapperAttrs">
254
262
  <span
255
263
  v-if="hasSlotContent($slots['left'])"
@@ -430,6 +430,24 @@ describe("UTextarea.vue", () => {
430
430
  expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
431
431
  });
432
432
 
433
+ it("Description – renders custom content from description slot", () => {
434
+ const customDescription = "Custom description content";
435
+
436
+ const component = mount(UTextarea, {
437
+ props: {
438
+ description: "Default description",
439
+ },
440
+ slots: {
441
+ description: customDescription,
442
+ },
443
+ });
444
+
445
+ const labelComponent = component.getComponent(ULabel);
446
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
447
+
448
+ expect(descriptionElement.text()).toBe(customDescription);
449
+ });
450
+
433
451
  it("Left – renders custom content from left slot", () => {
434
452
  const slotText = "Custom Left Content";
435
453
  const testClass = "custom-left";
@@ -73,7 +73,7 @@ export interface Props {
73
73
  /**
74
74
  * Set error message.
75
75
  */
76
- error?: string;
76
+ error?: string | boolean;
77
77
 
78
78
  /**
79
79
  * Set number of visible rows.
@@ -81,6 +81,14 @@ const { getDataTest, filesLabelAttrs, itemsAttrs, itemAttrs } = useUI<Config>(de
81
81
  <slot name="label" :label="label" />
82
82
  </template>
83
83
 
84
+ <template #description>
85
+ <!--
86
+ @slot Use this to add custom content instead of the description.
87
+ @binding {string} description
88
+ -->
89
+ <slot name="description" :description="description" />
90
+ </template>
91
+
84
92
  <div ref="items" v-bind="itemsAttrs">
85
93
  <!-- @slot Use it to add UFile. -->
86
94
  <slot>
@@ -173,6 +173,25 @@ describe("UFiles.vue", () => {
173
173
  expect(component.find(`.${slotClass}`).text()).toBe(slotText);
174
174
  });
175
175
 
176
+ it("Description – renders custom content from description slot", () => {
177
+ const customDescription = "Custom description content";
178
+
179
+ const component = mount(UFiles, {
180
+ props: {
181
+ fileList: [],
182
+ description: "Default description",
183
+ },
184
+ slots: {
185
+ description: customDescription,
186
+ },
187
+ });
188
+
189
+ const labelComponent = component.getComponent(ULabel);
190
+ const descriptionElement = labelComponent.find("[vl-child-key='description']");
191
+
192
+ expect(descriptionElement.text()).toBe(customDescription);
193
+ });
194
+
176
195
  it("Before – renders content from before-file slot", () => {
177
196
  const slotText = "Before";
178
197
  const slotClass = "before-content";