diff options
Diffstat (limited to 'app/assets/javascripts/webhooks/components/form_url_app.vue')
-rw-r--r-- | app/assets/javascripts/webhooks/components/form_url_app.vue | 92 |
1 files changed, 88 insertions, 4 deletions
diff --git a/app/assets/javascripts/webhooks/components/form_url_app.vue b/app/assets/javascripts/webhooks/components/form_url_app.vue index 5ec16d4ba15..4fafeff8804 100644 --- a/app/assets/javascripts/webhooks/components/form_url_app.vue +++ b/app/assets/javascripts/webhooks/components/form_url_app.vue @@ -1,7 +1,8 @@ <script> -import { isEmpty } from 'lodash'; +import { cloneDeep, isEmpty } from 'lodash'; import { GlFormGroup, GlFormInput, GlFormRadio, GlFormRadioGroup, GlLink } from '@gitlab/ui'; import { __, s__ } from '~/locale'; +import { scrollToElement } from '~/lib/utils/common_utils'; import FormUrlMaskItem from './form_url_mask_item.vue'; @@ -30,10 +31,15 @@ export default { return { maskEnabled: !isEmpty(this.initialUrlVariables), url: this.initialUrl, - items: isEmpty(this.initialUrlVariables) ? [{}] : this.initialUrlVariables, + items: this.getInitialItems(), + isValidated: false, + formEl: null, }; }, computed: { + urlState() { + return !this.isValidated || !isEmpty(this.url); + }, maskedUrl() { if (!this.url) { return null; @@ -46,14 +52,83 @@ export default { return; } - const replacementExpression = new RegExp(value, 'g'); - maskedUrl = maskedUrl.replace(replacementExpression, `{${key}}`); + maskedUrl = this.maskUrl(maskedUrl, key, value); }); return maskedUrl; }, }, + mounted() { + this.formEl = document.querySelector('.js-webhook-form'); + + this.formEl?.addEventListener('submit', this.handleSubmit); + }, + destroy() { + this.formEl?.removeEventListener('submit', this.handleSubmit); + }, methods: { + getInitialItems() { + return isEmpty(this.initialUrlVariables) ? [{}] : cloneDeep(this.initialUrlVariables); + }, + isEditingItem(index, key) { + if (isEmpty(this.initialUrlVariables)) { + return false; + } + + const item = this.initialUrlVariables[index]; + return item && item.key === key; + }, + keyInvalidFeedback(key) { + if (this.isValidated && isEmpty(key)) { + return this.$options.i18n.inputRequired; + } + + return null; + }, + valueInvalidFeedback(index, key, value) { + if (this.isEditingItem(index, key)) { + return null; + } + + if (this.isValidated && isEmpty(value)) { + return this.$options.i18n.inputRequired; + } + + if (!isEmpty(value) && !this.url?.includes(value)) { + return this.$options.i18n.valuePartOfUrl; + } + + return null; + }, + isValid() { + this.isValidated = true; + + if (!this.urlState) { + return false; + } + + if ( + this.maskEnabled && + this.items.some( + ({ key, value }, index) => + this.keyInvalidFeedback(key) || this.valueInvalidFeedback(index, key, value), + ) + ) { + return false; + } + + return true; + }, + handleSubmit(e) { + if (!this.isValid()) { + scrollToElement(this.$refs.formUrl.$el); + e.preventDefault(); + e.stopPropagation(); + } + }, + maskUrl(url, key, value) { + return url.split(value).join(`{${key}}`); + }, onItemInput({ index, key, value }) { this.$set(this.items, index, { key, value }); }, @@ -66,6 +141,7 @@ export default { }, i18n: { addItem: s__('Webhooks|+ Mask another portion of URL'), + inputRequired: __('This field is required.'), radioFullUrlText: s__('Webhooks|Show full URL'), radioMaskUrlText: s__('Webhooks|Mask portions of URL'), radioMaskUrlHelp: s__('Webhooks|Do not show sensitive data such as tokens in the UI.'), @@ -75,6 +151,7 @@ export default { urlLabel: __('URL'), urlPlaceholder: 'http://example.com/trigger-ci.json', urlPreview: s__('Webhooks|URL preview'), + valuePartOfUrl: s__('Webhooks|Must match part of URL'), }, }; </script> @@ -82,14 +159,18 @@ export default { <template> <div> <gl-form-group + ref="formUrl" :label="$options.i18n.urlLabel" label-for="webhook-url" :description="$options.i18n.urlDescription" + :invalid-feedback="$options.i18n.inputRequired" + :state="urlState" > <gl-form-input id="webhook-url" v-model="url" name="hook[url]" + :state="urlState" :placeholder="$options.i18n.urlPlaceholder" data-testid="form-url" /> @@ -112,6 +193,9 @@ export default { :index="index" :item-key="key" :item-value="value" + :is-editing="isEditingItem(index, key)" + :key-invalid-feedback="keyInvalidFeedback(key)" + :value-invalid-feedback="valueInvalidFeedback(index, key, value)" @input="onItemInput" @remove="removeItem" /> |