diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-20 09:16:11 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-20 09:16:11 +0000 |
commit | edaa33dee2ff2f7ea3fac488d41558eb5f86d68c (patch) | |
tree | 11f143effbfeba52329fb7afbd05e6e2a3790241 /app/assets/javascripts/integrations | |
parent | d8a5691316400a0f7ec4f83832698f1988eb27c1 (diff) | |
download | gitlab-ce-edaa33dee2ff2f7ea3fac488d41558eb5f86d68c.tar.gz |
Add latest changes from gitlab-org/gitlab@14-7-stable-eev14.7.0-rc42
Diffstat (limited to 'app/assets/javascripts/integrations')
11 files changed, 176 insertions, 84 deletions
diff --git a/app/assets/javascripts/integrations/constants.js b/app/assets/javascripts/integrations/constants.js index 84656bd41bb..b90658fb13c 100644 --- a/app/assets/javascripts/integrations/constants.js +++ b/app/assets/javascripts/integrations/constants.js @@ -23,3 +23,8 @@ export const I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE = s__( ); export const I18N_DEFAULT_ERROR_MESSAGE = __('Something went wrong on our end.'); export const I18N_SUCCESSFUL_CONNECTION_MESSAGE = s__('Integrations|Connection successful.'); + +export const settingsTabTitle = __('Settings'); +export const overridesTabTitle = s__('Integrations|Projects using custom settings'); + +export const INTEGRATION_FORM_SELECTOR = '.js-integration-settings-form'; diff --git a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue index 258cd1bf365..4b0579a5beb 100644 --- a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue +++ b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue @@ -153,7 +153,7 @@ export default { :invalid-feedback="__('This field is required.')" :state="valid" > - <template #description> + <template v-if="!isCheckbox" #description> <span v-safe-html:[$options.helpHtmlConfig]="help"></span> </template> @@ -161,6 +161,9 @@ export default { <input :name="fieldName" type="hidden" :value="model || false" /> <gl-form-checkbox :id="fieldId" v-model="model" :disabled="isInheriting"> {{ checkboxLabel || humanizedTitle }} + <template #help> + <span v-safe-html:[$options.helpHtmlConfig]="help"></span> + </template> </gl-form-checkbox> </template> <template v-else-if="isSelect"> diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue index e570a468944..c3cc35adfa5 100644 --- a/app/assets/javascripts/integrations/edit/components/integration_form.vue +++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue @@ -1,5 +1,6 @@ <script> -import { GlButton, GlModalDirective, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { GlButton, GlModalDirective, GlSafeHtmlDirective as SafeHtml, GlForm } from '@gitlab/ui'; +import axios from 'axios'; import * as Sentry from '@sentry/browser'; import { mapState, mapActions, mapGetters } from 'vuex'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -8,8 +9,11 @@ import { I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE, I18N_DEFAULT_ERROR_MESSAGE, I18N_SUCCESSFUL_CONNECTION_MESSAGE, + INTEGRATION_FORM_SELECTOR, integrationLevels, } from '~/integrations/constants'; +import { refreshCurrentPage } from '~/lib/utils/url_utility'; +import csrf from '~/lib/utils/csrf'; import eventHub from '../event_hub'; import { testIntegrationSettings } from '../api'; import ActiveCheckbox from './active_checkbox.vue'; @@ -33,6 +37,7 @@ export default { ConfirmationModal, ResetConfirmationModal, GlButton, + GlForm, }, directives: { GlModal: GlModalDirective, @@ -40,10 +45,6 @@ export default { }, mixins: [glFeatureFlagsMixin()], props: { - formSelector: { - type: String, - required: true, - }, helpHtml: { type: String, required: false, @@ -55,11 +56,12 @@ export default { integrationActive: false, isTesting: false, isSaving: false, + isResetting: false, }; }, computed: { ...mapGetters(['currentKey', 'propsSource']), - ...mapState(['defaultState', 'customState', 'override', 'isResetting']), + ...mapState(['defaultState', 'customState', 'override']), isEditable() { return this.propsSource.editable; }, @@ -81,10 +83,28 @@ export default { disableButtons() { return Boolean(this.isSaving || this.isResetting || this.isTesting); }, + useVueForm() { + return this.glFeatures?.vueIntegrationForm; + }, + formContainerProps() { + return this.useVueForm + ? { + ref: 'integrationForm', + method: 'post', + class: 'gl-mb-3 gl-show-field-errors integration-settings-form', + action: this.propsSource.formPath, + novalidate: !this.integrationActive, + } + : {}; + }, + formContainer() { + return this.useVueForm ? GlForm : 'div'; + }, }, mounted() { - // this form element is defined in Haml - this.form = document.querySelector(this.formSelector); + this.form = this.useVueForm + ? this.$refs.integrationForm.$el + : document.querySelector(INTEGRATION_FORM_SELECTOR); }, methods: { ...mapActions(['setOverride', 'fetchResetIntegration', 'requestJiraIssueTypes']), @@ -126,7 +146,20 @@ export default { }); }, onResetClick() { - this.fetchResetIntegration(); + this.isResetting = true; + + return axios + .post(this.propsSource.resetPath) + .then(() => { + refreshCurrentPage(); + }) + .catch((error) => { + this.$toast.show(I18N_DEFAULT_ERROR_MESSAGE); + Sentry.captureException(error); + }) + .finally(() => { + this.isResetting = false; + }); }, onRequestJiraIssueTypes() { this.requestJiraIssueTypes(this.getFormData()); @@ -136,7 +169,7 @@ export default { }, onToggleIntegrationState(integrationActive) { this.integrationActive = integrationActive; - if (!this.form) { + if (!this.form || this.useVueForm) { return; } @@ -153,11 +186,23 @@ export default { ADD_TAGS: ['use'], // to support icon SVGs FORBID_ATTR: [], // This is trusted input so we can override the default config to allow data-* attributes }, + csrf, }; </script> <template> - <div class="gl-mb-3"> + <component :is="formContainer" v-bind="formContainerProps"> + <template v-if="useVueForm"> + <input type="hidden" name="_method" value="put" /> + <input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> + <input + type="hidden" + name="redirect_to" + :value="propsSource.redirectTo" + data-testid="redirect-to-field" + /> + </template> + <override-dropdown v-if="defaultState !== null" :inherit-from-id="defaultState.id" @@ -200,63 +245,71 @@ export default { v-bind="propsSource.jiraIssuesProps" @request-jira-issue-types="onRequestJiraIssueTypes" /> - <div v-if="isEditable" class="footer-block row-content-block"> - <template v-if="isInstanceOrGroupLevel"> + + <div + v-if="isEditable" + class="footer-block row-content-block gl-display-flex gl-justify-content-space-between" + > + <div> + <template v-if="isInstanceOrGroupLevel"> + <gl-button + v-gl-modal.confirmSaveIntegration + category="primary" + variant="confirm" + :loading="isSaving" + :disabled="disableButtons" + data-testid="save-button-instance-group" + data-qa-selector="save_changes_button" + > + {{ __('Save changes') }} + </gl-button> + <confirmation-modal @submit="onSaveClick" /> + </template> <gl-button - v-gl-modal.confirmSaveIntegration + v-else category="primary" variant="confirm" + type="submit" :loading="isSaving" :disabled="disableButtons" + data-testid="save-button" data-qa-selector="save_changes_button" + @click.prevent="onSaveClick" > {{ __('Save changes') }} </gl-button> - <confirmation-modal @submit="onSaveClick" /> - </template> - <gl-button - v-else - category="primary" - variant="confirm" - type="submit" - :loading="isSaving" - :disabled="disableButtons" - data-testid="save-button" - data-qa-selector="save_changes_button" - @click.prevent="onSaveClick" - > - {{ __('Save changes') }} - </gl-button> - <gl-button - v-if="showTestButton" - category="secondary" - variant="confirm" - :loading="isTesting" - :disabled="disableButtons" - data-testid="test-button" - @click.prevent="onTestClick" - > - {{ __('Test settings') }} - </gl-button> + <gl-button + v-if="showTestButton" + category="secondary" + variant="confirm" + :loading="isTesting" + :disabled="disableButtons" + data-testid="test-button" + @click.prevent="onTestClick" + > + {{ __('Test settings') }} + </gl-button> + + <gl-button :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button> + </div> <template v-if="showResetButton"> <gl-button v-gl-modal.confirmResetIntegration - category="secondary" - variant="confirm" + category="tertiary" + variant="danger" :loading="isResetting" :disabled="disableButtons" data-testid="reset-button" > {{ __('Reset') }} </gl-button> + <reset-confirmation-modal @reset="onResetClick" /> </template> - - <gl-button :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button> </div> </div> </div> - </div> + </component> </template> diff --git a/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue b/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue index 5a445235219..403bad3db11 100644 --- a/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue +++ b/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue @@ -11,7 +11,7 @@ export default { primaryProps() { return { text: __('Reset'), - attributes: [{ variant: 'warning' }, { category: 'primary' }], + attributes: [{ variant: 'danger' }, { category: 'primary' }], }; }, cancelProps() { diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js index 9c9e3edbeb8..fbda8c1e3d0 100644 --- a/app/assets/javascripts/integrations/edit/index.js +++ b/app/assets/javascripts/integrations/edit/index.js @@ -28,9 +28,11 @@ function parseDatasetToProps(data) { cancelPath, testPath, resetPath, + formPath, vulnerabilitiesIssuetype, jiraIssueTransitionAutomatic, jiraIssueTransitionId, + redirectTo, ...booleanAttributes } = data; const { @@ -57,6 +59,7 @@ function parseDatasetToProps(data) { canTest, testPath, resetPath, + formPath, triggerFieldsProps: { initialTriggerCommit: commitEvents, initialTriggerMergeRequest: mergeRequestEvents, @@ -82,10 +85,11 @@ function parseDatasetToProps(data) { inheritFromId: parseInt(inheritFromId, 10), integrationLevel, id: parseInt(id, 10), + redirectTo, }; } -export default function initIntegrationSettingsForm(formSelector) { +export default function initIntegrationSettingsForm() { const customSettingsEl = document.querySelector('.js-vue-integration-settings'); const defaultSettingsEl = document.querySelector('.js-vue-default-integration-settings'); @@ -115,7 +119,6 @@ export default function initIntegrationSettingsForm(formSelector) { return createElement(IntegrationForm, { props: { helpHtml, - formSelector, }, }); }, diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js index 97565a3a69c..1398b710d1d 100644 --- a/app/assets/javascripts/integrations/edit/store/actions.js +++ b/app/assets/javascripts/integrations/edit/store/actions.js @@ -1,5 +1,3 @@ -import axios from 'axios'; -import { refreshCurrentPage } from '~/lib/utils/url_utility'; import { VALIDATE_INTEGRATION_FORM_EVENT, I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE, @@ -10,27 +8,6 @@ import eventHub from '../event_hub'; import * as types from './mutation_types'; export const setOverride = ({ commit }, override) => commit(types.SET_OVERRIDE, override); -export const setIsResetting = ({ commit }, isResetting) => - commit(types.SET_IS_RESETTING, isResetting); - -export const requestResetIntegration = ({ commit }) => { - commit(types.REQUEST_RESET_INTEGRATION); -}; -export const receiveResetIntegrationSuccess = () => { - refreshCurrentPage(); -}; -export const receiveResetIntegrationError = ({ commit }) => { - commit(types.RECEIVE_RESET_INTEGRATION_ERROR); -}; - -export const fetchResetIntegration = ({ dispatch, getters }) => { - dispatch('requestResetIntegration'); - - return axios - .post(getters.propsSource.resetPath, { params: { format: 'json' } }) - .then(() => dispatch('receiveResetIntegrationSuccess')) - .catch(() => dispatch('receiveResetIntegrationError')); -}; export const requestJiraIssueTypes = ({ commit, dispatch, getters }, formData) => { commit(types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, ''); diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js index e7e312ce650..6ca644f8821 100644 --- a/app/assets/javascripts/integrations/edit/store/mutations.js +++ b/app/assets/javascripts/integrations/edit/store/mutations.js @@ -4,15 +4,6 @@ export default { [types.SET_OVERRIDE](state, override) { state.override = override; }, - [types.SET_IS_RESETTING](state, isResetting) { - state.isResetting = isResetting; - }, - [types.REQUEST_RESET_INTEGRATION](state) { - state.isResetting = true; - }, - [types.RECEIVE_RESET_INTEGRATION_ERROR](state) { - state.isResetting = false; - }, [types.SET_JIRA_ISSUE_TYPES](state, jiraIssueTypes) { state.jiraIssueTypes = jiraIssueTypes; }, diff --git a/app/assets/javascripts/integrations/edit/store/state.js b/app/assets/javascripts/integrations/edit/store/state.js index 3d40d1b90d5..088476b2b37 100644 --- a/app/assets/javascripts/integrations/edit/store/state.js +++ b/app/assets/javascripts/integrations/edit/store/state.js @@ -5,8 +5,6 @@ export default ({ defaultState = null, customState = {} } = {}) => { override, defaultState, customState, - isSaving: false, - isResetting: false, isLoadingJiraIssueTypes: false, loadingJiraIssueTypesErrorMessage: '', jiraIssueTypes: [], diff --git a/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue b/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue index 3fc554c5371..f2d3e6489ee 100644 --- a/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue +++ b/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue @@ -11,6 +11,8 @@ import { __, s__ } from '~/locale'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import UrlSync from '~/vue_shared/components/url_sync.vue'; +import IntegrationTabs from './integration_tabs.vue'; + const DEFAULT_PAGE = 1; export default { @@ -23,6 +25,7 @@ export default { GlAlert, ProjectAvatar, UrlSync, + IntegrationTabs, }, props: { overridesPath: { @@ -46,6 +49,9 @@ export default { }; }, computed: { + overridesCount() { + return this.isLoading ? null : this.totalItems; + }, showPagination() { return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0; }, @@ -100,6 +106,7 @@ export default { <template> <div> + <integration-tabs :project-overrides-count="overridesCount" /> <gl-alert v-if="errorMessage" variant="danger" :dismissible="false"> {{ errorMessage }} </gl-alert> diff --git a/app/assets/javascripts/integrations/overrides/components/integration_tabs.vue b/app/assets/javascripts/integrations/overrides/components/integration_tabs.vue new file mode 100644 index 00000000000..3f67c987231 --- /dev/null +++ b/app/assets/javascripts/integrations/overrides/components/integration_tabs.vue @@ -0,0 +1,52 @@ +<script> +import { GlBadge, GlNavItem, GlTabs, GlTab } from '@gitlab/ui'; +import { settingsTabTitle, overridesTabTitle } from '~/integrations/constants'; + +export default { + components: { + GlBadge, + GlNavItem, + GlTabs, + GlTab, + }, + inject: { + editPath: { + default: '', + }, + }, + props: { + projectOverridesCount: { + type: [Number, String], + required: false, + default: null, + }, + }, + i18n: { + settingsTabTitle, + overridesTabTitle, + }, +}; +</script> + +<template> + <gl-tabs> + <template #tabs-start> + <gl-nav-item role="presentation" link-classes="gl-tab-nav-item" :href="editPath">{{ + $options.i18n.settingsTabTitle + }}</gl-nav-item> + </template> + + <gl-tab active> + <template #title> + {{ $options.i18n.overridesTabTitle }} + <gl-badge + v-if="projectOverridesCount !== null" + variant="muted" + size="sm" + class="gl-tab-counter-badge" + >{{ projectOverridesCount }}</gl-badge + > + </template> + </gl-tab> + </gl-tabs> +</template> diff --git a/app/assets/javascripts/integrations/overrides/index.js b/app/assets/javascripts/integrations/overrides/index.js index 0f03b23ba21..f289a2d3d1a 100644 --- a/app/assets/javascripts/integrations/overrides/index.js +++ b/app/assets/javascripts/integrations/overrides/index.js @@ -8,10 +8,13 @@ export default () => { return null; } - const { overridesPath } = el.dataset; + const { editPath, overridesPath } = el.dataset; return new Vue({ el, + provide: { + editPath, + }, render(createElement) { return createElement(IntegrationOverrides, { props: { |