This is an automated email from the ASF dual-hosted git repository. machristie pushed a commit to branch AIRAVATA-3562 in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit 6d761044053c67f0000b00a5aedda025585f8d61 Author: Marcus Christie <[email protected]> AuthorDate: Tue May 10 16:12:44 2022 -0400 AIRAVATA-3565 Add validation to extended user profile editor --- .../js/components/ExtendedUserProfileEditor.vue | 6 +++ .../ExtendedUserProfileMultiChoiceFieldEditor.vue | 39 +++++++++++++++++ .../ExtendedUserProfileSingleChoiceFieldEditor.vue | 50 +++++++++++++++++++++- .../ExtendedUserProfileTextFieldEditor.vue | 21 ++++++++- ...ExtendedUserProfileUserAgreementFieldEditor.vue | 30 ++++++++++++- .../js/store/modules/extendedUserProfile.js | 2 +- .../static/common/js/errors/vuelidateHelpers.js | 14 ++++++ 7 files changed, 158 insertions(+), 4 deletions(-) diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue index b224fba2..1f6bcdf8 100644 --- a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue +++ b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue @@ -2,6 +2,7 @@ <b-card> <template v-for="extendedUserProfileField in extendedUserProfileFields"> <component + ref="extendedUserProfileFieldComponents" :key="extendedUserProfileField.id" :is="getEditor(extendedUserProfileField)" :extended-user-profile-field="extendedUserProfileField" @@ -19,6 +20,11 @@ import ExtendedUserProfileUserAgreementFieldEditor from "./ExtendedUserProfileUs export default { computed: { ...mapGetters("extendedUserProfile", ["extendedUserProfileFields"]), + valid() { + return this.$refs.extendedUserProfileFieldComponents.every( + (c) => c.valid + ); + }, }, methods: { getEditor(extendedUserProfileField) { diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue index db45cbb9..8edc55b6 100644 --- a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue +++ b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue @@ -5,25 +5,39 @@ :options="options" stacked @change="onChange" + :state="validateStateErrorOnly($v.value)" > <b-form-checkbox :value="otherOptionValue" >Other (please specify)</b-form-checkbox > + + <b-form-invalid-feedback :state="validateState($v.value)" + >This field is required.</b-form-invalid-feedback + > </b-form-checkbox-group> <b-form-input class="mt-2" v-if="showOther" v-model="other" placeholder="Please specify" + :state="validateState($v.other)" + @input="onInput" /> + <b-form-invalid-feedback :state="validateState($v.other)" + >Please specify a value for 'Other'.</b-form-invalid-feedback + > </extended-user-profile-field-editor> </template> <script> import { mapGetters, mapMutations } from "vuex"; +import { validationMixin } from "vuelidate"; +import { required } from "vuelidate/lib/validators"; +import { errors } from "django-airavata-common-ui"; import ExtendedUserProfileFieldEditor from "./ExtendedUserProfileFieldEditor.vue"; const OTHER_OPTION = new Object(); // sentinel value export default { + mixins: [validationMixin], components: { ExtendedUserProfileFieldEditor }, props: ["extendedUserProfileField"], data() { @@ -52,6 +66,7 @@ export default { value: values, id: this.extendedUserProfileField.id, }); + this.$v.value.$touch(); }, }, other: { @@ -63,6 +78,7 @@ export default { value, id: this.extendedUserProfileField.id, }); + this.$v.other.$touch(); }, }, showOther() { @@ -82,6 +98,21 @@ export default { otherOptionValue() { return OTHER_OPTION; }, + valid() { + return !this.$v.$invalid; + }, + }, + validations() { + const validations = { + value: { + required, + }, + other: {}, + }; + if (this.showOther) { + validations.other = { required }; + } + return validations; }, methods: { ...mapMutations("extendedUserProfile", [ @@ -94,6 +125,14 @@ export default { this.other = ""; } }, + onInput() { + // Handle case where initially there is an other value. If the user + // deletes the other value, then we still want to keep the other text box + // until the user unchecks the other option. + this.otherOptionSelected = true; + }, + validateState: errors.vuelidateHelpers.validateState, + validateStateErrorOnly: errors.vuelidateHelpers.validateStateErrorOnly, }, }; </script> diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue index 524fee70..0324b753 100644 --- a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue +++ b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue @@ -1,25 +1,48 @@ <template> <extended-user-profile-field-editor v-bind="$props"> - <b-form-select v-model="value" :options="options" @change="onChange"> + <b-form-select + v-model="value" + :options="options" + @change="onChange" + :state="validateStateErrorOnly($v.value)" + > + <template #first> + <b-form-select-option :value="null" disabled + >-- Please select an option --</b-form-select-option + > + </template> + <b-form-select-option :value="otherOptionValue" >Other (please specify)</b-form-select-option > </b-form-select> + <b-form-invalid-feedback :state="validateState($v.value)" + >This field is required.</b-form-invalid-feedback + > <b-form-input class="mt-2" v-if="showOther" v-model="other" placeholder="Please specify" + :state="validateState($v.other)" + @input="onInput" /> + <b-form-invalid-feedback :state="validateState($v.other)" + >Please specify a value for 'Other'.</b-form-invalid-feedback + > </extended-user-profile-field-editor> </template> <script> import { mapGetters, mapMutations } from "vuex"; +import { validationMixin } from "vuelidate"; +import { required } from "vuelidate/lib/validators"; +import { errors } from "django-airavata-common-ui"; import ExtendedUserProfileFieldEditor from "./ExtendedUserProfileFieldEditor.vue"; const OTHER_OPTION = new Object(); // sentinel value export default { + mixins: [validationMixin], components: { ExtendedUserProfileFieldEditor }, props: ["extendedUserProfileField"], data() { @@ -46,6 +69,7 @@ export default { value, id: this.extendedUserProfileField.id, }); + this.$v.value.$touch(); } }, }, @@ -58,6 +82,7 @@ export default { value, id: this.extendedUserProfileField.id, }); + this.$v.other.$touch(); }, }, showOther() { @@ -78,6 +103,21 @@ export default { otherOptionValue() { return OTHER_OPTION; }, + valid() { + return !this.$v.$invalid; + }, + }, + validations() { + const validations = { + value: {}, + other: {}, + }; + if (this.showOther) { + validations.other = { required }; + } else { + validations.value = { required }; + } + return validations; }, methods: { ...mapMutations("extendedUserProfile", [ @@ -87,6 +127,14 @@ export default { onChange(value) { this.otherOptionSelected = value === this.otherOptionValue; }, + onInput() { + // Handle case where initially there is an other value. If the user + // deletes the other value, then we still want to keep the other text box + // until the user unchecks the other option. + this.otherOptionSelected = true; + }, + validateState: errors.vuelidateHelpers.validateState, + validateStateErrorOnly: errors.vuelidateHelpers.validateStateErrorOnly, }, }; </script> diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue index c9a96488..284ac65d 100644 --- a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue +++ b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue @@ -1,13 +1,20 @@ <template> <extended-user-profile-field-editor v-bind="$props"> - <b-form-input v-model="value" /> + <b-form-input v-model="value" :state="validateState($v.value)" /> + <b-form-invalid-feedback :state="validateState($v.value)" + >This field is required.</b-form-invalid-feedback + > </extended-user-profile-field-editor> </template> <script> import { mapGetters, mapMutations } from "vuex"; +import { validationMixin } from "vuelidate"; +import { required } from "vuelidate/lib/validators"; +import { errors } from "django-airavata-common-ui"; import ExtendedUserProfileFieldEditor from "./ExtendedUserProfileFieldEditor.vue"; export default { + mixins: [validationMixin], components: { ExtendedUserProfileFieldEditor }, props: ["extendedUserProfileField"], computed: { @@ -18,11 +25,23 @@ export default { }, set(value) { this.setTextValue({ value, id: this.extendedUserProfileField.id }); + this.$v.$touch(); }, }, + valid() { + return !this.$v.$invalid; + }, + }, + validations() { + return { + value: { + required, + }, + }; }, methods: { ...mapMutations("extendedUserProfile", ["setTextValue"]), + validateState: errors.vuelidateHelpers.validateState, }, }; </script> diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue index f21965be..9d1c6ffa 100644 --- a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue +++ b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue @@ -1,15 +1,29 @@ <template> <extended-user-profile-field-editor v-bind="$props"> - <b-form-checkbox v-model="value" :unchecked-value="false"> + <b-form-checkbox + v-model="value" + :unchecked-value="false" + :value="true" + :state="validateStateErrorOnly($v.value)" + > {{ extendedUserProfileField.checkbox_label }} </b-form-checkbox> + <b-form-invalid-feedback :state="validateState($v.value)" + >This field is required.</b-form-invalid-feedback + > </extended-user-profile-field-editor> </template> <script> import { mapGetters, mapMutations } from "vuex"; +import { validationMixin } from "vuelidate"; +import { errors } from "django-airavata-common-ui"; import ExtendedUserProfileFieldEditor from "./ExtendedUserProfileFieldEditor.vue"; + +const mustBeTrue = (value) => value === true; + export default { + mixins: [validationMixin], components: { ExtendedUserProfileFieldEditor }, props: ["extendedUserProfileField"], computed: { @@ -23,11 +37,25 @@ export default { value, id: this.extendedUserProfileField.id, }); + this.$v.value.$touch(); }, }, + valid() { + return !this.$v.$invalid; + }, + }, + validations() { + const validations = { + value: { + mustBeTrue, + }, + }; + return validations; }, methods: { ...mapMutations("extendedUserProfile", ["setUserAgreementValue"]), + validateState: errors.vuelidateHelpers.validateState, + validateStateErrorOnly: errors.vuelidateHelpers.validateStateErrorOnly, }, }; </script> diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/store/modules/extendedUserProfile.js b/django_airavata/apps/auth/static/django_airavata_auth/js/store/modules/extendedUserProfile.js index d2163890..6e264490 100644 --- a/django_airavata/apps/auth/static/django_airavata_auth/js/store/modules/extendedUserProfile.js +++ b/django_airavata/apps/auth/static/django_airavata_auth/js/store/modules/extendedUserProfile.js @@ -50,7 +50,7 @@ const getters = { const value = state.extendedUserProfileValues.find( (v) => v.ext_user_profile_field === id ); - return value && value.agreement_value; + return value ? value.agreement_value : false; }, }; diff --git a/django_airavata/static/common/js/errors/vuelidateHelpers.js b/django_airavata/static/common/js/errors/vuelidateHelpers.js index 3f431834..1332e77d 100644 --- a/django_airavata/static/common/js/errors/vuelidateHelpers.js +++ b/django_airavata/static/common/js/errors/vuelidateHelpers.js @@ -2,3 +2,17 @@ export function validateState(validation) { const { $dirty, $error } = validation; return $dirty ? !$error : null; } + +/** + * Return false if there is a validation error, null otherwise. + * + * This is just like validateState except it doesn't return true when valid + * which is useful if you only want to show invalid feedback. + * + * @param {*} validation + * @returns + */ +export function validateStateErrorOnly(validation) { + const { $dirty, $error } = validation; + return $dirty && $error ? false : null; +}
