Skip to content

Commit 19b42e3

Browse files
Merge pull request #495 from lionel-bijaoui/lb_clean_validation_state
Add a "clean" CSS class for untouched fields
2 parents b02d866 + 4f6b05e commit 19b42e3

File tree

3 files changed

+71
-53
lines changed

3 files changed

+71
-53
lines changed

src/fields/abstractField.js

+12
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default {
4646
const fieldUID = uniqueId(this.fieldID + "_");
4747
return {
4848
fieldUID,
49+
touched: false,
4950
errors: [],
5051
debouncedValidateFunc: null,
5152
debouncedFormatFunction: null
@@ -75,6 +76,8 @@ export default {
7576
},
7677

7778
set(newValue) {
79+
this.touch();
80+
7881
let oldValue = this.value;
7982
newValue = this.formatValueToModel(newValue);
8083

@@ -133,6 +136,8 @@ export default {
133136
}
134137
},
135138
validate() {
139+
this.touch();
140+
136141
this.clearValidationErrors();
137142
let validateAsync = objGet(this.formOptions, "validateAsync", false);
138143

@@ -277,6 +282,13 @@ export default {
277282

278283
formatValueToModel(value) {
279284
return value;
285+
},
286+
287+
touch() {
288+
if (!this.touched) {
289+
this.touched = true;
290+
this.$emit("field-touched");
291+
}
280292
}
281293
},
282294
created() {

src/formElement.vue

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</label>
77

88
<div class="field-wrap">
9-
<component ref="child" :is="fieldType" :model="model" :schema="field" :formOptions="options" :eventBus="eventBus" :fieldID="fieldID" @errors-updated="onChildValidated"></component>
9+
<component ref="child" :is="fieldType" :model="model" :schema="field" :formOptions="options" :eventBus="eventBus" :fieldID="fieldID" @field-touched="onFieldTouched" @errors-updated="onChildValidated"></component>
1010
<div v-if="buttonsAreVisible" class="buttons">
1111
<button v-for="(btn, index) in field.buttons" @click="buttonClickHandler(btn, field, $event)" :class="btn.classes" :key="index" v-text="btn.label"></button>
1212
</div>
@@ -51,7 +51,8 @@ export default {
5151
},
5252
data() {
5353
return {
54-
childErrors: []
54+
childErrors: [],
55+
childTouched: false
5556
};
5657
},
5758
computed: {
@@ -84,7 +85,8 @@ export default {
8485
fieldRowClasses() {
8586
let baseClasses = {
8687
[objGet(this.options, "validationErrorClass", "error")]: this.fieldHasErrors,
87-
[objGet(this.options, "validationSuccessClass", "valid")]: !this.fieldHasErrors,
88+
[objGet(this.options, "validationSuccessClass", "valid")]: !this.fieldHasErrors && this.childTouched,
89+
[objGet(this.options, "validationCleanClass", "clean")]: !this.fieldHasErrors && !this.childTouched,
8890
disabled: this.getValueFromOption(this.field, "disabled"),
8991
readonly: this.getValueFromOption(this.field, "readonly"),
9092
featured: this.getValueFromOption(this.field, "featured"),
@@ -119,6 +121,9 @@ export default {
119121
buttonClickHandler(btn, field, event) {
120122
return btn.onclick.call(this, this.model, field, event, this);
121123
},
124+
onFieldTouched() {
125+
this.childTouched = true;
126+
},
122127
onChildValidated(errors) {
123128
this.childErrors = errors;
124129
}

test/unit/specs/VueFormGenerator.spec.js

+51-50
Original file line numberDiff line numberDiff line change
@@ -72,33 +72,22 @@ describe("VueFormGenerator.vue", () => {
7272
});
7373

7474
describe("check form-element classes", () => {
75-
let group;
76-
let schema = {
77-
fields: [
78-
{
79-
type: "input",
80-
fieldOptions: {
81-
inputType: "text"
82-
},
83-
label: "Name",
84-
model: "name",
85-
readonly: false,
86-
featured: false,
87-
required: false,
88-
disabled: false
89-
}
90-
]
91-
};
75+
let formGenerator;
76+
let formElement;
77+
let schema;
9278

9379
beforeEach(() => {
9480
// Reset schema value
9581
schema = {
9682
fields: [
9783
{
9884
type: "input",
99-
fieldOptions: {
100-
inputType: "text"
101-
},
85+
fieldOptions: { inputType: "text" },
86+
/*
87+
styleClasses need to be defined for the unit test to work (add getter/setter)
88+
In real use, it is not mandatory
89+
*/
90+
styleClasses: "",
10291
label: "Name",
10392
model: "name",
10493
readonly: false,
@@ -109,83 +98,95 @@ describe("VueFormGenerator.vue", () => {
10998
]
11099
};
111100
createFormGenerator({ schema });
112-
group = wrapper.find(".form-element");
101+
formGenerator = wrapper.find({ name: "formGenerator" });
102+
formElement = wrapper.find({ name: "form-element" });
113103
});
114104

115105
it("should be minimal classes", () => {
116-
expect(group.classes().length).to.be.equal(2);
117-
expect(group.classes()).to.include("form-element");
118-
expect(group.classes()).to.include("field-input");
106+
expect(formElement.classes().length).to.be.equal(3);
107+
expect(formElement.classes()).to.include("form-element");
108+
expect(formElement.classes()).to.include("field-input");
119109
});
120110

121111
it("should be featured class", () => {
122112
wrapper.vm.schema.fields[0].featured = true;
123113

124-
expect(group.classes()).to.include("featured");
114+
expect(formElement.classes()).to.include("featured");
125115
});
126116

127117
it("should be readonly class", () => {
128118
wrapper.vm.schema.fields[0].readonly = true;
129119

130-
expect(group.classes()).to.include("readonly");
120+
expect(formElement.classes()).to.include("readonly");
131121
});
132122

133123
it("should be disabled class", () => {
134124
wrapper.vm.schema.fields[0].disabled = true;
135125

136-
expect(group.classes()).to.include("disabled");
126+
expect(formElement.classes()).to.include("disabled");
137127
});
138128

139129
it("should be required class", () => {
140130
wrapper.vm.schema.fields[0].required = true;
141131

142-
expect(group.classes()).to.include("required");
132+
expect(formElement.classes()).to.include("required");
143133
});
144134

145135
it("should be error class", () => {
146-
const formElement = wrapper.find({ name: "form-element" });
147136
formElement.vm.onChildValidated(["Validation error!"]);
148-
expect(group.classes()).to.include("error");
137+
expect(formElement.classes()).to.include("error");
149138
});
150139

151140
describe("custom validation classes", () => {
141+
let formGenerator;
142+
let formElement;
152143
beforeEach(() => {
153-
let options = { validationErrorClass: "has-error", validationSuccessClass: "has-success" };
144+
let options = {
145+
validationCleanClass: "is-clean",
146+
validationSuccessClass: "has-success",
147+
validationErrorClass: "has-error"
148+
};
154149
createFormGenerator({
155150
schema,
156151
options: options
157152
});
158-
group = wrapper.find(".form-element");
153+
formGenerator = wrapper.find({ name: "formGenerator" });
154+
formElement = wrapper.find({ name: "form-element" });
155+
});
156+
157+
it("clean class", () => {
158+
expect(formElement.classes()).to.include("is-clean");
159159
});
160160

161161
it("error class", () => {
162-
const formElement = wrapper.find({ name: "form-element" });
163162
formElement.vm.onChildValidated(["Validation error!"]);
164163

165-
expect(group.classes()).to.include("has-error");
164+
expect(formElement.classes()).to.include("has-error");
166165
});
167166

168-
it("success class", () => {
169-
const formElement = wrapper.find({
170-
name: "form-element"
171-
});
172-
formElement.vm.onChildValidated([]);
173-
174-
expect(group.classes()).to.include("has-success");
167+
it("success class", (done) => {
168+
formGenerator.vm.validate().then(
169+
() => {
170+
expect(formElement.classes()).to.include("has-success");
171+
done();
172+
},
173+
() => {}
174+
);
175175
});
176176
});
177-
// Work in real use, but not here
178-
it.skip("should be add a custom classes", () => {
179-
wrapper.vm.schema.fields[0].styleClasses = "classA";
180177

181-
expect(group.classes()).to.include("classA");
178+
it("should be add a custom classes", () => {
179+
schema.fields[0].styleClasses = "classA";
180+
formGenerator.setProps({ schema: { ...schema } });
181+
182+
expect(formElement.classes()).to.include("classA");
182183
});
183-
// Work in real use, but not here
184-
it.skip("should be add more custom classes", () => {
185-
wrapper.vm.schema.fields[0].styleClasses = ["classB", "classC"];
186184

187-
expect(group.classes()).to.include("classB");
188-
expect(group.classes()).to.include("classC");
185+
it("should be add more custom classes", () => {
186+
schema.fields[0].styleClasses = ["classB", "classC"];
187+
188+
expect(formElement.classes()).to.include("classB");
189+
expect(formElement.classes()).to.include("classC");
189190
});
190191
});
191192
// TODO: should be moved to formGroup

0 commit comments

Comments
 (0)