vueless 1.0.2-beta.37 → 1.0.2-beta.38

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 (35) hide show
  1. package/package.json +1 -1
  2. package/ui.container-modal/storybook/stories.ts +1 -1
  3. package/ui.container-page/storybook/stories.ts +1 -1
  4. package/ui.form-checkbox/tests/UCheckbox.test.ts +314 -0
  5. package/ui.form-checkbox/types.ts +1 -1
  6. package/ui.form-checkbox-group/UCheckboxGroup.vue +2 -2
  7. package/ui.form-checkbox-group/tests/UCheckboxGroup.test.ts +267 -0
  8. package/ui.form-checkbox-multi-state/UCheckboxMultiState.vue +1 -0
  9. package/ui.form-checkbox-multi-state/tests/UCheckboxMultiState.test.ts +224 -0
  10. package/ui.form-color-picker/UColorPicker.vue +2 -2
  11. package/ui.form-color-picker/tests/UColorPicker.test.ts +142 -0
  12. package/ui.form-date-picker/storybook/stories.ts +4 -2
  13. package/ui.form-date-picker-range/storybook/stories.ts +4 -2
  14. package/ui.form-input/UInput.vue +3 -3
  15. package/ui.form-input/config.ts +1 -1
  16. package/ui.form-input/storybook/stories.ts +36 -13
  17. package/ui.form-input/tests/UInput.test.ts +2 -2
  18. package/ui.form-input/types.ts +2 -2
  19. package/ui.form-input-counter/config.ts +3 -2
  20. package/ui.form-input-file/config.ts +2 -2
  21. package/ui.form-input-file/storybook/stories.ts +14 -14
  22. package/ui.form-input-file/tests/UInputFile.test.ts +4 -3
  23. package/ui.form-input-number/storybook/stories.ts +22 -9
  24. package/ui.form-input-password/UInputPassword.vue +8 -0
  25. package/ui.form-input-password/storybook/stories.ts +35 -10
  26. package/ui.form-input-search/storybook/stories.ts +24 -5
  27. package/ui.form-label/storybook/stories.ts +5 -4
  28. package/ui.form-radio/URadio.vue +2 -4
  29. package/ui.form-radio/tests/URadio.test.ts +226 -0
  30. package/ui.form-radio-group/tests/URadioGroup.test.ts +277 -0
  31. package/ui.form-select/storybook/stories.ts +17 -22
  32. package/ui.form-switch/storybook/stories.ts +5 -3
  33. package/ui.form-switch/tests/USwitch.test.ts +239 -0
  34. package/ui.form-textarea/storybook/stories.ts +4 -4
  35. package/ui.skeleton-input/storybook/stories.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.0.2-beta.37",
