diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 01:45:44 +0000 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /app/assets/javascripts/integrations | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) | |
download | gitlab-ce-85dc423f7090da0a52c73eb66faf22ddb20efff9.tar.gz |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'app/assets/javascripts/integrations')
14 files changed, 189 insertions, 133 deletions
diff --git a/app/assets/javascripts/integrations/edit/components/active_toggle.vue b/app/assets/javascripts/integrations/edit/components/active_checkbox.vue index e6a96600539..6698984d02f 100644 --- a/app/assets/javascripts/integrations/edit/components/active_toggle.vue +++ b/app/assets/javascripts/integrations/edit/components/active_checkbox.vue @@ -1,36 +1,31 @@ <script> import { mapGetters } from 'vuex'; -import { GlFormGroup, GlToggle } from '@gitlab/ui'; +import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui'; import eventHub from '../event_hub'; export default { - name: 'ActiveToggle', + name: 'ActiveCheckbox', components: { GlFormGroup, - GlToggle, - }, - props: { - initialActivated: { - type: Boolean, - required: true, - }, + GlFormCheckbox, }, data() { return { - activated: this.initialActivated, + activated: false, }; }, computed: { - ...mapGetters(['isInheriting']), + ...mapGetters(['isInheriting', 'propsSource']), }, mounted() { + this.activated = this.propsSource.initialActivated; // Initialize view this.$nextTick(() => { - this.onToggle(this.activated); + this.onChange(this.activated); }); }, methods: { - onToggle(e) { + onChange(e) { eventHub.$emit('toggle', e); }, }, @@ -39,12 +34,15 @@ export default { <template> <gl-form-group :label="__('Enable integration')" label-for="service[active]"> - <gl-toggle + <input name="service[active]" type="hidden" :value="activated || false" /> + <gl-form-checkbox v-model="activated" name="service[active]" class="gl-display-block gl-line-height-0" :disabled="isInheriting" - @change="onToggle" - /> + @change="onChange" + > + {{ __('Active') }} + </gl-form-checkbox> </gl-form-group> </template> diff --git a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue index 090381b8da4..9dde1ed1055 100644 --- a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue +++ b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapGetters } from 'vuex'; import { capitalize, lowerCase, isEmpty } from 'lodash'; import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui'; diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue index 5088664c3bd..0460ed6791e 100644 --- a/app/assets/javascripts/integrations/edit/components/integration_form.vue +++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue @@ -1,9 +1,11 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex'; +import { GlButton } from '@gitlab/ui'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import eventHub from '../event_hub'; import OverrideDropdown from './override_dropdown.vue'; -import ActiveToggle from './active_toggle.vue'; +import ActiveCheckbox from './active_checkbox.vue'; import JiraTriggerFields from './jira_trigger_fields.vue'; import JiraIssuesFields from './jira_issues_fields.vue'; import TriggerFields from './trigger_fields.vue'; @@ -13,16 +15,20 @@ export default { name: 'IntegrationForm', components: { OverrideDropdown, - ActiveToggle, + ActiveCheckbox, JiraTriggerFields, JiraIssuesFields, TriggerFields, DynamicField, + GlButton, }, mixins: [glFeatureFlagsMixin()], computed: { - ...mapGetters(['currentKey', 'propsSource']), - ...mapState(['adminState', 'override']), + ...mapGetters(['currentKey', 'propsSource', 'isSavingOrTesting']), + ...mapState(['defaultState', 'override', 'isSaving', 'isTesting']), + isEditable() { + return this.propsSource.editable; + }, isJira() { return this.propsSource.type === 'jira'; }, @@ -31,7 +37,15 @@ export default { }, }, methods: { - ...mapActions(['setOverride']), + ...mapActions(['setOverride', 'setIsSaving', 'setIsTesting']), + onSaveClick() { + this.setIsSaving(true); + eventHub.$emit('saveIntegration'); + }, + onTestClick() { + this.setIsTesting(true); + eventHub.$emit('testIntegration'); + }, }, }; </script> @@ -39,16 +53,13 @@ export default { <template> <div> <override-dropdown - v-if="adminState !== null" - :inherit-from-id="adminState.id" + v-if="defaultState !== null" + :inherit-from-id="defaultState.id" :override="override" + :learn-more-path="propsSource.learnMorePath" @change="setOverride" /> - <active-toggle - v-if="propsSource.showActive" - :key="`${currentKey}-active-toggle`" - v-bind="propsSource.activeToggleProps" - /> + <active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" /> <jira-trigger-fields v-if="isJira" :key="`${currentKey}-jira-trigger-fields`" @@ -70,5 +81,29 @@ export default { :key="`${currentKey}-jira-issues-fields`" v-bind="propsSource.jiraIssuesProps" /> + <div v-if="isEditable" class="footer-block row-content-block"> + <gl-button + category="primary" + variant="success" + type="submit" + :loading="isSaving" + :disabled="isSavingOrTesting" + data-qa-selector="save_changes_button" + @click.prevent="onSaveClick" + > + {{ __('Save changes') }} + </gl-button> + <gl-button + v-if="propsSource.canTest" + :loading="isTesting" + :disabled="isSavingOrTesting" + :href="propsSource.testPath" + @click.prevent="onTestClick" + > + {{ __('Test settings') }} + </gl-button> + + <gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button> + </div> </div> </template> diff --git a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue index 5a1f86718b0..1baa2b440b0 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue @@ -37,6 +37,11 @@ export default { required: false, default: null, }, + gitlabIssuesEnabled: { + type: Boolean, + required: false, + default: true, + }, upgradePlanPath: { type: String, required: false, @@ -133,7 +138,7 @@ export default { :disabled="!enableJiraIssues" /> </gl-form-group> - <p> + <p v-if="gitlabIssuesEnabled"> <gl-sprintf :message=" s__( diff --git a/app/assets/javascripts/integrations/edit/components/override_dropdown.vue b/app/assets/javascripts/integrations/edit/components/override_dropdown.vue index accfc26974c..c31dada8d2f 100644 --- a/app/assets/javascripts/integrations/edit/components/override_dropdown.vue +++ b/app/assets/javascripts/integrations/edit/components/override_dropdown.vue @@ -1,6 +1,8 @@ <script> -import { GlNewDropdown, GlNewDropdownItem } from '@gitlab/ui'; +import { mapState } from 'vuex'; +import { GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui'; import { s__ } from '~/locale'; +import { defaultIntegrationLevel, overrideDropdownDescriptions } from '../constants'; const dropdownOptions = [ { @@ -17,14 +19,20 @@ export default { dropdownOptions, name: 'OverrideDropdown', components: { - GlNewDropdown, - GlNewDropdownItem, + GlDropdown, + GlDropdownItem, + GlLink, }, props: { inheritFromId: { type: Number, required: true, }, + learnMorePath: { + type: String, + required: false, + default: null, + }, override: { type: Boolean, required: true, @@ -35,6 +43,16 @@ export default { selected: dropdownOptions.find(x => x.value === this.override), }; }, + computed: { + ...mapState(['defaultState']), + description() { + const level = this.defaultState.integrationLevel; + + return ( + overrideDropdownDescriptions[level] || overrideDropdownDescriptions[defaultIntegrationLevel] + ); + }, + }, methods: { onClick(option) { this.selected = option; @@ -48,16 +66,21 @@ export default { <div class="gl-display-flex gl-justify-content-space-between gl-align-items-baseline gl-py-4 gl-mt-5 gl-mb-6 gl-border-t-1 gl-border-t-solid gl-border-b-1 gl-border-b-solid gl-border-gray-100" > - <span>{{ s__('Integrations|Default settings are inherited from the instance level.') }}</span> + <span + >{{ description }} + <gl-link v-if="learnMorePath" :href="learnMorePath" target="_blank">{{ + __('Learn more') + }}</gl-link> + </span> <input name="service[inherit_from_id]" :value="override ? '' : inheritFromId" type="hidden" /> - <gl-new-dropdown :text="selected.text"> - <gl-new-dropdown-item + <gl-dropdown :text="selected.text"> + <gl-dropdown-item v-for="option in $options.dropdownOptions" :key="option.value" @click="onClick(option)" > {{ option.text }} - </gl-new-dropdown-item> - </gl-new-dropdown> + </gl-dropdown-item> + </gl-dropdown> </div> </template> diff --git a/app/assets/javascripts/integrations/edit/constants.js b/app/assets/javascripts/integrations/edit/constants.js new file mode 100644 index 00000000000..b74ae209eb7 --- /dev/null +++ b/app/assets/javascripts/integrations/edit/constants.js @@ -0,0 +1,17 @@ +import { s__ } from '~/locale'; + +export const integrationLevels = { + GROUP: 'group', + INSTANCE: 'instance', +}; + +export const defaultIntegrationLevel = integrationLevels.INSTANCE; + +export const overrideDropdownDescriptions = { + [integrationLevels.GROUP]: s__( + 'Integrations|Default settings are inherited from the group level.', + ), + [integrationLevels.INSTANCE]: s__( + 'Integrations|Default settings are inherited from the instance level.', + ), +}; diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js index ea5463832ce..248ee62d43a 100644 --- a/app/assets/javascripts/integrations/edit/index.js +++ b/app/assets/javascripts/integrations/edit/index.js @@ -19,27 +19,36 @@ function parseDatasetToProps(data) { projectKey, upgradePlanPath, editProjectPath, + learnMorePath, triggerEvents, fields, inheritFromId, + integrationLevel, + cancelPath, + testPath, ...booleanAttributes } = data; const { showActive, activated, + editable, + canTest, commitEvents, mergeRequestEvents, enableComments, showJiraIssuesIntegration, enableJiraIssues, + gitlabIssuesEnabled, } = parseBooleanInData(booleanAttributes); return { - activeToggleProps: { - initialActivated: activated, - }, + initialActivated: activated, showActive, type, + cancelPath, + editable, + canTest, + testPath, triggerFieldsProps: { initialTriggerCommit: commitEvents, initialTriggerMergeRequest: mergeRequestEvents, @@ -50,17 +59,20 @@ function parseDatasetToProps(data) { showJiraIssuesIntegration, initialEnableJiraIssues: enableJiraIssues, initialProjectKey: projectKey, + gitlabIssuesEnabled, upgradePlanPath, editProjectPath, }, + learnMorePath, triggerEvents: JSON.parse(triggerEvents), fields: JSON.parse(fields), inheritFromId: parseInt(inheritFromId, 10), + integrationLevel, id: parseInt(id, 10), }; } -export default (el, adminEl) => { +export default (el, defaultEl) => { if (!el) { return null; } @@ -68,12 +80,12 @@ export default (el, adminEl) => { const props = parseDatasetToProps(el.dataset); const initialState = { - adminState: null, + defaultState: null, customState: props, }; - if (adminEl) { - initialState.adminState = Object.freeze(parseDatasetToProps(adminEl.dataset)); + if (defaultEl) { + initialState.defaultState = Object.freeze(parseDatasetToProps(defaultEl.dataset)); } return new Vue({ diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js index 3decdaab55d..199c9074ead 100644 --- a/app/assets/javascripts/integrations/edit/store/actions.js +++ b/app/assets/javascripts/integrations/edit/store/actions.js @@ -1,4 +1,5 @@ import * as types from './mutation_types'; -// eslint-disable-next-line import/prefer-default-export export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override); +export const setIsSaving = ({ commit }, isSaving) => commit(types.SET_IS_SAVING, isSaving); +export const setIsTesting = ({ commit }, isTesting) => commit(types.SET_IS_TESTING, isTesting); diff --git a/app/assets/javascripts/integrations/edit/store/getters.js b/app/assets/javascripts/integrations/edit/store/getters.js index b68bd668980..4ee5f11855c 100644 --- a/app/assets/javascripts/integrations/edit/store/getters.js +++ b/app/assets/javascripts/integrations/edit/store/getters.js @@ -1,6 +1,8 @@ -export const isInheriting = state => (state.adminState === null ? false : !state.override); +export const isInheriting = state => (state.defaultState === null ? false : !state.override); + +export const isSavingOrTesting = state => state.isSaving || state.isTesting; export const propsSource = (state, getters) => - getters.isInheriting ? state.adminState : state.customState; + getters.isInheriting ? state.defaultState : state.customState; export const currentKey = (state, getters) => (getters.isInheriting ? 'admin' : 'custom'); diff --git a/app/assets/javascripts/integrations/edit/store/index.js b/app/assets/javascripts/integrations/edit/store/index.js index eea5e48780d..a8375f345c6 100644 --- a/app/assets/javascripts/integrations/edit/store/index.js +++ b/app/assets/javascripts/integrations/edit/store/index.js @@ -7,7 +7,6 @@ import createState from './state'; Vue.use(Vuex); -// eslint-disable-next-line import/prefer-default-export export const createStore = (initialState = {}) => new Vuex.Store({ actions, diff --git a/app/assets/javascripts/integrations/edit/store/mutation_types.js b/app/assets/javascripts/integrations/edit/store/mutation_types.js index 274afe3fb49..0dae8ea079e 100644 --- a/app/assets/javascripts/integrations/edit/store/mutation_types.js +++ b/app/assets/javascripts/integrations/edit/store/mutation_types.js @@ -1,2 +1,3 @@ -// eslint-disable-next-line import/prefer-default-export export const SET_OVERRIDE = 'SET_OVERRIDE'; +export const SET_IS_SAVING = 'SET_IS_SAVING'; +export const SET_IS_TESTING = 'SET_IS_TESTING'; diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js index 8757d415197..8ac3c476f9e 100644 --- a/app/assets/javascripts/integrations/edit/store/mutations.js +++ b/app/assets/javascripts/integrations/edit/store/mutations.js @@ -4,4 +4,10 @@ export default { [types.SET_OVERRIDE](state, override) { state.override = override; }, + [types.SET_IS_SAVING](state, isSaving) { + state.isSaving = isSaving; + }, + [types.SET_IS_TESTING](state, isTesting) { + state.isTesting = isTesting; + }, }; diff --git a/app/assets/javascripts/integrations/edit/store/state.js b/app/assets/javascripts/integrations/edit/store/state.js index 95c1a2be500..a9ecee6c539 100644 --- a/app/assets/javascripts/integrations/edit/store/state.js +++ b/app/assets/javascripts/integrations/edit/store/state.js @@ -1,9 +1,11 @@ -export default ({ adminState = null, customState = {} } = {}) => { - const override = adminState !== null ? adminState.id !== customState.inheritFromId : false; +export default ({ defaultState = null, customState = {} } = {}) => { + const override = defaultState !== null ? defaultState.id !== customState.inheritFromId : false; return { override, - adminState, + defaultState, customState, + isSaving: false, + isTesting: false, }; }; diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index 1135065b06c..1d0814125e6 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import axios from '../lib/utils/axios_utils'; -import { deprecatedCreateFlash as flash } from '../flash'; -import { __ } from '~/locale'; +import { __, s__ } from '~/locale'; +import toast from '~/vue_shared/plugins/global_toast'; import initForm from './edit'; import eventHub from './edit/event_hub'; @@ -10,65 +10,63 @@ export default class IntegrationSettingsForm { this.$form = $(formSelector); this.formActive = false; + this.vue = null; + // Form Metadata - this.canTestService = this.$form.data('canTest'); this.testEndPoint = this.$form.data('testUrl'); - - // Form Child Elements - this.$submitBtn = this.$form.find('button[type="submit"]'); - this.$submitBtnLoader = this.$submitBtn.find('.js-btn-spinner'); - this.$submitBtnLabel = this.$submitBtn.find('.js-btn-label'); } init() { // Init Vue component - initForm( + this.vue = initForm( document.querySelector('.js-vue-integration-settings'), - document.querySelector('.js-vue-admin-integration-settings'), + document.querySelector('.js-vue-default-integration-settings'), ); eventHub.$on('toggle', active => { this.formActive = active; - this.handleServiceToggle(); + this.toggleServiceState(); + }); + eventHub.$on('testIntegration', () => { + this.testIntegration(); + }); + eventHub.$on('saveIntegration', () => { + this.saveIntegration(); }); - - // Bind Event Listeners - this.$submitBtn.on('click', e => this.handleSettingsSave(e)); } - handleSettingsSave(e) { - // Check if Service is marked active, as if not marked active, - // We can skip testing it and directly go ahead to allow form to - // be submitted - if (!this.formActive) { - return; + saveIntegration() { + // Service was marked active so now we check; + // 1) If form contents are valid + // 2) If this service can be saved + // If both conditions are true, we override form submission + // and save the service using provided configuration. + if (this.$form.get(0).checkValidity()) { + this.$form.submit(); + } else { + eventHub.$emit('validateForm'); + this.vue.$store.dispatch('setIsSaving', false); } + } + testIntegration() { // Service was marked active so now we check; // 1) If form contents are valid // 2) If this service can be tested // If both conditions are true, we override form submission // and test the service using provided configuration. if (this.$form.get(0).checkValidity()) { - if (this.canTestService) { - e.preventDefault(); - // eslint-disable-next-line no-jquery/no-serialize - this.testSettings(this.$form.serialize()); - } + // eslint-disable-next-line no-jquery/no-serialize + this.testSettings(this.$form.serialize()); } else { - e.preventDefault(); eventHub.$emit('validateForm'); + this.vue.$store.dispatch('setIsTesting', false); } } - handleServiceToggle() { - this.toggleServiceState(); - } - /** * Change Form's validation enforcement based on service status (active/inactive) */ toggleServiceState() { - this.toggleSubmitBtnLabel(); if (this.formActive) { this.$form.removeAttr('novalidate'); } else if (!this.$form.attr('novalidate')) { @@ -77,67 +75,23 @@ export default class IntegrationSettingsForm { } /** - * Toggle Submit button label based on Integration status and ability to test service - */ - toggleSubmitBtnLabel() { - let btnLabel = __('Save changes'); - - if (this.formActive && this.canTestService) { - btnLabel = __('Test settings and save changes'); - } - - this.$submitBtnLabel.text(btnLabel); - } - - /** - * Toggle Submit button state based on provided boolean value of `saveTestActive` - * When enabled, it does two things, and reverts back when disabled - * - * 1. It shows load spinner on submit button - * 2. Makes submit button disabled - */ - toggleSubmitBtnState(saveTestActive) { - if (saveTestActive) { - this.$submitBtn.disable(); - this.$submitBtnLoader.removeClass('hidden'); - } else { - this.$submitBtn.enable(); - this.$submitBtnLoader.addClass('hidden'); - } - } - - /** * Test Integration config */ testSettings(formData) { - this.toggleSubmitBtnState(true); - return axios .put(this.testEndPoint, formData) .then(({ data }) => { if (data.error) { - let flashActions; - - if (data.test_failed) { - flashActions = { - title: __('Save anyway'), - clickHandler: e => { - e.preventDefault(); - this.$form.submit(); - }, - }; - } - - flash(`${data.message} ${data.service_response}`, 'alert', document, flashActions); + toast(`${data.message} ${data.service_response}`); } else { - this.$form.submit(); + toast(s__('Integrations|Connection successful.')); } - - this.toggleSubmitBtnState(false); }) .catch(() => { - flash(__('Something went wrong on our end.')); - this.toggleSubmitBtnState(false); + toast(__('Something went wrong on our end.')); + }) + .finally(() => { + this.vue.$store.dispatch('setIsTesting', false); }); } } |