aloha-vue 2.81.0 → 2.82.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/CHANGELOG.md +4 -0
- package/dist/aloha-vue.es.js +314 -10
- package/dist/aloha-vue.umd.js +15 -15
- package/package.json +1 -1
- package/src/ui/ACheckbox/ACheckbox.js +6 -0
- package/src/ui/ACheckbox/ACheckboxItem/ACheckboxItem.js +6 -0
- package/src/ui/ADatepicker/ADatepicker.js +6 -0
- package/src/ui/ADatepickerRange/ADatepickerRange.js +6 -1
- package/src/ui/AFieldset/AFieldset.js +7 -1
- package/src/ui/AForm/AForm.js +6 -0
- package/src/ui/AFormElement/AFormElement.js +5 -0
- package/src/ui/AFormReadonly/AFormReadonly.js +1 -1
- package/src/ui/AGroup/AGroup.js +8 -1
- package/src/ui/AInput/AInput.js +6 -0
- package/src/ui/AInputCurrency/AInputCurrency.js +6 -0
- package/src/ui/AInputFile/AInputFile.js +40 -5
- package/src/ui/AInputNumber/AInputNumber.js +6 -0
- package/src/ui/AInputNumberRange/AInputNumberRange.js +6 -1
- package/src/ui/AJson/AJson.js +6 -0
- package/src/ui/AMultiselectOrdered/AMultiselectOrdered.js +30 -24
- package/src/ui/AOneCheckbox/AOneCheckbox.js +6 -0
- package/src/ui/ARadio/ARadio.js +6 -0
- package/src/ui/ARadio/ARadioItem/ARadioItem.js +6 -0
- package/src/ui/ARouterLinkConfig/ARouterLinkConfig.js +7 -0
- package/src/ui/ASelect/ASelect.js +6 -0
- package/src/ui/ASelectIcon/ASelectIcon.js +16 -11
- package/src/ui/ASelectStyle/ASelectStyle.js +16 -11
- package/src/ui/ASlider/ASlider.js +6 -0
- package/src/ui/ASwitch/ASwitch.js +11 -5
- package/src/ui/ATemplate/ATemplate.js +7 -2
- package/src/ui/ATextarea/ATextarea.js +6 -0
- package/src/ui/AValidatedJson/AValidatedJson.js +10 -2
- package/src/ui/AValidatedJson/AValidatedJsonItem/AValidatedJsonItem.js +6 -0
- package/src/ui/__tests__/ReadonlyValueParentClass.test.js +217 -0
|
@@ -192,6 +192,11 @@ export default {
|
|
|
192
192
|
type: Boolean,
|
|
193
193
|
required: false,
|
|
194
194
|
},
|
|
195
|
+
readonlyValueParentClass: {
|
|
196
|
+
type: [String, Object, Array],
|
|
197
|
+
required: false,
|
|
198
|
+
default: undefined,
|
|
199
|
+
},
|
|
195
200
|
readonlyDefault: {
|
|
196
201
|
type: String,
|
|
197
202
|
required: false,
|
|
@@ -321,6 +326,7 @@ export default {
|
|
|
321
326
|
if (this.readonly) {
|
|
322
327
|
return h(AFormReadonly, {
|
|
323
328
|
...this.$attrs,
|
|
329
|
+
valueParentClass: this.readonlyValueParentClass,
|
|
324
330
|
id: this.htmlIdLocal,
|
|
325
331
|
alwaysTranslate: this.alwaysTranslate,
|
|
326
332
|
excludeRenderAttributes: this.excludeRenderAttributes,
|
|
@@ -348,11 +354,11 @@ export default {
|
|
|
348
354
|
}],
|
|
349
355
|
}, [
|
|
350
356
|
(this.label || this.labelScreenReader) ?
|
|
351
|
-
h(ALabel, {
|
|
352
|
-
id: this.htmlIdLocal,
|
|
353
|
-
alwaysTranslate: this.alwaysTranslate,
|
|
354
|
-
extra: this.extra,
|
|
355
|
-
label: this.label,
|
|
357
|
+
h(ALabel, {
|
|
358
|
+
id: this.htmlIdLocal,
|
|
359
|
+
alwaysTranslate: this.alwaysTranslate,
|
|
360
|
+
extra: this.extra,
|
|
361
|
+
label: this.label,
|
|
356
362
|
labelClass: this.labelClass,
|
|
357
363
|
labelScreenReader: this.labelScreenReader,
|
|
358
364
|
required: this.required,
|
|
@@ -105,6 +105,11 @@ export default {
|
|
|
105
105
|
type: Boolean,
|
|
106
106
|
required: false,
|
|
107
107
|
},
|
|
108
|
+
readonlyValueParentClass: {
|
|
109
|
+
type: [String, Object, Array],
|
|
110
|
+
required: false,
|
|
111
|
+
default: undefined,
|
|
112
|
+
},
|
|
108
113
|
readonlyDefault: {
|
|
109
114
|
type: String,
|
|
110
115
|
required: false,
|
|
@@ -148,7 +153,7 @@ export default {
|
|
|
148
153
|
}, [
|
|
149
154
|
this.$slots[this.slotName] ?
|
|
150
155
|
h("div", {
|
|
151
|
-
class: "a_template",
|
|
156
|
+
class: ["a_template", this.readonly && this.readonlyValueParentClass],
|
|
152
157
|
}, this.$slots[this.slotName]({
|
|
153
158
|
options: this.options,
|
|
154
159
|
props: this.$props,
|
|
@@ -156,7 +161,7 @@ export default {
|
|
|
156
161
|
"",
|
|
157
162
|
this.html ?
|
|
158
163
|
withDirectives(h("div", {
|
|
159
|
-
class: "a_template",
|
|
164
|
+
class: ["a_template", this.readonly && this.readonlyValueParentClass],
|
|
160
165
|
}), [
|
|
161
166
|
[ASafeHtml, this.html],
|
|
162
167
|
]) :
|
|
@@ -186,6 +186,11 @@ export default {
|
|
|
186
186
|
type: Boolean,
|
|
187
187
|
required: false,
|
|
188
188
|
},
|
|
189
|
+
readonlyValueParentClass: {
|
|
190
|
+
type: [String, Object, Array],
|
|
191
|
+
required: false,
|
|
192
|
+
default: undefined,
|
|
193
|
+
},
|
|
189
194
|
readonlyDefault: {
|
|
190
195
|
type: String,
|
|
191
196
|
required: false,
|
|
@@ -314,6 +319,7 @@ export default {
|
|
|
314
319
|
if (this.readonly) {
|
|
315
320
|
return h(AFormReadonly, {
|
|
316
321
|
...this.$attrs,
|
|
322
|
+
valueParentClass: this.readonlyValueParentClass,
|
|
317
323
|
id: this.htmlIdLocal,
|
|
318
324
|
alwaysTranslate: this.alwaysTranslate,
|
|
319
325
|
excludeRenderAttributes: this.excludeRenderAttributes,
|
|
@@ -194,6 +194,11 @@ export default {
|
|
|
194
194
|
required: false,
|
|
195
195
|
default: undefined,
|
|
196
196
|
},
|
|
197
|
+
readonlyValueParentClass: {
|
|
198
|
+
type: [String, Object, Array],
|
|
199
|
+
required: false,
|
|
200
|
+
default: undefined,
|
|
201
|
+
},
|
|
197
202
|
required: {
|
|
198
203
|
type: Boolean,
|
|
199
204
|
required: false,
|
|
@@ -385,6 +390,7 @@ export default {
|
|
|
385
390
|
modelDependencies: this.modelDependencies,
|
|
386
391
|
modelValue: this.modelValue,
|
|
387
392
|
readonly: this.readonly,
|
|
393
|
+
readonlyValueParentClass: this.readonlyValueParentClass,
|
|
388
394
|
required: this.required,
|
|
389
395
|
skipOwnIdInModelPath: true,
|
|
390
396
|
slotNamePrepend: `singlePrepend_${ this.id }`,
|
|
@@ -428,7 +434,7 @@ export default {
|
|
|
428
434
|
class: "a_column a_column_12",
|
|
429
435
|
}, [
|
|
430
436
|
h("ul", {
|
|
431
|
-
class: "a_list_group",
|
|
437
|
+
class: ["a_list_group", this.readonly && this.readonlyValueParentClass],
|
|
432
438
|
}, [
|
|
433
439
|
this.modelListMode.length > 0 ?
|
|
434
440
|
this.listModeItems.map((item, index) => {
|
|
@@ -449,6 +455,7 @@ export default {
|
|
|
449
455
|
parentHtmlId: this.htmlIdLocal,
|
|
450
456
|
parentId: this.id,
|
|
451
457
|
readonly: this.readonly,
|
|
458
|
+
readonlyValueParentClass: this.readonlyValueParentClass,
|
|
452
459
|
typedBaseId: this.typedBaseId,
|
|
453
460
|
typedChildren: this.typedChildren,
|
|
454
461
|
uniqueChildrenIds: this.uniqueChildrenIds,
|
|
@@ -534,7 +541,7 @@ export default {
|
|
|
534
541
|
class: "a_column a_column_12",
|
|
535
542
|
}, [
|
|
536
543
|
h("ul", {
|
|
537
|
-
class: "a_list_group",
|
|
544
|
+
class: ["a_list_group", this.readonly && this.readonlyValueParentClass],
|
|
538
545
|
}, [
|
|
539
546
|
this.isModelJsonModeEmpty ?
|
|
540
547
|
h(ATranslation, {
|
|
@@ -560,6 +567,7 @@ export default {
|
|
|
560
567
|
parentHtmlId: this.htmlIdLocal,
|
|
561
568
|
parentId: this.id,
|
|
562
569
|
readonly: this.readonly,
|
|
570
|
+
readonlyValueParentClass: this.readonlyValueParentClass,
|
|
563
571
|
typedBaseId: this.typedBaseId,
|
|
564
572
|
typedChildren: this.typedChildren,
|
|
565
573
|
uniqueChildrenIds: this.uniqueChildrenIds,
|
|
@@ -103,6 +103,11 @@ export default {
|
|
|
103
103
|
required: false,
|
|
104
104
|
default: undefined,
|
|
105
105
|
},
|
|
106
|
+
readonlyValueParentClass: {
|
|
107
|
+
type: [String, Object, Array],
|
|
108
|
+
required: false,
|
|
109
|
+
default: undefined,
|
|
110
|
+
},
|
|
106
111
|
typedBaseId: {
|
|
107
112
|
type: String,
|
|
108
113
|
required: false,
|
|
@@ -279,6 +284,7 @@ export default {
|
|
|
279
284
|
idPrefix: this.parentHtmlId,
|
|
280
285
|
modelValue: this.modelItem,
|
|
281
286
|
readonly: true,
|
|
287
|
+
readonlyValueParentClass: this.readonlyValueParentClass,
|
|
282
288
|
skipOwnIdInModelPath: true,
|
|
283
289
|
slotNamePrepend: "singlePrepend",
|
|
284
290
|
useFlatModel: this.useFlatModel,
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
h,
|
|
3
|
+
nextTick,
|
|
4
|
+
} from "vue";
|
|
5
|
+
import {
|
|
6
|
+
afterEach,
|
|
7
|
+
beforeEach,
|
|
8
|
+
describe,
|
|
9
|
+
expect,
|
|
10
|
+
it,
|
|
11
|
+
} from "vitest";
|
|
12
|
+
import {
|
|
13
|
+
mount,
|
|
14
|
+
} from "@vue/test-utils";
|
|
15
|
+
import {
|
|
16
|
+
ACheckbox,
|
|
17
|
+
ADatepicker,
|
|
18
|
+
ADatepickerRange,
|
|
19
|
+
AFieldset,
|
|
20
|
+
AForm,
|
|
21
|
+
AFormElement,
|
|
22
|
+
AGroup,
|
|
23
|
+
AInput,
|
|
24
|
+
AInputCurrency,
|
|
25
|
+
AInputFile,
|
|
26
|
+
AInputNumber,
|
|
27
|
+
AInputNumberRange,
|
|
28
|
+
AJson,
|
|
29
|
+
AMultiselectOrdered,
|
|
30
|
+
AOneCheckbox,
|
|
31
|
+
ARadio,
|
|
32
|
+
ARouterLinkConfig,
|
|
33
|
+
ASelect,
|
|
34
|
+
ASelectIcon,
|
|
35
|
+
ASelectStyle,
|
|
36
|
+
ASlider,
|
|
37
|
+
ASwitch,
|
|
38
|
+
ATemplate,
|
|
39
|
+
ATextarea,
|
|
40
|
+
AValidatedJson,
|
|
41
|
+
} from "../../index";
|
|
42
|
+
|
|
43
|
+
import ACheckboxItem from "../ACheckbox/ACheckboxItem/ACheckboxItem";
|
|
44
|
+
import ARadioItem from "../ARadio/ARadioItem/ARadioItem";
|
|
45
|
+
|
|
46
|
+
import {
|
|
47
|
+
setI18n,
|
|
48
|
+
updateTranslation,
|
|
49
|
+
} from "../../ATranslation/compositionAPI/ATranslationAPI";
|
|
50
|
+
|
|
51
|
+
const wrappers = [];
|
|
52
|
+
|
|
53
|
+
beforeEach(async() => {
|
|
54
|
+
setI18n({ de: {} });
|
|
55
|
+
updateTranslation();
|
|
56
|
+
await nextTick();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
wrappers.forEach(wrapper => wrapper.unmount());
|
|
61
|
+
wrappers.length = 0;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
function mountReadonly(component, props = {}, slots = {}) {
|
|
65
|
+
const wrapper = mount(component, {
|
|
66
|
+
props: {
|
|
67
|
+
id: "test",
|
|
68
|
+
readonly: true,
|
|
69
|
+
readonlyValueParentClass: ["custom_value", { custom_object: true }],
|
|
70
|
+
...props,
|
|
71
|
+
},
|
|
72
|
+
slots,
|
|
73
|
+
});
|
|
74
|
+
wrappers.push(wrapper);
|
|
75
|
+
return wrapper;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe("readonlyValueParentClass", () => {
|
|
79
|
+
it.each([
|
|
80
|
+
ACheckbox,
|
|
81
|
+
ADatepicker,
|
|
82
|
+
ADatepickerRange,
|
|
83
|
+
AFieldset,
|
|
84
|
+
AInput,
|
|
85
|
+
AInputCurrency,
|
|
86
|
+
AInputFile,
|
|
87
|
+
AInputNumber,
|
|
88
|
+
AInputNumberRange,
|
|
89
|
+
AJson,
|
|
90
|
+
AMultiselectOrdered,
|
|
91
|
+
AOneCheckbox,
|
|
92
|
+
ARadio,
|
|
93
|
+
ASelect,
|
|
94
|
+
ASelectIcon,
|
|
95
|
+
ASelectStyle,
|
|
96
|
+
ASlider,
|
|
97
|
+
ASwitch,
|
|
98
|
+
ATextarea,
|
|
99
|
+
])("styles the value wrapper in $name and reacts to class/readonly changes", async component => {
|
|
100
|
+
const wrapper = mountReadonly(component);
|
|
101
|
+
await nextTick();
|
|
102
|
+
const value = wrapper.get(".a_form_element_readonly_value");
|
|
103
|
+
expect(value.classes()).toContain("custom_value");
|
|
104
|
+
expect(value.classes()).toContain("custom_object");
|
|
105
|
+
expect(wrapper.classes()).not.toContain("custom_value");
|
|
106
|
+
await wrapper.setProps({ readonlyValueParentClass: "updated_value" });
|
|
107
|
+
expect(value.classes()).toContain("updated_value");
|
|
108
|
+
expect(value.classes()).not.toContain("custom_value");
|
|
109
|
+
await wrapper.setProps({ readonly: false });
|
|
110
|
+
expect(wrapper.find(".updated_value").exists()).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it.each([ADatepickerRange, AInputNumberRange])("preserves range layout in $name", component => {
|
|
114
|
+
const wrapper = mountReadonly(component);
|
|
115
|
+
expect(wrapper.get(".custom_value").classes()).toContain("a_form_element_readonly_value_range");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("preserves fieldset columns and propagates defaults with per-child overrides", () => {
|
|
119
|
+
const wrapper = mountReadonly(AFieldset, {
|
|
120
|
+
children: [
|
|
121
|
+
{ id: "first", type: "text" },
|
|
122
|
+
{ id: "second", type: "text", readonlyValueParentClass: "child_value" },
|
|
123
|
+
],
|
|
124
|
+
modelValue: {},
|
|
125
|
+
});
|
|
126
|
+
const value = wrapper.get(".a_form_element_readonly_value");
|
|
127
|
+
expect(value.classes()).toContain("a_columns");
|
|
128
|
+
const inputs = wrapper.findAllComponents(AInput);
|
|
129
|
+
expect(inputs[0].get(".a_form_element_readonly_value").classes()).toContain("custom_value");
|
|
130
|
+
expect(inputs[1].get(".a_form_element_readonly_value").classes()).toContain("child_value");
|
|
131
|
+
expect(inputs[1].get(".a_form_element_readonly_value").classes()).not.toContain("custom_value");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("propagates defaults through AForm and nested groups", () => {
|
|
135
|
+
const wrapper = mountReadonly(AForm, {
|
|
136
|
+
modelValue: { group: {} },
|
|
137
|
+
data: [{
|
|
138
|
+
id: "group",
|
|
139
|
+
type: "group",
|
|
140
|
+
children: [
|
|
141
|
+
{ id: "first", type: "text" },
|
|
142
|
+
{ id: "second", type: "text", readonlyValueParentClass: "child_value" },
|
|
143
|
+
],
|
|
144
|
+
}],
|
|
145
|
+
});
|
|
146
|
+
const group = wrapper.findComponent(AGroup);
|
|
147
|
+
expect(group.get(".a_columns").classes()).toContain("custom_value");
|
|
148
|
+
const inputs = group.findAllComponents(AInput);
|
|
149
|
+
expect(inputs[0].get(".a_form_element_readonly_value").classes()).toContain("custom_value");
|
|
150
|
+
expect(inputs[1].get(".a_form_element_readonly_value").classes()).toContain("child_value");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("passes the class through AFormElement", () => {
|
|
154
|
+
const wrapper = mountReadonly(AFormElement, { type: "text" });
|
|
155
|
+
expect(wrapper.get(".a_form_element_readonly_value").classes()).toContain("custom_value");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it.each(["routerLinkConfig", "selectRoute", "multiselectRoute"])("passes the class through ARouterLinkConfig (%s)", async type => {
|
|
159
|
+
const wrapper = mountReadonly(ARouterLinkConfig, { type });
|
|
160
|
+
await nextTick();
|
|
161
|
+
expect(wrapper.get(".a_form_element_readonly_value").classes()).toContain("custom_value");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("styles template HTML and slot content only in readonly mode", async() => {
|
|
165
|
+
const wrapper = mountReadonly(ATemplate, { html: "Value", slotName: "content" }, {
|
|
166
|
+
content: () => h("span", "Slot"),
|
|
167
|
+
});
|
|
168
|
+
expect(wrapper.findAll(".a_template.custom_value")).toHaveLength(2);
|
|
169
|
+
await wrapper.setProps({ readonly: false });
|
|
170
|
+
expect(wrapper.find(".custom_value").exists()).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it.each(["list", "json"])("styles AValidatedJson values and previews in %s mode", async mode => {
|
|
174
|
+
const wrapper = mountReadonly(AValidatedJson, {
|
|
175
|
+
mode,
|
|
176
|
+
children: [{ id: "value", type: "text" }],
|
|
177
|
+
modelValue: mode === "list" ? [{ value: "Hello" }] : { first: { value: "Hello" } },
|
|
178
|
+
});
|
|
179
|
+
expect(wrapper.get("ul.a_list_group").classes()).toContain("custom_value");
|
|
180
|
+
await wrapper.get(".test_toggle_details").trigger("click");
|
|
181
|
+
expect(wrapper.get(".a_form_element_readonly_value").classes()).toContain("custom_value");
|
|
182
|
+
await wrapper.setProps({ readonly: false });
|
|
183
|
+
expect(wrapper.get("ul.a_list_group").classes()).not.toContain("custom_value");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("styles AValidatedJson values in single mode", () => {
|
|
187
|
+
const wrapper = mountReadonly(AValidatedJson, {
|
|
188
|
+
mode: "single",
|
|
189
|
+
children: [{ id: "value", type: "text" }],
|
|
190
|
+
modelValue: { value: "Hello" },
|
|
191
|
+
});
|
|
192
|
+
expect(wrapper.get(".a_form_element_readonly_value").classes()).toContain("custom_value");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it.each([ACheckboxItem, ARadioItem])("styles standalone readonly $name", async component => {
|
|
196
|
+
const wrapper = mountReadonly(component, { itemIndex: 0, dataItem: { label: "Value", value: 1 } });
|
|
197
|
+
expect(wrapper.classes()).toContain("custom_value");
|
|
198
|
+
await wrapper.setProps({ readonly: false });
|
|
199
|
+
expect(wrapper.find(".custom_value").exists()).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it.each([false, true])("shows file names without file input (multiple: %s)", isMultiple => {
|
|
203
|
+
const files = [{ name: "<first>.txt" }, { name: "second.txt" }];
|
|
204
|
+
const wrapper = mountReadonly(AInputFile, {
|
|
205
|
+
isMultiple,
|
|
206
|
+
modelValue: isMultiple ? files : files[0],
|
|
207
|
+
});
|
|
208
|
+
expect(wrapper.findAll("li").map(item => item.text())).toEqual(isMultiple ? ["<first>.txt", "second.txt"] : ["<first>.txt"]);
|
|
209
|
+
expect(wrapper.find("input[type=file]").exists()).toBe(false);
|
|
210
|
+
expect(wrapper.find("first").exists()).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("shows readonlyDefault for an empty file value", () => {
|
|
214
|
+
const wrapper = mountReadonly(AInputFile, { readonlyDefault: "No files" });
|
|
215
|
+
expect(wrapper.get(".a_form_element_readonly_value").text()).toBe("No files");
|
|
216
|
+
});
|
|
217
|
+
});
|