3
+ "version": "1.0.2-beta.38",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
6
6
  "keywords": [
@@ -52,7 +52,7 @@ export default {
52
52
  const defaultTemplate = `
53
53
  <UCol align="stretch">
54
54
  <URow>
55
- <UInput label="Full Name" placeholder="John Doe" />
55
+ <UInput label="Name" placeholder="John Doe" />
56
56
  <UInput label="Email Address" type="email" placeholder="john.doe@example.com" />
57
57
  </URow>
58
58
 
@@ -47,7 +47,7 @@ const defaultTemplate = `
47
47
  <UCard title="Profile Information">
48
48
  <UCol align="stretch">
49
49
  <URow>
50
- <UInput label="Full Name" placeholder="John Doe" />
50
+ <UInput label="Name" placeholder="John Doe" />
51
51
  <UInput label="Email Address" type="email" placeholder="john.doe@example.com" />
52
52
  </URow>
53
53
 
@@ -0,0 +1,314 @@
1
+ import { flushPromises, mount } from "@vue/test-utils";
2
+ import { describe, it, expect } from "vitest";
3
+
4
+ import UCheckbox from "../UCheckbox.vue";
5
+ import UIcon from "../../ui.image-icon/UIcon.vue";
6
+ import ULabel from "../../ui.form-label/ULabel.vue";
7
+
8
+ import type { Props } from "../types.ts";
9
+
10
+ describe("UCheckbox.vue", () => {
11
+ describe("Props", () => {
12
+ it("Model Value – sets the correct model value", async () => {
13
+ const modelValue = true;
14
+
15
+ const component = mount(UCheckbox, {
16
+ props: {
17
+ modelValue,
18
+ "onUpdate:modelValue": (e) => component.setProps({ modelValue: e }),
19
+ },
20
+ });
21
+
22
+ const inputElement = component.find("input");
23
+
24
+ await inputElement.trigger("change");
25
+
26
+ expect(component.emitted("update:modelValue")![0][0]).toBe(false);
27
+
28
+ await inputElement.trigger("change");
29
+
30
+ expect(component.emitted("update:modelValue")![1][0]).toBe(true);
31
+ });
32
+
33
+ it("Value – returns correct value type when checkbox is checked", async () => {
34
+ const testValues = ["string-value", 42, true, { id: 1, name: "test" }, [1, 2, 3]];
35
+
36
+ testValues.forEach(async (testValue) => {
37
+ const component = mount(UCheckbox, {
38
+ props: {
39
+ value: testValue,
40
+ modelValue: [],
41
+ },
42
+ });
43
+
44
+ const inputElement = component.find("input");
45
+
46
+ await inputElement.setValue(true);
47
+
48
+ const emittedValues = component.emitted("update:modelValue");
49
+
50
+ expect(emittedValues![0][0]).toEqual([testValue]);
51
+ });
52
+ });
53
+
54
+ it("TrueValue and FalseValue – returns correct values when checkbox is checked and unchecked", async () => {
55
+ const testValuePairs = [
56
+ { trueValue: "checked", falseValue: "unchecked" },
57
+ { trueValue: 1, falseValue: 0 },
58
+ { trueValue: { status: "active" }, falseValue: { status: "inactive" } },
59
+ { trueValue: true, falseValue: false },
60
+ ];
61
+
62
+ testValuePairs.forEach(async ({ trueValue, falseValue }) => {
63
+ const component = mount(UCheckbox, {
64
+ props: {
65
+ trueValue,
66
+ falseValue,
67
+ modelValue: falseValue,
68
+ },
69
+ });
70
+
71
+ const inputElement = component.find("input");
72
+
73
+ await inputElement.setValue(true);
74
+ let emittedValues = component.emitted("update:modelValue");
75
+
76
+ expect(emittedValues![0][0]).toEqual(trueValue);
77
+
78
+ await component.setProps({ modelValue: trueValue });
79
+
80
+ await inputElement.setValue(false);
81
+ emittedValues = component.emitted("update:modelValue");
82
+
83
+ expect(emittedValues![1][0]).toEqual(falseValue);
84
+ });
85
+ });
86
+
87
+ it("Partial – displays correct icon when checkbox is partially checked", () => {
88
+ const normalComponent = mount(UCheckbox, {
89
+ props: {
90
+ modelValue: true,
91
+ partial: false,
92
+ },
93
+ });
94
+
95
+ const normalIcon = normalComponent.findComponent(UIcon);
96
+
97
+ expect(normalIcon.props("name")).toBe("check");
98
+
99
+ const partialComponent = mount(UCheckbox, {
100
+ props: {
101
+ modelValue: true,
102
+ partial: true,
103
+ },
104
+ });
105
+
106
+ const partialIcon = partialComponent.findComponent(UIcon);
107
+
108
+ expect(partialIcon.props("name")).toBe("remove");
109
+ });
110
+
111
+ it("Name – sets the correct name attribute", async () => {
112
+ const name = "checkbox-name";
113
+
114
+ const component = mount(UCheckbox, {
115
+ props: {
116
+ name,
117
+ },
118
+ });
119
+
120
+ await flushPromises();
121
+
122
+ expect(component.find("input").attributes("name")).toBe(name);
123
+ });
124
+
125
+ it("Label – passes label to ULabel component", () => {
126
+ const labelText = "Test Label";
127
+
128
+ const component = mount(UCheckbox, {
129
+ props: {
130
+ label: labelText,
131
+ },
132
+ });
133
+
134
+ expect(component.getComponent(ULabel).props("label")).toBe(labelText);
135
+ });
136
+
137
+ it("Label Align – passes labelAlign prop to ULabel component", () => {
138
+ const labelAlign = "left";
139
+
140
+ const component = mount(UCheckbox, {
141
+ props: {
142
+ label: "Test Label",
143
+ labelAlign,
144
+ },
145
+ });
146
+
147
+ expect(component.getComponent(ULabel).props("align")).toBe(labelAlign);
148
+ });
149
+
150
+ it("Description – passes description to ULabel component", () => {
151
+ const descriptionText = "This is a description";
152
+
153
+ const component = mount(UCheckbox, {
154
+ props: {
155
+ description: descriptionText,
156
+ },
157
+ });
158
+
159
+ expect(component.getComponent(ULabel).props("description")).toBe(descriptionText);
160
+ });
161
+
162
+ it("Size – applies the correct size class", () => {
163
+ const size = {
164
+ sm: "size-4",
165
+ md: "size-5",
166
+ lg: "size-6",
167
+ };
168
+
169
+ Object.entries(size).forEach(([size, classes]) => {
170
+ const component = mount(UCheckbox, {
171
+ props: {
172
+ size: size as Props["size"],
173
+ },
174
+ });
175
+
176
+ expect(component.get("[vl-key='checkbox']").attributes("class")).toContain(classes);
177
+ });
178
+ });
179
+
180
+ it("Color – applies the correct color class when checked", () => {
181
+ const colors = [
182
+ "primary",
183
+ "secondary",
184
+ "error",
185
+ "warning",
186
+ "success",
187
+ "info",
188
+ "notice",
189
+ "neutral",
190
+ "grayscale",
191
+ ];
192
+
193
+ colors.forEach((color) => {
194
+ const component = mount(UCheckbox, {
195
+ props: {
196
+ color: color as Props["color"],
197
+ modelValue: true,
198
+ },
199
+ });
200
+
201
+ expect(component.find("label").attributes("class")).toContain(color);
202
+ });
203
+ });
204
+
205
+ it("Disabled – applies disabled attribute when disabled prop is true", () => {
206
+ const disabledOpacityVar = "--vl-disabled-opacity";
207
+
208
+ const component = mount(UCheckbox, {
209
+ props: {
210
+ disabled: true,
211
+ },
212
+ });
213
+
214
+ expect(component.find("input").attributes("disabled")).toBeDefined();
215
+ const labelComponent = component.findComponent(ULabel);
216
+ const checkboxInput = component.get("[vl-key='checkbox']");
217
+
218
+ expect(labelComponent.props("disabled")).toBe(true);
219
+ expect(checkboxInput.attributes("class")).toContain(disabledOpacityVar);
220
+ });
221
+
222
+ it("Id – applies the correct id attribute", () => {
223
+ const id = "test-switch-id";
224
+
225
+ const component = mount(UCheckbox, {
226
+ props: {
227
+ id,
228
+ },
229
+ });
230
+
231
+ expect(component.find("input").attributes("id")).toBe(id);
232
+ });
233
+
234
+ it("Data Test – applies the correct data-test attribute", () => {
235
+ const dataTest = "test-checkbox";
236
+ const labelDataTest = "test-checkbox-label";
237
+
238
+ const component = mount(UCheckbox, {
239
+ props: {
240
+ label: "Test",
241
+ dataTest,
242
+ },
243
+ });
244
+
245
+ expect(component.findComponent(ULabel).attributes("data-test")).toBe(labelDataTest);
246
+ expect(component.get("input").attributes("data-test")).toBe(dataTest);
247
+ });
248
+ });
249
+
250
+ describe("Slots", () => {
251
+ it("Label – renders custom content from label slot", () => {
252
+ const customLabelContent = "Custom Label Content";
253
+
254
+ const component = mount(UCheckbox, {
255
+ props: {
256
+ label: "Default Label",
257
+ },
258
+ slots: {
259
+ label: customLabelContent,
260
+ },
261
+ });
262
+
263
+ const labelComponent = component.getComponent(ULabel);
264
+ const labelElement = labelComponent.find("label");
265
+
266
+ expect(labelElement.text()).toBe(customLabelContent);
267
+ });
268
+
269
+ it("Label – exposes label prop to slot", () => {
270
+ const defaultLabel = "Test Label";
271
+
272
+ const component = mount(UCheckbox, {
273
+ props: {
274
+ label: defaultLabel,
275
+ },
276
+ slots: {
277
+ label: "Modified {{ params.label }}",
278
+ },
279
+ });
280
+
281
+ const labelComponent = component.getComponent(ULabel);
282
+ const labelElement = labelComponent.find("label");
283
+
284
+ expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
285
+ });
286
+
287
+ it("Bottom – renders custom content from bottom slot", () => {
288
+ const customBottomContent = "Custom Bottom Content";
289
+
290
+ const component = mount(UCheckbox, {
291
+ props: {
292
+ label: "Test Label",
293
+ },
294
+ slots: {
295
+ bottom: customBottomContent,
296
+ },
297
+ });
298
+
299
+ const labelComponent = component.getComponent(ULabel);
300
+
301
+ expect(labelComponent.text()).toContain(customBottomContent);
302
+ });
303
+ });
304
+
305
+ describe("Events", () => {
306
+ it("Input – emits input event when checkbox is toggled", async () => {
307
+ const component = mount(UCheckbox);
308
+
309
+ await component.find("input").setValue(true);
310
+
311
+ expect(component.emitted("input")![0]).toEqual([true]);
312
+ });
313
+ });
314
+ });
@@ -21,7 +21,7 @@ export interface Props {
21
21
  modelValue?: boolean | string | number | UnknownArray | UnknownObject;
22
22
 
23
23
  /**
24
- * Native value attribute.
24
+ * Value of the checkbox.
25
25
  */
26
26
  value?: boolean | string | number | UnknownArray | UnknownObject;
27
27
 
@@ -46,7 +46,7 @@ provide<() => string>("getCheckboxGroupName", () => props.name);
46
46
  provide("getCheckboxGroupColor", () => props.color);
47
47
  provide("getCheckboxSize", () => props.size);
48
48
 
49
- watch(() => checkedItems.value.length, onChangeCheckedItems);
49
+ watch(checkedItems, onChangeCheckedItems, { deep: true });
50
50
  watch(
51
51
  () => props?.modelValue?.length,
52
52
  (newValue, oldValue) => {
@@ -97,7 +97,7 @@ const { getDataTest, groupLabelAttrs, groupCheckboxAttrs, listAttrs } =
97
97
  </template>
98
98
 
99
99
  <div ref="list" v-bind="listAttrs">
100
- <!-- @slot Use it to add URadio directly. -->
100
+ <!-- @slot Use it to add UCheckbox directly. -->
101
101
  <slot>
102
102
  <UCheckbox
103
103
  v-for="(option, index) in options"
@@ -0,0 +1,267 @@
1
+ import { flushPromises, mount } from "@vue/test-utils";
2
+ import { describe, it, expect } from "vitest";
3
+ import { nextTick } from "vue";
4
+
5
+ import UCheckboxGroup from "../UCheckboxGroup.vue";
6
+ import UCheckbox from "../../ui.form-checkbox/UCheckbox.vue";
7
+ import ULabel from "../../ui.form-label/ULabel.vue";
8
+
9
+ import type { Props } from "../types.ts";
10
+
11
+ describe("UCheckboxGroup.vue", () => {
12
+ const defaultOptions = [
13
+ { label: "Email Notifications", value: "email" },
14
+ { label: "SMS Alerts", value: "sms" },
15
+ { label: "Push Notifications", value: "push" },
16
+ ];
17
+
18
+ describe("Props", () => {
19
+ it("Options - renders checkboxes for each option", () => {
20
+ const component = mount(UCheckboxGroup, {
21
+ props: {
22
+ options: defaultOptions,
23
+ },
24
+ });
25
+
26
+ const checkboxes = component.findAllComponents(UCheckbox);
27
+
28
+ expect(checkboxes).toHaveLength(defaultOptions.length);
29
+
30
+ checkboxes.forEach((checkbox, index) => {
31
+ expect(checkbox.props("label")).toBe(defaultOptions[index].label);
32
+ expect(checkbox.props("value")).toBe(defaultOptions[index].value);
33
+ });
34
+ });
35
+
36
+ it("Options – checkbox emits value on change", async () => {
37
+ const expectedValues = defaultOptions.map((option) => option.value);
38
+
39
+ const component = mount(UCheckboxGroup, {
40
+ props: {
41
+ options: defaultOptions,
42
+ name: "notification-preferences",
43
+ modelValue: [],
44
+ },
45
+ });
46
+
47
+ component.findAll("input[type='checkbox']").forEach(async (checkbox) => {
48
+ await checkbox.setValue(true);
49
+ });
50
+
51
+ await nextTick();
52
+
53
+ expect(component.emitted("update:modelValue")![1][0]).toEqual(expectedValues);
54
+ });
55
+
56
+ it("Label – passes label to ULabel component", () => {
57
+ const labelText = "Test Label";
58
+
59
+ const component = mount(UCheckboxGroup, {
60
+ props: {
61
+ label: labelText,
62
+ },
63
+ });
64
+
65
+ expect(component.getComponent(ULabel).props("label")).toBe(labelText);
66
+ });
67
+
68
+ it("Description – passes description to ULabel component", () => {
69
+ const descriptionText = "This is a description";
70
+
71
+ const component = mount(UCheckboxGroup, {
72
+ props: {
73
+ description: descriptionText,
74
+ },
75
+ });
76
+
77
+ expect(component.getComponent(ULabel).props("description")).toBe(descriptionText);
78
+ });
79
+
80
+ it("Error – passes error to ULabel component", () => {
81
+ const errorText = "This is an error message";
82
+
83
+ const component = mount(UCheckboxGroup, {
84
+ props: {
85
+ error: errorText,
86
+ },
87
+ });
88
+
89
+ expect(component.getComponent(ULabel).props("error")).toBe(errorText);
90
+ });
91
+
92
+ it("Size – applies the correct size class", () => {
93
+ const size = {
94
+ sm: "size-4",
95
+ md: "size-5",
96
+ lg: "size-6",
97
+ };
98
+
99
+ Object.entries(size).forEach(([size, classes]) => {
100
+ const component = mount(UCheckboxGroup, {
101
+ props: {
102
+ size: size as Props["size"],
103
+ options: defaultOptions,
104
+ },
105
+ });
106
+
107
+ const checkboxInput = component.getComponent(UCheckbox).get("input");
108
+
109
+ expect(checkboxInput.attributes("class")).toContain(classes);
110
+ });
111
+ });
112
+
113
+ it("Name – sets the correct UCheckbox name prop", async () => {
114
+ const name = "checkbox-name";
115
+
116
+ const component = mount(UCheckboxGroup, {
117
+ props: {
118
+ name,
119
+ options: defaultOptions,
120
+ },
121
+ });
122
+
123
+ await flushPromises();
124
+
125
+ component.findAllComponents("input").forEach((checkbox) => {
126
+ expect(checkbox.attributes("name")).toBe(name);
127
+ });
128
+ });
129
+
130
+ it("Color – sets correct UCheckbox color prop", () => {
131
+ const colors = [
132
+ "primary",
133
+ "secondary",
134
+ "error",
135
+ "warning",
136
+ "success",
137
+ "info",
138
+ "notice",
139
+ "neutral",
140
+ "grayscale",
141
+ ];
142
+
143
+ colors.forEach((color) => {
144
+ const component = mount(UCheckboxGroup, {
145
+ props: {
146
+ color: color as Props["color"],
147
+ options: defaultOptions,
148
+ },
149
+ });
150
+
151
+ component.findAllComponents(UCheckbox).forEach(async (checkbox) => {
152
+ const checkboxInput = checkbox.get("input");
153
+
154
+ await checkboxInput.trigger("input");
155
+
156
+ expect(checkboxInput.attributes("class")).toContain(color);
157
+ });
158
+ });
159
+ });
160
+
161
+ it("Disabled – sets correct UCheckbox disabled prop", () => {
162
+ const component = mount(UCheckboxGroup, {
163
+ props: {
164
+ disabled: true,
165
+ options: defaultOptions,
166
+ },
167
+ });
168
+
169
+ component.findAllComponents(UCheckbox).forEach((checkbox) => {
170
+ expect(checkbox.props("disabled")).toBe(true);
171
+ });
172
+ });
173
+
174
+ it("Data test – sets correct data-test attribute to checkboxes", () => {
175
+ const dataTestValue = "checkbox";
176
+
177
+ const component = mount(UCheckboxGroup, {
178
+ props: {
179
+ "data-test": dataTestValue,
180
+ options: defaultOptions,
181
+ },
182
+ });
183
+
184
+ component.findAllComponents(UCheckbox).forEach((checkbox, idx) => {
185
+ expect(checkbox.attributes("data-test")).toBe(`${dataTestValue}-item-${idx}-label`);
186
+ });
187
+ });
188
+ });
189
+
190
+ describe("Slots", () => {
191
+ it("Label – renders custom content from label slot", () => {
192
+ const customLabelContent = "Custom Label Content";
193
+
194
+ const component = mount(UCheckboxGroup, {
195
+ props: {
196
+ label: "Default Label",
197
+ },
198
+ slots: {
199
+ label: customLabelContent,
200
+ },
201
+ });
202
+
203
+ const labelComponent = component.getComponent(ULabel);
204
+ const labelElement = labelComponent.find("label");
205
+
206
+ expect(labelElement.text()).toBe(customLabelContent);
207
+ });
208
+
209
+ it("Label – exposes label prop to slot", () => {
210
+ const defaultLabel = "Test Label";
211
+
212
+ const component = mount(UCheckboxGroup, {
213
+ props: {
214
+ label: defaultLabel,
215
+ },
216
+ slots: {
217
+ label: "Modified {{ params.label }}",
218
+ },
219
+ });
220
+
221
+ const labelComponent = component.getComponent(ULabel);
222
+ const labelElement = labelComponent.find("label");
223
+
224
+ expect(labelElement.text()).toBe(`Modified ${defaultLabel}`);
225
+ });
226
+
227
+ it("Default slot – renders custom UCheckbox components", () => {
228
+ const component = mount(UCheckboxGroup, {
229
+ props: {
230
+ modelValue: [],
231
+ name: "custom-group",
232
+ },
233
+ slots: {
234
+ default: `
235
+ <UCheckbox value="option1" label="Custom Option 1" />
236
+ <UCheckbox value="option2" label="Custom Option 2" />
237
+ <UCheckbox value="option3" label="Custom Option 3" />
238
+ `,
239
+ },
240
+ global: {
241
+ components: {
242
+ UCheckbox,
243
+ },
244
+ },
245
+ });
246
+
247
+ const checkboxes = component.findAllComponents(UCheckbox);
248
+
249
+ expect(checkboxes).toHaveLength(3);
250
+
251
+ expect(checkboxes[0].props("value")).toBe("option1");
252
+ expect(checkboxes[0].props("label")).toBe("Custom Option 1");
253
+ expect(checkboxes[1].props("value")).toBe("option2");
254
+ expect(checkboxes[1].props("label")).toBe("Custom Option 2");
255
+ expect(checkboxes[2].props("value")).toBe("option3");
256
+ expect(checkboxes[2].props("label")).toBe("Custom Option 3");
257
+ });
258
+ });
259
+
260
+ describe("Exposed properties", () => {
261
+ it("exposes listRef", () => {
262
+ const component = mount(UCheckboxGroup);
263
+
264
+ expect(component.vm.listRef).toBeDefined();
265
+ });
266
+ });
267
+ });
@@ -92,6 +92,7 @@ const multiStateCheckboxAttrs = computed(() => {
92
92
  :color="color"
93
93
  :label-align="labelAlign"
94
94
  :disabled="disabled"
95
+ :data-test="dataTest"
95
96
  v-bind="multiStateCheckboxAttrs"
96
97
  @input="onClickCheckbox"
97
98
  />