You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2022/05/10 21:30:43 UTC
[airavata-django-portal] 01/04: AIRAVATA-3565 Add validation to extended user profile editor
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 <ma...@apache.org>
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;
+}