vueless 1.2.8 → 1.2.10-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.
- package/constants.d.ts +4 -0
- package/constants.js +4 -0
- package/icons/storybook/rocket_launch.svg +1 -0
- package/index.d.ts +3 -1
- package/index.ts +3 -1
- package/package.json +9 -5
- package/plugin-vite.js +6 -1
- package/types.ts +14 -2
- package/ui.button/config.ts +4 -4
- package/ui.button/tests/UButton.test.ts +3 -3
- package/ui.button-toggle/config.ts +2 -2
- package/ui.container-accordion/UAccordion.vue +0 -1
- package/ui.container-accordion/config.ts +1 -1
- package/ui.container-accordion/storybook/stories.ts +13 -1
- package/ui.container-accordion-item/UAccordionItem.vue +17 -4
- package/ui.container-accordion-item/config.ts +1 -1
- package/ui.container-accordion-item/storybook/stories.ts +26 -1
- package/ui.container-accordion-item/tests/UAccordionItem.test.ts +186 -0
- package/ui.container-card/config.ts +1 -1
- package/ui.data-table/config.ts +4 -4
- package/ui.dropdown-badge/UDropdownBadge.vue +68 -3
- package/ui.dropdown-badge/config.ts +5 -1
- package/ui.dropdown-badge/storybook/stories.ts +280 -4
- package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +194 -0
- package/ui.dropdown-badge/types.ts +30 -0
- package/ui.dropdown-button/UDropdownButton.vue +69 -6
- package/ui.dropdown-button/config.ts +5 -1
- package/ui.dropdown-button/storybook/stories.ts +288 -3
- package/ui.dropdown-button/tests/UDropdownButton.test.ts +190 -0
- package/ui.dropdown-button/types.ts +30 -0
- package/ui.dropdown-link/UDropdownLink.vue +69 -6
- package/ui.dropdown-link/config.ts +5 -1
- package/ui.dropdown-link/storybook/stories.ts +281 -4
- package/ui.dropdown-link/tests/UDropdownLink.test.ts +194 -0
- package/ui.dropdown-link/types.ts +30 -0
- package/ui.form-calendar/config.ts +4 -2
- package/ui.form-checkbox/config.ts +1 -1
- package/ui.form-checkbox/tests/UCheckbox.test.ts +2 -2
- package/ui.form-checkbox-group/tests/UCheckboxGroup.test.ts +2 -2
- package/ui.form-date-picker-range/config.ts +1 -1
- package/ui.form-input/UInput.vue +4 -2
- package/ui.form-input/config.ts +1 -1
- package/ui.form-input/tests/UInput.test.ts +2 -2
- package/ui.form-input-counter/UInputCounter.vue +25 -1
- package/ui.form-input-counter/config.ts +7 -2
- package/ui.form-input-counter/tests/UInputCounter.test.ts +85 -1
- package/ui.form-input-counter/types.ts +25 -0
- package/ui.form-input-file/tests/UInputFile.test.ts +2 -2
- package/ui.form-input-number/UInputNumber.vue +15 -3
- package/ui.form-input-number/utilFormat.ts +17 -7
- package/ui.form-input-password/UInputPassword.vue +23 -1
- package/ui.form-label/ULabel.vue +10 -4
- package/ui.form-label/tests/ULabel.test.ts +29 -12
- package/ui.form-listbox/UListbox.vue +21 -9
- package/ui.form-listbox/config.ts +1 -1
- package/ui.form-listbox/storybook/stories.ts +188 -1
- package/ui.form-listbox/tests/UListbox.test.ts +36 -0
- package/ui.form-listbox/types.ts +5 -0
- package/ui.form-radio/config.ts +1 -1
- package/ui.form-radio/tests/URadio.test.ts +2 -2
- package/ui.form-radio-group/tests/URadioGroup.test.ts +2 -2
- package/ui.form-select/USelect.vue +20 -2
- package/ui.form-select/config.ts +2 -1
- package/ui.form-select/storybook/stories.ts +31 -4
- package/ui.form-select/tests/USelect.test.ts +143 -0
- package/ui.form-select/types.ts +10 -0
- package/ui.form-textarea/config.ts +1 -1
- package/ui.form-textarea/tests/UTextarea.test.ts +2 -2
- package/ui.text-alert/config.ts +1 -1
- package/ui.text-badge/config.ts +1 -1
- package/utils/helper.ts +4 -0
- package/utils/node/dynamicProps.d.ts +5 -2
- package/utils/node/dynamicProps.js +126 -53
- package/utils/node/helper.d.ts +10 -7
- package/utils/node/helper.js +59 -2
- package/utils/node/tailwindSafelist.js +9 -2
- package/utils/theme.ts +75 -31
- package/utils/ui.ts +32 -3
|
@@ -8,14 +8,22 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import UDropdownLink from "../../ui.dropdown-link/UDropdownLink.vue";
|
|
10
10
|
import URow from "../../ui.container-row/URow.vue";
|
|
11
|
+
import UCol from "../../ui.container-col/UCol.vue";
|
|
11
12
|
import UIcon from "../../ui.image-icon/UIcon.vue";
|
|
12
13
|
import UBadge from "../../ui.text-badge/UBadge.vue";
|
|
13
14
|
import ULink from "../../ui.button-link/ULink.vue";
|
|
14
15
|
import UAvatar from "../../ui.image-avatar/UAvatar.vue";
|
|
16
|
+
import UText from "../../ui.text-block/UText.vue";
|
|
17
|
+
import ULoader from "../../ui.loader/ULoader.vue";
|
|
15
18
|
|
|
16
19
|
import type { Meta, StoryFn } from "@storybook/vue3-vite";
|
|
17
20
|
import type { Props } from "../types";
|
|
18
21
|
|
|
22
|
+
import johnDoe from "../../ui.form-select/storybook/assets/images/john-doe.png";
|
|
23
|
+
import emilyDavis from "../../ui.form-select/storybook/assets/images/emily-davis.png";
|
|
24
|
+
import alexJohnson from "../../ui.form-select/storybook/assets/images/alex-johnson.png";
|
|
25
|
+
import patMorgan from "../../ui.form-select/storybook/assets/images/pat-morgan.png";
|
|
26
|
+
|
|
19
27
|
interface DefaultUDropdownLinkArgs extends Props {
|
|
20
28
|
slotTemplate?: string;
|
|
21
29
|
}
|
|
@@ -51,7 +59,7 @@ export default {
|
|
|
51
59
|
} as Meta;
|
|
52
60
|
|
|
53
61
|
const DefaultTemplate: StoryFn<DefaultUDropdownLinkArgs> = (args: DefaultUDropdownLinkArgs) => ({
|
|
54
|
-
components: { UDropdownLink, UIcon, ULink, UBadge, UAvatar },
|
|
62
|
+
components: { UDropdownLink, UIcon, ULink, UBadge, UAvatar, ULoader, URow, UText },
|
|
55
63
|
setup: () => ({ args, slots: getSlotNames(UDropdownLink.__name) }),
|
|
56
64
|
template: `
|
|
57
65
|
<UDropdownLink v-bind="args">
|
|
@@ -87,6 +95,35 @@ const EnumTemplate: StoryFn<EnumUDropdownLinkArgs> = (
|
|
|
87
95
|
`,
|
|
88
96
|
});
|
|
89
97
|
|
|
98
|
+
const GroupValuesTemplate: StoryFn<DefaultUDropdownLinkArgs> = (
|
|
99
|
+
args: DefaultUDropdownLinkArgs,
|
|
100
|
+
) => ({
|
|
101
|
+
components: { UDropdownLink },
|
|
102
|
+
setup() {
|
|
103
|
+
return {
|
|
104
|
+
args,
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
template: `
|
|
108
|
+
<UDropdownLink
|
|
109
|
+
v-bind="args"
|
|
110
|
+
v-model="args.modelValue"
|
|
111
|
+
label="Single"
|
|
112
|
+
:config="{ listbox: 'min-w-[200px]' }"
|
|
113
|
+
class="max-w-96 mr-20"
|
|
114
|
+
/>
|
|
115
|
+
|
|
116
|
+
<UDropdownLink
|
|
117
|
+
v-bind="args"
|
|
118
|
+
v-model="args.modelValueMultiple"
|
|
119
|
+
label="Multiple"
|
|
120
|
+
multiple
|
|
121
|
+
:config="{ listbox: 'min-w-[200px]' }"
|
|
122
|
+
class="mt-5 max-w-96"
|
|
123
|
+
/>
|
|
124
|
+
`,
|
|
125
|
+
});
|
|
126
|
+
|
|
90
127
|
export const Default = DefaultTemplate.bind({});
|
|
91
128
|
Default.args = {};
|
|
92
129
|
Default.parameters = {
|
|
@@ -97,6 +134,9 @@ Default.parameters = {
|
|
|
97
134
|
},
|
|
98
135
|
};
|
|
99
136
|
|
|
137
|
+
export const Disabled = DefaultTemplate.bind({});
|
|
138
|
+
Disabled.args = { disabled: true };
|
|
139
|
+
|
|
100
140
|
export const Searchable = DefaultTemplate.bind({});
|
|
101
141
|
Searchable.args = { searchable: true };
|
|
102
142
|
Searchable.parameters = {
|
|
@@ -107,6 +147,19 @@ Searchable.parameters = {
|
|
|
107
147
|
},
|
|
108
148
|
};
|
|
109
149
|
|
|
150
|
+
export const SearchModelValue = DefaultTemplate.bind({});
|
|
151
|
+
SearchModelValue.args = { searchable: true, search: "Settings" };
|
|
152
|
+
SearchModelValue.parameters = {
|
|
153
|
+
docs: {
|
|
154
|
+
story: {
|
|
155
|
+
height: "250px",
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const NoCloseOnSelect = SelectableTemplate.bind({});
|
|
161
|
+
NoCloseOnSelect.args = { modelValue: "logout", closeOnSelect: false };
|
|
162
|
+
|
|
110
163
|
export const OptionSelection = SelectableTemplate.bind({});
|
|
111
164
|
OptionSelection.args = { modelValue: "profile" };
|
|
112
165
|
|
|
@@ -132,6 +185,60 @@ ListboxYPosition.parameters = {
|
|
|
132
185
|
storyClasses: "h-[350px] flex items-center px-6 pt-8 pb-12",
|
|
133
186
|
};
|
|
134
187
|
|
|
188
|
+
export const GroupValue = GroupValuesTemplate.bind({});
|
|
189
|
+
GroupValue.args = {
|
|
190
|
+
modelValue: "",
|
|
191
|
+
groupValueKey: "libs",
|
|
192
|
+
groupLabelKey: "language",
|
|
193
|
+
labelKey: "name",
|
|
194
|
+
valueKey: "name",
|
|
195
|
+
options: [
|
|
196
|
+
{
|
|
197
|
+
language: "Javascript",
|
|
198
|
+
libs: [{ name: "Vue.js" }, { name: "Adonis" }],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
language: "Ruby",
|
|
202
|
+
libs: [
|
|
203
|
+
{ name: "Frameworks", isSubGroup: true, level: 2 },
|
|
204
|
+
{ name: "Rails", level: 3 },
|
|
205
|
+
{ name: "Sinatra", level: 3 },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
language: "Other",
|
|
210
|
+
libs: [{ name: "Laravel" }, { name: "Phoenix" }],
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
GroupValue.parameters = {
|
|
215
|
+
docs: {
|
|
216
|
+
story: {
|
|
217
|
+
height: "400px",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export const OptionsLimit = DefaultTemplate.bind({});
|
|
223
|
+
OptionsLimit.args = { optionsLimit: 2 };
|
|
224
|
+
OptionsLimit.parameters = {
|
|
225
|
+
docs: {
|
|
226
|
+
description: {
|
|
227
|
+
story: "`optionsLimit` prop controls the number of options displayed in the dropdown.",
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export const VisibleOptions = DefaultTemplate.bind({});
|
|
233
|
+
VisibleOptions.args = { visibleOptions: 2 };
|
|
234
|
+
VisibleOptions.parameters = {
|
|
235
|
+
docs: {
|
|
236
|
+
description: {
|
|
237
|
+
story: "`visibleOptions` prop controls the number of options you can see without a scroll.",
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
135
242
|
export const Color = EnumTemplate.bind({});
|
|
136
243
|
Color.args = { enum: "color", label: "{enumValue}" };
|
|
137
244
|
|
|
@@ -149,9 +256,6 @@ export const UnderlineVariants: StoryFn<EnumUDropdownLinkArgs> = (args: EnumUDro
|
|
|
149
256
|
`,
|
|
150
257
|
});
|
|
151
258
|
|
|
152
|
-
export const Disabled = DefaultTemplate.bind({});
|
|
153
|
-
Disabled.args = { disabled: true };
|
|
154
|
-
|
|
155
259
|
export const WithoutToggleIcon = Default.bind({});
|
|
156
260
|
WithoutToggleIcon.args = { toggleIcon: false };
|
|
157
261
|
|
|
@@ -202,3 +306,176 @@ ToggleSlot.args = {
|
|
|
202
306
|
</template>
|
|
203
307
|
`,
|
|
204
308
|
};
|
|
309
|
+
|
|
310
|
+
export const EmptySlot = DefaultTemplate.bind({});
|
|
311
|
+
EmptySlot.args = {
|
|
312
|
+
options: [],
|
|
313
|
+
slotTemplate: `
|
|
314
|
+
<template #empty>
|
|
315
|
+
<URow align="center">
|
|
316
|
+
<ULoader loading size="sm" />
|
|
317
|
+
<UText label="Loading, this may take a while..." />
|
|
318
|
+
</URow>
|
|
319
|
+
</template>
|
|
320
|
+
`,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export const OptionSlots: StoryFn<DefaultUDropdownLinkArgs> = (args) => ({
|
|
324
|
+
components: { UDropdownLink, URow, UCol, UAvatar, UIcon, UBadge, UText },
|
|
325
|
+
setup: () => ({ args, johnDoe, emilyDavis, alexJohnson, patMorgan }),
|
|
326
|
+
template: `
|
|
327
|
+
<URow>
|
|
328
|
+
<UDropdownLink
|
|
329
|
+
v-model="args.beforeOptionModel"
|
|
330
|
+
label="Before option slot"
|
|
331
|
+
:options="[
|
|
332
|
+
{
|
|
333
|
+
label: 'John Doe',
|
|
334
|
+
id: '1',
|
|
335
|
+
role: 'Developer',
|
|
336
|
+
avatar: johnDoe,
|
|
337
|
+
status: 'online',
|
|
338
|
+
statusColor: 'success',
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
label: 'Jane Smith',
|
|
342
|
+
id: '2',
|
|
343
|
+
role: 'Designer',
|
|
344
|
+
avatar: emilyDavis,
|
|
345
|
+
status: 'away',
|
|
346
|
+
statusColor: 'warning',
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
label: 'Mike Johnson',
|
|
350
|
+
id: '3',
|
|
351
|
+
role: 'Product Manager',
|
|
352
|
+
avatar: alexJohnson,
|
|
353
|
+
status: 'offline',
|
|
354
|
+
statusColor: 'grayscale',
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
label: 'Sarah Wilson',
|
|
358
|
+
id: '4',
|
|
359
|
+
role: 'QA Engineer',
|
|
360
|
+
avatar: patMorgan,
|
|
361
|
+
status: 'online',
|
|
362
|
+
statusColor: 'success',
|
|
363
|
+
},
|
|
364
|
+
]"
|
|
365
|
+
>
|
|
366
|
+
<template #before-option="{ option }">
|
|
367
|
+
<UAvatar :src="option.avatar" size="sm" />
|
|
368
|
+
</template>
|
|
369
|
+
</UDropdownLink>
|
|
370
|
+
|
|
371
|
+
<UDropdownLink
|
|
372
|
+
v-model="args.optionModel"
|
|
373
|
+
label="Option slot"
|
|
374
|
+
:options="[
|
|
375
|
+
{
|
|
376
|
+
label: 'John Doe',
|
|
377
|
+
id: '1',
|
|
378
|
+
role: 'Developer',
|
|
379
|
+
avatar: johnDoe,
|
|
380
|
+
status: 'online',
|
|
381
|
+
statusColor: 'success',
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
label: 'Jane Smith',
|
|
385
|
+
id: '2',
|
|
386
|
+
role: 'Designer',
|
|
387
|
+
avatar: emilyDavis,
|
|
388
|
+
status: 'away',
|
|
389
|
+
statusColor: 'warning',
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
label: 'Mike Johnson',
|
|
393
|
+
id: '3',
|
|
394
|
+
role: 'Product Manager',
|
|
395
|
+
avatar: alexJohnson,
|
|
396
|
+
status: 'offline',
|
|
397
|
+
statusColor: 'grayscale',
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
label: 'Sarah Wilson',
|
|
401
|
+
id: '4',
|
|
402
|
+
role: 'QA Engineer',
|
|
403
|
+
avatar: patMorgan,
|
|
404
|
+
status: 'online',
|
|
405
|
+
statusColor: 'success',
|
|
406
|
+
},
|
|
407
|
+
]"
|
|
408
|
+
>
|
|
409
|
+
<template #option="{ option }">
|
|
410
|
+
<URow align="center" gap="xs">
|
|
411
|
+
<UCol gap="none">
|
|
412
|
+
<UText size="sm">{{ option.label }}</UText>
|
|
413
|
+
<UText variant="lifted" size="xs">{{ option.role }}</UText>
|
|
414
|
+
</UCol>
|
|
415
|
+
<UBadge
|
|
416
|
+
:label="option.status"
|
|
417
|
+
:color="option.statusColor"
|
|
418
|
+
size="sm"
|
|
419
|
+
variant="subtle"
|
|
420
|
+
/>
|
|
421
|
+
</URow>
|
|
422
|
+
</template>
|
|
423
|
+
</UDropdownLink>
|
|
424
|
+
|
|
425
|
+
<UDropdownLink
|
|
426
|
+
v-model="args.afterOptionModel"
|
|
427
|
+
label="After option slot"
|
|
428
|
+
:options="[
|
|
429
|
+
{
|
|
430
|
+
label: 'John Doe',
|
|
431
|
+
id: '1',
|
|
432
|
+
role: 'Developer',
|
|
433
|
+
avatar: johnDoe,
|
|
434
|
+
status: 'online',
|
|
435
|
+
statusColor: 'success',
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
label: 'Jane Smith',
|
|
439
|
+
id: '2',
|
|
440
|
+
role: 'Designer',
|
|
441
|
+
avatar: emilyDavis,
|
|
442
|
+
status: 'away',
|
|
443
|
+
statusColor: 'warning',
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
label: 'Mike Johnson',
|
|
447
|
+
id: '3',
|
|
448
|
+
role: 'Product Manager',
|
|
449
|
+
avatar: alexJohnson,
|
|
450
|
+
status: 'offline',
|
|
451
|
+
statusColor: 'grayscale',
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
label: 'Sarah Wilson',
|
|
455
|
+
id: '4',
|
|
456
|
+
role: 'QA Engineer',
|
|
457
|
+
avatar: patMorgan,
|
|
458
|
+
status: 'online',
|
|
459
|
+
statusColor: 'success',
|
|
460
|
+
},
|
|
461
|
+
]"
|
|
462
|
+
>
|
|
463
|
+
<template #after-option="{ option }">
|
|
464
|
+
<UBadge
|
|
465
|
+
:label="option.status"
|
|
466
|
+
:color="option.statusColor"
|
|
467
|
+
size="sm"
|
|
468
|
+
variant="subtle"
|
|
469
|
+
/>
|
|
470
|
+
</template>
|
|
471
|
+
</UDropdownLink>
|
|
472
|
+
</URow>
|
|
473
|
+
`,
|
|
474
|
+
});
|
|
475
|
+
OptionSlots.parameters = {
|
|
476
|
+
docs: {
|
|
477
|
+
story: {
|
|
478
|
+
height: "300px",
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
};
|
|
@@ -268,6 +268,101 @@ describe("UDropdownLink.vue", () => {
|
|
|
268
268
|
|
|
269
269
|
expect(component.findComponent(ULink).attributes("data-test")).toBe(dataTest);
|
|
270
270
|
});
|
|
271
|
+
|
|
272
|
+
// OptionsLimit prop
|
|
273
|
+
it("passes optionsLimit prop to UListbox component", async () => {
|
|
274
|
+
const optionsLimit = 2;
|
|
275
|
+
|
|
276
|
+
const component = mount(UDropdownLink, {
|
|
277
|
+
props: {
|
|
278
|
+
optionsLimit,
|
|
279
|
+
options: defaultOptions,
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
await component.findComponent(ULink).trigger("click");
|
|
284
|
+
|
|
285
|
+
expect(component.findComponent(UListbox).props("optionsLimit")).toBe(optionsLimit);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// VisibleOptions prop
|
|
289
|
+
it("passes visibleOptions prop to UListbox component", async () => {
|
|
290
|
+
const visibleOptions = 5;
|
|
291
|
+
|
|
292
|
+
const component = mount(UDropdownLink, {
|
|
293
|
+
props: {
|
|
294
|
+
visibleOptions,
|
|
295
|
+
options: defaultOptions,
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
await component.findComponent(ULink).trigger("click");
|
|
300
|
+
|
|
301
|
+
expect(component.findComponent(UListbox).props("visibleOptions")).toBe(visibleOptions);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// GroupLabelKey prop
|
|
305
|
+
it("passes groupLabelKey prop to UListbox component", async () => {
|
|
306
|
+
const groupLabelKey = "category";
|
|
307
|
+
const groupedOptions = [
|
|
308
|
+
{ groupLabel: "Group 1", category: "group1" },
|
|
309
|
+
{ label: "Option 1", id: "option1", category: "group1" },
|
|
310
|
+
{ groupLabel: "Group 2", category: "group2" },
|
|
311
|
+
{ label: "Option 2", id: "option2", category: "group2" },
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
const component = mount(UDropdownLink, {
|
|
315
|
+
props: {
|
|
316
|
+
groupLabelKey,
|
|
317
|
+
options: groupedOptions,
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
await component.findComponent(ULink).trigger("click");
|
|
322
|
+
|
|
323
|
+
expect(component.findComponent(UListbox).props("groupLabelKey")).toBe(groupLabelKey);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("Search v-model – passes search to UListbox and filters", async () => {
|
|
327
|
+
const component = mount(UDropdownLink, {
|
|
328
|
+
props: {
|
|
329
|
+
searchable: true,
|
|
330
|
+
options: defaultOptions,
|
|
331
|
+
search: "Option 3",
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
await component.findComponent(ULink).trigger("click");
|
|
336
|
+
|
|
337
|
+
const listbox = component.getComponent(UListbox);
|
|
338
|
+
const options = listbox.findAll("[vl-child-key='option']");
|
|
339
|
+
|
|
340
|
+
expect(options).toHaveLength(1);
|
|
341
|
+
expect(options[0].text()).toBe("Option 3");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// CloseOnSelect prop
|
|
345
|
+
it("keeps dropdown open when closeOnSelect is false", async () => {
|
|
346
|
+
const component = mount(UDropdownLink, {
|
|
347
|
+
props: {
|
|
348
|
+
options: defaultOptions,
|
|
349
|
+
closeOnSelect: false,
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Open the dropdown
|
|
354
|
+
await component.findComponent(ULink).trigger("click");
|
|
355
|
+
expect(component.findComponent(UListbox).exists()).toBe(true);
|
|
356
|
+
|
|
357
|
+
// Find the listbox component
|
|
358
|
+
const listbox = component.findComponent(UListbox);
|
|
359
|
+
|
|
360
|
+
// Simulate selecting an option by emitting update:modelValue from the listbox
|
|
361
|
+
listbox.vm.$emit("update:modelValue", 2);
|
|
362
|
+
|
|
363
|
+
// Dropdown should remain open
|
|
364
|
+
expect(component.findComponent(UListbox).exists()).toBe(true);
|
|
365
|
+
});
|
|
271
366
|
});
|
|
272
367
|
|
|
273
368
|
// Slots tests
|
|
@@ -329,6 +424,105 @@ describe("UDropdownLink.vue", () => {
|
|
|
329
424
|
expect(component.find(`.${slotClass}`).exists()).toBe(true);
|
|
330
425
|
expect(component.find(`.${slotClass}`).text()).toBe(slotText);
|
|
331
426
|
});
|
|
427
|
+
|
|
428
|
+
// Before-option slot
|
|
429
|
+
it("renders content from before-option slot", async () => {
|
|
430
|
+
const label = "Dropdown Link";
|
|
431
|
+
const slotText = "Before";
|
|
432
|
+
const slotClass = "before-option-content";
|
|
433
|
+
|
|
434
|
+
const component = mount(UDropdownLink, {
|
|
435
|
+
props: {
|
|
436
|
+
label,
|
|
437
|
+
options: defaultOptions,
|
|
438
|
+
},
|
|
439
|
+
slots: {
|
|
440
|
+
"before-option": `<span class='${slotClass}'>${slotText}</span>`,
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
await component.findComponent(ULink).trigger("click");
|
|
445
|
+
|
|
446
|
+
const listbox = component.findComponent(UListbox);
|
|
447
|
+
const beforeOptionSlot = listbox.find(`.${slotClass}`);
|
|
448
|
+
|
|
449
|
+
expect(beforeOptionSlot.exists()).toBe(true);
|
|
450
|
+
expect(beforeOptionSlot.text()).toBe(slotText);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Option slot
|
|
454
|
+
it("renders custom content from option slot", async () => {
|
|
455
|
+
const label = "Dropdown Link";
|
|
456
|
+
const slotClass = "custom-option-content";
|
|
457
|
+
|
|
458
|
+
const component = mount(UDropdownLink, {
|
|
459
|
+
props: {
|
|
460
|
+
label,
|
|
461
|
+
options: defaultOptions,
|
|
462
|
+
},
|
|
463
|
+
slots: {
|
|
464
|
+
option: `<span class='${slotClass}'>Custom {{ params.option.label }}</span>`,
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
await component.findComponent(ULink).trigger("click");
|
|
469
|
+
|
|
470
|
+
const listbox = component.findComponent(UListbox);
|
|
471
|
+
const customOptionSlot = listbox.find(`.${slotClass}`);
|
|
472
|
+
|
|
473
|
+
expect(customOptionSlot.exists()).toBe(true);
|
|
474
|
+
expect(customOptionSlot.text()).toBe("Custom Option 1");
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// After-option slot
|
|
478
|
+
it("renders content from after-option slot", async () => {
|
|
479
|
+
const label = "Dropdown Link";
|
|
480
|
+
const slotText = "After";
|
|
481
|
+
const slotClass = "after-option-content";
|
|
482
|
+
|
|
483
|
+
const component = mount(UDropdownLink, {
|
|
484
|
+
props: {
|
|
485
|
+
label,
|
|
486
|
+
options: defaultOptions,
|
|
487
|
+
},
|
|
488
|
+
slots: {
|
|
489
|
+
"after-option": `<span class='${slotClass}'>${slotText}</span>`,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
await component.findComponent(ULink).trigger("click");
|
|
494
|
+
|
|
495
|
+
const listbox = component.findComponent(UListbox);
|
|
496
|
+
const afterOptionSlot = listbox.find(`.${slotClass}`);
|
|
497
|
+
|
|
498
|
+
expect(afterOptionSlot.exists()).toBe(true);
|
|
499
|
+
expect(afterOptionSlot.text()).toBe(slotText);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
// Empty slot
|
|
503
|
+
it("renders custom content from empty slot", async () => {
|
|
504
|
+
const label = "Dropdown Link";
|
|
505
|
+
const slotContent = "No options available";
|
|
506
|
+
const slotClass = "custom-empty";
|
|
507
|
+
|
|
508
|
+
const component = mount(UDropdownLink, {
|
|
509
|
+
props: {
|
|
510
|
+
label,
|
|
511
|
+
options: [],
|
|
512
|
+
},
|
|
513
|
+
slots: {
|
|
514
|
+
empty: `<span class='${slotClass}'>${slotContent}</span>`,
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
await component.findComponent(ULink).trigger("click");
|
|
519
|
+
|
|
520
|
+
const listbox = component.findComponent(UListbox);
|
|
521
|
+
const emptySlot = listbox.find(`.${slotClass}`);
|
|
522
|
+
|
|
523
|
+
expect(emptySlot.exists()).toBe(true);
|
|
524
|
+
expect(emptySlot.text()).toBe(slotContent);
|
|
525
|
+
});
|
|
332
526
|
});
|
|
333
527
|
|
|
334
528
|
// Events tests
|
|
@@ -36,6 +36,26 @@ export interface Props {
|
|
|
36
36
|
*/
|
|
37
37
|
valueKey?: string;
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Set a name of the property containing the group label.
|
|
41
|
+
*/
|
|
42
|
+
groupLabelKey?: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Set a name of the property containing the group values.
|
|
46
|
+
*/
|
|
47
|
+
groupValueKey?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Number of options displayed in the dropdown.
|
|
51
|
+
*/
|
|
52
|
+
optionsLimit?: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Number of options you can see without a scroll.
|
|
56
|
+
*/
|
|
57
|
+
visibleOptions?: number;
|
|
58
|
+
|
|
39
59
|
/**
|
|
40
60
|
* Link color.
|
|
41
61
|
*/
|
|
@@ -65,6 +85,16 @@ export interface Props {
|
|
|
65
85
|
*/
|
|
66
86
|
searchable?: boolean;
|
|
67
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Search input model value for the dropdown list.
|
|
90
|
+
*/
|
|
91
|
+
search?: string;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Close dropdown on option select.
|
|
95
|
+
*/
|
|
96
|
+
closeOnSelect?: boolean;
|
|
97
|
+
|
|
68
98
|
/**
|
|
69
99
|
* Allows multiple selection.
|
|
70
100
|
*/
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export default /*tw*/ {
|
|
2
|
-
wrapper:
|
|
2
|
+
wrapper: `
|
|
3
|
+
p-3 w-[19rem] bg-default shadow-sm overflow-hidden focus:outline-hidden
|
|
4
|
+
border border-solid border-default rounded-medium`,
|
|
3
5
|
navigation: "mb-2 pb-2 border-b border-muted flex items-center justify-between",
|
|
4
6
|
viewSwitchButton: {
|
|
5
7
|
base: "{UButton}",
|
|
@@ -54,7 +56,7 @@ export default /*tw*/ {
|
|
|
54
56
|
timepicker: "mt-2 pl-1 pt-3 text-medium flex items-stretch justify-between gap-2 border-t border-muted",
|
|
55
57
|
timepickerLabel: "w-full self-center",
|
|
56
58
|
timepickerInputWrapper: `
|
|
57
|
-
flex items-center rounded-medium border border-default
|
|
59
|
+
flex items-center rounded-medium border border-solid border-default
|
|
58
60
|
hover:focus-within:border-primary focus-within:border-primary
|
|
59
61
|
focus-within:outline focus-within:outline-small focus-within:outline-primary
|
|
60
62
|
`,
|
|
@@ -3,7 +3,7 @@ export default /*tw*/ {
|
|
|
3
3
|
checkbox: {
|
|
4
4
|
base: `
|
|
5
5
|
bg-default cursor-pointer transition
|
|
6
|
-
border border-default rounded-small outline-transparent
|
|
6
|
+
border border-solid border-default rounded-small outline-transparent
|
|
7
7
|
appearance-none p-0 print:color-adjust-exact inline-block align-middle bg-origin-border select-none shrink-0
|
|
8
8
|
hover:border-lifted
|
|
9
9
|
active:border-{color} active:bg-{color}/15
|
|
@@ -261,7 +261,7 @@ describe("UCheckbox.vue", () => {
|
|
|
261
261
|
});
|
|
262
262
|
|
|
263
263
|
const labelComponent = component.getComponent(ULabel);
|
|
264
|
-
const labelElement = labelComponent.find("label");
|
|
264
|
+
const labelElement = labelComponent.find("[vl-child-key='label']");
|
|
265
265
|
|
|
266
266
|
expect(labelElement.text()).toBe(customLabelContent);
|
|
267
267
|
});
|
|
@@ -279,7 +279,7 @@ describe("UCheckbox.vue", () => {
|
|
|
279
279
|
});
|
|
280
280
|
|
|
281
281
|
const labelComponent = component.getComponent(ULabel);
|
|
282
|
-
const labelElement = labelComponent.find("label");
|
|
282
|
+
const labelElement = labelComponent.find("[vl-child-key='label']");
|
|
283
283
|
|
|
284
284
|
expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
|
|
285
285
|
});
|
|
@@ -201,7 +201,7 @@ describe("UCheckboxGroup.vue", () => {
|
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
const labelComponent = component.getComponent(ULabel);
|
|
204
|
-
const labelElement = labelComponent.find("label");
|
|
204
|
+
const labelElement = labelComponent.find("[vl-child-key='label']");
|
|
205
205
|
|
|
206
206
|
expect(labelElement.text()).toBe(customLabelContent);
|
|
207
207
|
});
|
|
@@ -219,7 +219,7 @@ describe("UCheckboxGroup.vue", () => {
|
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
const labelComponent = component.getComponent(ULabel);
|
|
222
|
-
const labelElement = labelComponent.find("label");
|
|
222
|
+
const labelElement = labelComponent.find("[vl-child-key='label']");
|
|
223
223
|
|
|
224
224
|
expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
|
|
225
225
|
});
|
|
@@ -31,7 +31,7 @@ export default /*tw*/ {
|
|
|
31
31
|
menu: {
|
|
32
32
|
base: `
|
|
33
33
|
absolute z-40 mb-3 w-80 overflow-hidden rounded-medium
|
|
34
|
-
border border-default bg-default p-2 shadow-sm focus:outline-hidden
|
|
34
|
+
border border-solid border-default bg-default p-2 shadow-sm focus:outline-hidden
|
|
35
35
|
`,
|
|
36
36
|
variants: {
|
|
37
37
|
openDirectionX: {
|
package/ui.form-input/UInput.vue
CHANGED
|
@@ -25,7 +25,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
25
25
|
|
|
26
26
|
const emit = defineEmits([
|
|
27
27
|
/**
|
|
28
|
-
* Triggers when the input value is
|
|
28
|
+
* Triggers when the input value is changed.
|
|
29
29
|
* @property {string} modelValue
|
|
30
30
|
* @property {number} modelValue
|
|
31
31
|
*/
|
|
@@ -43,6 +43,7 @@ const emit = defineEmits([
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Triggers when the input gains focus.
|
|
46
|
+
* @property {FocusEvent} event
|
|
46
47
|
*/
|
|
47
48
|
"focus",
|
|
48
49
|
|
|
@@ -53,11 +54,12 @@ const emit = defineEmits([
|
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Triggers when the input loses focus.
|
|
57
|
+
* @property {FocusEvent} event
|
|
56
58
|
*/
|
|
57
59
|
"blur",
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
|
-
* Triggers when the input value is
|
|
62
|
+
* Triggers when the input value is changed.
|
|
61
63
|
* @property {string} modelValue
|
|
62
64
|
* @property {number} modelValue
|
|
63
65
|
*/
|