vueless 1.4.7 → 1.4.8
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.
- package/icons/internal/apps.svg +1 -1
- package/icons/internal/calendar_month-fill.svg +1 -1
- package/icons/internal/error.svg +1 -1
- package/icons/internal/info.svg +1 -1
- package/icons/internal/progress_activity.svg +1 -1
- package/icons/internal/search.svg +1 -1
- package/icons/internal/visibility-fill.svg +1 -1
- package/icons/internal/warning.svg +1 -1
- package/icons/storybook/account_circle.svg +1 -1
- package/icons/storybook/cloud_sync.svg +1 -0
- package/icons/storybook/contact_mail.svg +1 -1
- package/icons/storybook/delivery_truck_speed.svg +1 -1
- package/icons/storybook/directions_bike.svg +1 -1
- package/icons/storybook/error.svg +1 -1
- package/icons/storybook/folder.svg +1 -0
- package/icons/storybook/handyman.svg +1 -1
- package/icons/storybook/help.svg +1 -1
- package/icons/storybook/inbox.svg +1 -1
- package/icons/storybook/inbox_customize.svg +1 -1
- package/icons/storybook/info.svg +1 -1
- package/icons/storybook/key.svg +1 -1
- package/icons/storybook/local_shipping.svg +1 -0
- package/icons/storybook/lock.svg +1 -1
- package/icons/storybook/lock_open.svg +1 -1
- package/icons/storybook/notifications.svg +1 -0
- package/icons/storybook/palette.svg +1 -1
- package/icons/storybook/person.svg +1 -1
- package/icons/storybook/person_search.svg +1 -1
- package/icons/storybook/progress_activity.svg +1 -1
- package/icons/storybook/rocket_launch.svg +1 -1
- package/icons/storybook/sentiment_satisfied.svg +1 -1
- package/icons/storybook/timer.svg +1 -1
- package/icons/storybook/travel_explore.svg +1 -1
- package/package.json +33 -32
- package/plugin-vite.js +18 -7
- package/ui.container-accordion/storybook/stories.ts +1 -1
- package/ui.container-accordion-item/storybook/stories.ts +1 -1
- package/ui.container-accordion-item/tests/UAccordionItem.test.ts +4 -2
- package/ui.container-card/storybook/stories.ts +40 -39
- package/ui.container-col/config.ts +4 -2
- package/ui.container-col/tests/UCol.test.ts +24 -16
- package/ui.container-drawer/tests/UDrawer.test.ts +11 -29
- package/ui.container-modal/storybook/stories.ts +2 -2
- package/ui.container-modal/tests/UModal.test.ts +10 -22
- package/ui.container-modal-confirm/storybook/stories.ts +17 -2
- package/ui.container-page/storybook/stories.ts +6 -1
- package/ui.container-row/config.ts +4 -2
- package/ui.container-row/tests/URow.test.ts +24 -16
- package/ui.data-table/UTableRow.vue +2 -2
- package/ui.data-table/storybook/stories.ts +1 -1
- package/ui.form-calendar/tests/UCalendar.test.ts +14 -13
- package/ui.form-calendar/tests/UCalendarDayView.test.ts +15 -16
- package/ui.form-checkbox/UCheckbox.vue +0 -5
- package/ui.form-checkbox/storybook/stories.ts +50 -18
- package/ui.form-checkbox/tests/UCheckbox.test.ts +0 -17
- package/ui.form-checkbox-group/storybook/stories.ts +61 -0
- package/ui.form-date-picker/storybook/stories.ts +87 -37
- package/ui.form-date-picker-range/storybook/stories.ts +91 -38
- package/ui.form-input/storybook/stories.ts +71 -37
- package/ui.form-input-file/storybook/stories.ts +38 -5
- package/ui.form-input-number/storybook/stories.ts +60 -28
- package/ui.form-input-password/storybook/stories.ts +88 -50
- package/ui.form-input-rating/UInputRating.vue +8 -3
- package/ui.form-input-search/storybook/stories.ts +34 -4
- package/ui.form-label/ULabel.vue +0 -6
- package/ui.form-label/storybook/stories.ts +40 -24
- package/ui.form-label/tests/ULabel.test.ts +0 -12
- package/ui.form-listbox/tests/UListbox.test.ts +39 -3
- package/ui.form-radio/URadio.vue +0 -5
- package/ui.form-radio/storybook/stories.ts +60 -25
- package/ui.form-radio/tests/URadio.test.ts +0 -17
- package/ui.form-radio-group/storybook/stories.ts +61 -0
- package/ui.form-select/config.ts +4 -1
- package/ui.form-select/storybook/stories.ts +109 -46
- package/ui.form-switch/storybook/stories.ts +35 -11
- package/ui.form-textarea/storybook/stories.ts +55 -20
- package/ui.loader/tests/ULoader.test.ts +3 -3
- package/ui.navigation-progress/tests/UProgress.test.ts +2 -3
- package/ui.text-block/tests/UText.test.ts +3 -3
- package/ui.text-files/storybook/stories.ts +30 -13
- package/ui.text-header/tests/UHeader.test.ts +3 -3
- package/ui.text-notify/tests/UNotify.test.ts +14 -18
- package/utils/node/helper.js +8 -9
|
@@ -14,6 +14,7 @@ import UBadge from "../../ui.text-badge/UBadge.vue";
|
|
|
14
14
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
15
15
|
import URow from "../../ui.container-row/URow.vue";
|
|
16
16
|
import UText from "../../ui.text-block/UText.vue";
|
|
17
|
+
import ULink from "../../ui.button-link/ULink.vue";
|
|
17
18
|
|
|
18
19
|
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
19
20
|
import type { Props } from "../types";
|
|
@@ -109,15 +110,17 @@ LabelSlot.args = {
|
|
|
109
110
|
};
|
|
110
111
|
|
|
111
112
|
export const Slots: StoryFn<UInputFileArgs> = (args) => ({
|
|
112
|
-
components: { UInputFile, UCol, UBadge, UIcon, URow, UText },
|
|
113
|
+
components: { UInputFile, UCol, UBadge, UIcon, URow, UText, ULink },
|
|
113
114
|
setup() {
|
|
114
|
-
|
|
115
|
+
const files = ref<File[]>([]);
|
|
116
|
+
|
|
117
|
+
return { args, files };
|
|
115
118
|
},
|
|
116
119
|
template: `
|
|
117
120
|
<UCol gap="xl">
|
|
118
121
|
<UInputFile
|
|
119
122
|
v-bind="args"
|
|
120
|
-
v-model="
|
|
123
|
+
v-model="files"
|
|
121
124
|
label="Top Slot"
|
|
122
125
|
:allowedFileTypes="['.jpeg', '.png']"
|
|
123
126
|
:maxFileSize="2"
|
|
@@ -132,7 +135,7 @@ export const Slots: StoryFn<UInputFileArgs> = (args) => ({
|
|
|
132
135
|
|
|
133
136
|
<UInputFile
|
|
134
137
|
v-bind="args"
|
|
135
|
-
v-model="
|
|
138
|
+
v-model="files"
|
|
136
139
|
label="Left Slot"
|
|
137
140
|
:allowedFileTypes="['.pdf', '.doc', '.docx']"
|
|
138
141
|
>
|
|
@@ -146,7 +149,7 @@ export const Slots: StoryFn<UInputFileArgs> = (args) => ({
|
|
|
146
149
|
|
|
147
150
|
<UInputFile
|
|
148
151
|
v-bind="args"
|
|
149
|
-
v-model="
|
|
152
|
+
v-model="files"
|
|
150
153
|
label="Bottom Slot"
|
|
151
154
|
multiple
|
|
152
155
|
:allowedFileTypes="['.png', '.jpeg']"
|
|
@@ -158,6 +161,36 @@ export const Slots: StoryFn<UInputFileArgs> = (args) => ({
|
|
|
158
161
|
</URow>
|
|
159
162
|
</template>
|
|
160
163
|
</UInputFile>
|
|
164
|
+
|
|
165
|
+
<UInputFile
|
|
166
|
+
v-bind="args"
|
|
167
|
+
v-model="files"
|
|
168
|
+
label="Upload document"
|
|
169
|
+
>
|
|
170
|
+
<template #description>
|
|
171
|
+
<UText size="sm" variant="lifted">
|
|
172
|
+
PDF or image, max 5MB, see
|
|
173
|
+
<ULink label="requirements" underlined size="sm" />.
|
|
174
|
+
</UText>
|
|
175
|
+
</template>
|
|
176
|
+
</UInputFile>
|
|
177
|
+
|
|
178
|
+
<UInputFile
|
|
179
|
+
v-bind="args"
|
|
180
|
+
v-model="files"
|
|
181
|
+
label="Upload document"
|
|
182
|
+
:error="true"
|
|
183
|
+
>
|
|
184
|
+
<template #error>
|
|
185
|
+
<UText size="sm" color="error">
|
|
186
|
+
<ul>
|
|
187
|
+
<li>File type is not accepted for this field</li>
|
|
188
|
+
<li>Maximum file size has been exceeded</li>
|
|
189
|
+
<li>Choose another file and upload again</li>
|
|
190
|
+
</ul>
|
|
191
|
+
</UText>
|
|
192
|
+
</template>
|
|
193
|
+
</UInputFile>
|
|
161
194
|
</UCol>
|
|
162
195
|
`,
|
|
163
196
|
});
|
|
@@ -14,6 +14,7 @@ import UButton from "../../ui.button/UButton.vue";
|
|
|
14
14
|
import URow from "../../ui.container-row/URow.vue";
|
|
15
15
|
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
16
16
|
import UText from "../../ui.text-block/UText.vue";
|
|
17
|
+
import ULink from "../../ui.button-link/ULink.vue";
|
|
17
18
|
|
|
18
19
|
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
19
20
|
import type { Props } from "../types";
|
|
@@ -200,7 +201,7 @@ export const IconProps: StoryFn<UInputNumberArgs> = (args) => ({
|
|
|
200
201
|
});
|
|
201
202
|
|
|
202
203
|
export const Slots: StoryFn<UInputNumberArgs> = (args) => ({
|
|
203
|
-
components: { UInputNumber, URow, UButton, UDropdownButton, UText },
|
|
204
|
+
components: { UInputNumber, URow, UCol, UButton, UDropdownButton, UText, ULink, UIcon },
|
|
204
205
|
setup() {
|
|
205
206
|
const currencies = [
|
|
206
207
|
{ label: "USD", value: "usd" },
|
|
@@ -209,42 +210,73 @@ export const Slots: StoryFn<UInputNumberArgs> = (args) => ({
|
|
|
209
210
|
];
|
|
210
211
|
|
|
211
212
|
const currency = ref("usd");
|
|
213
|
+
const descriptionSlotValue = ref(0);
|
|
214
|
+
const errorSlotValue = ref(null);
|
|
212
215
|
|
|
213
|
-
return { args, currency, currencies };
|
|
216
|
+
return { args, currency, currencies, descriptionSlotValue, errorSlotValue };
|
|
214
217
|
},
|
|
215
218
|
template: `
|
|
216
|
-
<
|
|
217
|
-
<
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
219
|
+
<UCol gap="3xl">
|
|
220
|
+
<URow block>
|
|
221
|
+
<UInputNumber
|
|
222
|
+
label="Left slot"
|
|
223
|
+
placeholder="Enter discount amount"
|
|
224
|
+
:config="{ numberInput: { wrapper: 'pl-0' } }"
|
|
225
|
+
>
|
|
226
|
+
<template #left>
|
|
227
|
+
<UDropdownButton
|
|
228
|
+
v-model="currency"
|
|
229
|
+
:options="currencies"
|
|
230
|
+
size="sm"
|
|
231
|
+
variant="soft"
|
|
232
|
+
class="rounded-r-none h-[49px]"
|
|
233
|
+
/>
|
|
234
|
+
</template>
|
|
235
|
+
</UInputNumber>
|
|
232
236
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
<UInputNumber
|
|
238
|
+
label="Right slot"
|
|
239
|
+
placeholder="Enter your annual payment"
|
|
240
|
+
>
|
|
241
|
+
<template #right>
|
|
242
|
+
<UText label="%, per year" variant="lifted" size="sm" :wrap="false" />
|
|
243
|
+
</template>
|
|
244
|
+
</UInputNumber>
|
|
245
|
+
</URow>
|
|
246
|
+
|
|
247
|
+
<URow block>
|
|
248
|
+
<UInputNumber v-model="descriptionSlotValue" label="Amount">
|
|
249
|
+
<template #description>
|
|
250
|
+
<UText size="sm" variant="lifted">
|
|
251
|
+
Net amount in
|
|
252
|
+
<ULink label="account currency" underlined size="sm" />.
|
|
253
|
+
</UText>
|
|
254
|
+
</template>
|
|
255
|
+
</UInputNumber>
|
|
256
|
+
|
|
257
|
+
<UInputNumber
|
|
258
|
+
v-model="errorSlotValue"
|
|
259
|
+
label="Amount"
|
|
260
|
+
:error="true"
|
|
261
|
+
>
|
|
262
|
+
<template #error>
|
|
263
|
+
<UText size="sm" color="error">
|
|
264
|
+
<ul>
|
|
265
|
+
<li>Value is outside the allowed range</li>
|
|
266
|
+
<li>Use up to two decimal places for this currency</li>
|
|
267
|
+
<li>Enter a number greater than zero</li>
|
|
268
|
+
</ul>
|
|
269
|
+
</UText>
|
|
270
|
+
</template>
|
|
271
|
+
</UInputNumber>
|
|
272
|
+
</URow>
|
|
273
|
+
</UCol>
|
|
242
274
|
`,
|
|
243
275
|
});
|
|
244
276
|
Slots.parameters = {
|
|
245
277
|
docs: {
|
|
246
278
|
story: {
|
|
247
|
-
height: "
|
|
279
|
+
height: "300px",
|
|
248
280
|
},
|
|
249
281
|
},
|
|
250
282
|
};
|
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
|
|
11
11
|
import UInputPassword from "../UInputPassword.vue";
|
|
12
12
|
import UCol from "../../ui.container-col/UCol.vue";
|
|
13
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
14
|
+
import ULink from "../../ui.button-link/ULink.vue";
|
|
15
|
+
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
13
16
|
import URow from "../../ui.container-row/URow.vue";
|
|
14
17
|
import UButton from "../../ui.button/UButton.vue";
|
|
15
18
|
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
@@ -139,69 +142,104 @@ export const IconProps: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
|
139
142
|
});
|
|
140
143
|
|
|
141
144
|
export const Slots: StoryFn<UInputPasswordArgs> = (args) => ({
|
|
142
|
-
components: { UInputPassword, URow, UButton, UDropdownButton },
|
|
145
|
+
components: { UInputPassword, URow, UCol, UButton, UDropdownButton, UText, ULink, UIcon },
|
|
143
146
|
setup() {
|
|
144
147
|
const wifiTypes = [
|
|
145
148
|
{ label: "WPA2", value: "wpa2" },
|
|
146
149
|
{ label: "WPA3", value: "wpa3" },
|
|
147
150
|
];
|
|
148
151
|
|
|
149
|
-
|
|
152
|
+
const descriptionSlotValue = ref("");
|
|
153
|
+
const errorSlotValue = ref("");
|
|
154
|
+
|
|
155
|
+
return { args, wifiTypes, descriptionSlotValue, errorSlotValue };
|
|
150
156
|
},
|
|
151
157
|
template: `
|
|
152
|
-
<
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
{
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
<
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
158
|
+
<UCol gap="3xl">
|
|
159
|
+
<URow block>
|
|
160
|
+
<UInputPassword
|
|
161
|
+
v-bind="args"
|
|
162
|
+
v-model="args.modelValue"
|
|
163
|
+
label="Enter your password"
|
|
164
|
+
:config="{ passwordInput: { wrapper: 'pl-0' } }"
|
|
165
|
+
>
|
|
166
|
+
<template #label="{ label }">
|
|
167
|
+
{{ label }}
|
|
168
|
+
<span class="text-red-500">*</span>
|
|
169
|
+
</template>
|
|
170
|
+
|
|
171
|
+
<template #left>
|
|
172
|
+
<UDropdownButton
|
|
173
|
+
v-model="args.wifiType"
|
|
174
|
+
:options="wifiTypes"
|
|
175
|
+
label="Wifi type"
|
|
176
|
+
size="sm"
|
|
177
|
+
variant="soft"
|
|
178
|
+
class="rounded-r-none h-[49px]"
|
|
179
|
+
/>
|
|
180
|
+
</template>
|
|
181
|
+
</UInputPassword>
|
|
182
|
+
|
|
183
|
+
<UInputPassword
|
|
184
|
+
v-bind="args"
|
|
185
|
+
v-model="args.modelValue"
|
|
186
|
+
label="Enter your password"
|
|
187
|
+
:config="{ passwordInput: { wrapper: 'pr-0' } }"
|
|
188
|
+
>
|
|
189
|
+
<template #label="{ label }">
|
|
190
|
+
{{ label }}
|
|
191
|
+
<span class="text-red-500">*</span>
|
|
192
|
+
</template>
|
|
193
|
+
|
|
194
|
+
<template #right="{ visible, toggle }">
|
|
195
|
+
<UButton
|
|
196
|
+
:label="visible ? 'Hide' : 'Show'"
|
|
197
|
+
color="neutral"
|
|
198
|
+
variant="ghost"
|
|
199
|
+
size="sm"
|
|
200
|
+
class="rounded-l-none h-[49px] min-w-[69px]"
|
|
201
|
+
@click="toggle"
|
|
202
|
+
/>
|
|
203
|
+
</template>
|
|
204
|
+
</UInputPassword>
|
|
205
|
+
</URow>
|
|
206
|
+
|
|
207
|
+
<URow block>
|
|
208
|
+
<UInputPassword
|
|
209
|
+
v-model="descriptionSlotValue"
|
|
210
|
+
label="Password"
|
|
211
|
+
>
|
|
212
|
+
<template #description>
|
|
213
|
+
<UText size="sm" variant="lifted">
|
|
214
|
+
Minimum 8 characters, see
|
|
215
|
+
<ULink label="password rules" underlined size="sm" />.
|
|
216
|
+
</UText>
|
|
217
|
+
</template>
|
|
218
|
+
</UInputPassword>
|
|
219
|
+
|
|
220
|
+
<UInputPassword
|
|
221
|
+
v-model="errorSlotValue"
|
|
222
|
+
label="Password"
|
|
223
|
+
:error="true"
|
|
224
|
+
>
|
|
225
|
+
<template #error>
|
|
226
|
+
<UText size="sm" color="error">
|
|
227
|
+
<ul>
|
|
228
|
+
<li>Password does not meet strength requirements</li>
|
|
229
|
+
<li>Use at least 8 characters with mixed case and numbers</li>
|
|
230
|
+
<li>Avoid common words and repeated characters</li>
|
|
231
|
+
</ul>
|
|
232
|
+
</UText>
|
|
233
|
+
</template>
|
|
234
|
+
</UInputPassword>
|
|
235
|
+
</URow>
|
|
236
|
+
</UCol>
|
|
199
237
|
`,
|
|
200
238
|
});
|
|
201
239
|
Slots.parameters = {
|
|
202
240
|
docs: {
|
|
203
241
|
story: {
|
|
204
|
-
height: "
|
|
242
|
+
height: "300px",
|
|
205
243
|
},
|
|
206
244
|
},
|
|
207
245
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, ref, useTemplateRef } from "vue";
|
|
2
|
+
import { computed, ref, useId, useTemplateRef } from "vue";
|
|
3
3
|
|
|
4
4
|
import { useUI } from "../composables/useUI";
|
|
5
5
|
import { hasSlotContent } from "../utils/helper";
|
|
@@ -27,6 +27,7 @@ const emit = defineEmits([
|
|
|
27
27
|
]);
|
|
28
28
|
|
|
29
29
|
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper");
|
|
30
|
+
const instanceId = useId();
|
|
30
31
|
|
|
31
32
|
const hovered = ref<number | null>(null);
|
|
32
33
|
const preventHover = ref(false);
|
|
@@ -66,6 +67,10 @@ function onMouseHover(overStar: number | null = null) {
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
function getStarInputId(index: number) {
|
|
71
|
+
return `${props.id ?? instanceId}-${index}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
defineExpose({
|
|
70
75
|
/**
|
|
71
76
|
* A reference to the component's wrapper element for direct DOM manipulation.
|
|
@@ -112,13 +117,13 @@ const {
|
|
|
112
117
|
v-for="(star, index) in stars"
|
|
113
118
|
:key="star"
|
|
114
119
|
tabindex="0"
|
|
115
|
-
:for="
|
|
120
|
+
:for="getStarInputId(index)"
|
|
116
121
|
v-bind="starLabelAttrs"
|
|
117
122
|
@keydown.enter="onClickStar(star)"
|
|
118
123
|
@keydown.space.prevent="onClickStar(star)"
|
|
119
124
|
>
|
|
120
125
|
<input
|
|
121
|
-
:id="
|
|
126
|
+
:id="getStarInputId(index)"
|
|
122
127
|
tabindex="-1"
|
|
123
128
|
type="radio"
|
|
124
129
|
:disabled="disabled"
|
|
@@ -14,6 +14,7 @@ import UCol from "../../ui.container-col/UCol.vue";
|
|
|
14
14
|
import URow from "../../ui.container-row/URow.vue";
|
|
15
15
|
import UDropdownButton from "../../ui.dropdown-button/UDropdownButton.vue";
|
|
16
16
|
import UText from "../../ui.text-block/UText.vue";
|
|
17
|
+
import ULink from "../../ui.button-link/ULink.vue";
|
|
17
18
|
|
|
18
19
|
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
19
20
|
import type { Props } from "../types";
|
|
@@ -152,7 +153,7 @@ export const IconProps: StoryFn<UInputSearchArgs> = (args) => ({
|
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
export const Slots: StoryFn<UInputSearchArgs> = (args) => ({
|
|
155
|
-
components: { UInputSearch, UCol, URow, UIcon, UDropdownButton, UText },
|
|
156
|
+
components: { UInputSearch, UCol, URow, UIcon, UDropdownButton, UText, ULink },
|
|
156
157
|
setup() {
|
|
157
158
|
const aiVersions = [
|
|
158
159
|
{ label: "GPT-4o", value: "gpt-4o" },
|
|
@@ -160,10 +161,13 @@ export const Slots: StoryFn<UInputSearchArgs> = (args) => ({
|
|
|
160
161
|
{ label: "GPT-4", value: "gpt-4" },
|
|
161
162
|
];
|
|
162
163
|
|
|
163
|
-
|
|
164
|
+
const descriptionSlotValue = ref("");
|
|
165
|
+
const errorSlotValue = ref("");
|
|
166
|
+
|
|
167
|
+
return { args, aiVersions, descriptionSlotValue, errorSlotValue };
|
|
164
168
|
},
|
|
165
169
|
template: `
|
|
166
|
-
<UCol>
|
|
170
|
+
<UCol gap="3xl">
|
|
167
171
|
<UInputSearch placeholder="Search by rental district...">
|
|
168
172
|
<template #right>
|
|
169
173
|
<URow align="center" gap="xs">
|
|
@@ -188,13 +192,39 @@ export const Slots: StoryFn<UInputSearchArgs> = (args) => ({
|
|
|
188
192
|
/>
|
|
189
193
|
</template>
|
|
190
194
|
</UInputSearch>
|
|
195
|
+
|
|
196
|
+
<UInputSearch v-model="descriptionSlotValue" label="Search products">
|
|
197
|
+
<template #description>
|
|
198
|
+
<UText size="sm" variant="lifted">
|
|
199
|
+
Tip: use
|
|
200
|
+
<ULink label="filters" underlined size="sm" />
|
|
201
|
+
for exact matches.
|
|
202
|
+
</UText>
|
|
203
|
+
</template>
|
|
204
|
+
</UInputSearch>
|
|
205
|
+
|
|
206
|
+
<UInputSearch
|
|
207
|
+
v-model="errorSlotValue"
|
|
208
|
+
label="Search"
|
|
209
|
+
:error="true"
|
|
210
|
+
>
|
|
211
|
+
<template #error>
|
|
212
|
+
<UText size="sm" color="error">
|
|
213
|
+
<ul>
|
|
214
|
+
<li>Search query is too short</li>
|
|
215
|
+
<li>Remove unsupported symbols from your search</li>
|
|
216
|
+
<li>Try different keywords and search again</li>
|
|
217
|
+
</ul>
|
|
218
|
+
</UText>
|
|
219
|
+
</template>
|
|
220
|
+
</UInputSearch>
|
|
191
221
|
</UCol>
|
|
192
222
|
`,
|
|
193
223
|
});
|
|
194
224
|
Slots.parameters = {
|
|
195
225
|
docs: {
|
|
196
226
|
story: {
|
|
197
|
-
height: "
|
|
227
|
+
height: "500px",
|
|
198
228
|
},
|
|
199
229
|
},
|
|
200
230
|
};
|
package/ui.form-label/ULabel.vue
CHANGED
|
@@ -170,9 +170,6 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
|
|
|
170
170
|
{{ description }}
|
|
171
171
|
</slot>
|
|
172
172
|
</div>
|
|
173
|
-
|
|
174
|
-
<!-- @slot Use it to add something below the label content. -->
|
|
175
|
-
<slot name="bottom" />
|
|
176
173
|
</div>
|
|
177
174
|
</div>
|
|
178
175
|
|
|
@@ -226,8 +223,5 @@ const { getDataTest, wrapperAttrs, contentAttrs, labelAttrs, descriptionAttrs, e
|
|
|
226
223
|
{{ description }}
|
|
227
224
|
</slot>
|
|
228
225
|
</div>
|
|
229
|
-
|
|
230
|
-
<!-- @slot Use it to add something below the label content. -->
|
|
231
|
-
<slot name="bottom" />
|
|
232
226
|
</div>
|
|
233
227
|
</template>
|
|
@@ -114,29 +114,45 @@ Sizes.args = { enum: "size" };
|
|
|
114
114
|
export const LabelAlign = EnumTemplate.bind({});
|
|
115
115
|
LabelAlign.args = { enum: "align" };
|
|
116
116
|
|
|
117
|
-
export const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
117
|
+
export const Slots: StoryFn<ULabelArgs> = () => ({
|
|
118
|
+
components: { ULabel, UText, URow, ULink, UIcon, UCol, UChip },
|
|
119
|
+
template: `
|
|
120
|
+
<UCol gap="3xl">
|
|
121
|
+
<ULabel label="Work email">
|
|
122
|
+
<UText label="johndoe@example.com" />
|
|
123
|
+
<template #label="{ label }">
|
|
124
|
+
<URow align="center" gap="xs">
|
|
125
|
+
<UIcon name="mail" size="sm" class="text-primary" />
|
|
126
|
+
<UChip icon="asterisk" color="error" size="xs">
|
|
127
|
+
<UText :label="label" class="mr-2" />
|
|
128
|
+
</UChip>
|
|
129
|
+
</URow>
|
|
130
|
+
</template>
|
|
131
|
+
</ULabel>
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
<ULabel label="Work email">
|
|
134
|
+
<UText label="johndoe@example.com" />
|
|
135
|
+
<template #description>
|
|
136
|
+
<UText size="sm" variant="lifted">
|
|
137
|
+
We will send a
|
|
138
|
+
<ULink label="confirmation link" underlined size="sm" />
|
|
139
|
+
to this address.
|
|
140
|
+
</UText>
|
|
141
|
+
</template>
|
|
142
|
+
</ULabel>
|
|
143
|
+
|
|
144
|
+
<ULabel label="Email" :error="true">
|
|
145
|
+
<UText label="not-an-email" />
|
|
146
|
+
<template #error>
|
|
147
|
+
<UText size="sm" color="error">
|
|
148
|
+
<ul>
|
|
149
|
+
<li>Email address format is invalid</li>
|
|
150
|
+
<li>Use a name@domain.com style address</li>
|
|
151
|
+
<li>Remove spaces and special characters from the local part</li>
|
|
152
|
+
</ul>
|
|
153
|
+
</UText>
|
|
154
|
+
</template>
|
|
155
|
+
</ULabel>
|
|
156
|
+
</UCol>
|
|
141
157
|
`,
|
|
142
|
-
};
|
|
158
|
+
});
|
|
@@ -369,18 +369,6 @@ describe("ULabel.vue", () => {
|
|
|
369
369
|
|
|
370
370
|
expect(component.find("[vl-key='label']").text()).toBe(slotContent);
|
|
371
371
|
});
|
|
372
|
-
|
|
373
|
-
it("Bottom – renders content from bottom slot", () => {
|
|
374
|
-
const testClass = "custom-bottom";
|
|
375
|
-
|
|
376
|
-
const component = mount(ULabel, {
|
|
377
|
-
slots: {
|
|
378
|
-
bottom: `<div class="${testClass}">Bottom Slot Content</div>`,
|
|
379
|
-
},
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
expect(component.find(`.${testClass}`).exists()).toBe(true);
|
|
383
|
-
});
|
|
384
372
|
});
|
|
385
373
|
|
|
386
374
|
describe("Events", () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { nextTick } from "vue";
|
|
1
2
|
import { flushPromises, mount } from "@vue/test-utils";
|
|
2
|
-
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { describe, it, expect, vi } from "vitest";
|
|
3
4
|
|
|
4
5
|
import UListbox from "../UListbox.vue";
|
|
5
6
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
@@ -399,7 +400,37 @@ describe("UListbox.vue", () => {
|
|
|
399
400
|
id: `option-${i}`,
|
|
400
401
|
}));
|
|
401
402
|
|
|
402
|
-
|
|
403
|
+
// Mock getComputedStyle to return non-zero heights for option elements
|
|
404
|
+
const originalGetComputedStyle = window.getComputedStyle;
|
|
405
|
+
|
|
406
|
+
vi.spyOn(window, "getComputedStyle").mockImplementation((el) => {
|
|
407
|
+
const real = originalGetComputedStyle(el);
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
...real,
|
|
411
|
+
height: "40px",
|
|
412
|
+
marginTop: "0px",
|
|
413
|
+
marginBottom: "4px",
|
|
414
|
+
paddingTop: "4px",
|
|
415
|
+
paddingBottom: "4px",
|
|
416
|
+
borderTopWidth: "1px",
|
|
417
|
+
borderBottomWidth: "1px",
|
|
418
|
+
gap: "4px",
|
|
419
|
+
} as CSSStyleDeclaration;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Mock getBoundingClientRect for option elements
|
|
423
|
+
vi.spyOn(HTMLElement.prototype, "getBoundingClientRect").mockReturnValue({
|
|
424
|
+
height: 40,
|
|
425
|
+
width: 100,
|
|
426
|
+
top: 0,
|
|
427
|
+
left: 0,
|
|
428
|
+
bottom: 40,
|
|
429
|
+
right: 100,
|
|
430
|
+
x: 0,
|
|
431
|
+
y: 0,
|
|
432
|
+
toJSON: () => {},
|
|
433
|
+
});
|
|
403
434
|
|
|
404
435
|
const component = mount(UListbox, {
|
|
405
436
|
props: {
|
|
@@ -409,10 +440,15 @@ describe("UListbox.vue", () => {
|
|
|
409
440
|
});
|
|
410
441
|
|
|
411
442
|
await flushPromises();
|
|
443
|
+
await nextTick();
|
|
412
444
|
|
|
413
445
|
const wrapper = component.find('[vl-key="wrapper"]');
|
|
446
|
+
const styleAttr = wrapper.attributes("style");
|
|
447
|
+
const inlineMaxHeight = (wrapper.element as HTMLElement).style.maxHeight;
|
|
448
|
+
|
|
449
|
+
expect(styleAttr ?? inlineMaxHeight).toContain("max-height:");
|
|
414
450
|
|
|
415
|
-
|
|
451
|
+
vi.restoreAllMocks();
|
|
416
452
|
});
|
|
417
453
|
});
|
|
418
454
|
|
package/ui.form-radio/URadio.vue
CHANGED
|
@@ -151,10 +151,5 @@ const { getDataTest, radioLabelAttrs, radioAttrs } = useUI<Config>(defaultConfig
|
|
|
151
151
|
:data-test="getDataTest()"
|
|
152
152
|
@change="onChange"
|
|
153
153
|
/>
|
|
154
|
-
|
|
155
|
-
<template #bottom>
|
|
156
|
-
<!-- @slot Use it to add something below the radio. -->
|
|
157
|
-
<slot name="bottom" />
|
|
158
|
-
</template>
|
|
159
154
|
</ULabel>
|
|
160
155
|
</template>
|