Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 12:45:46 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 12:45:46 +0300
commita7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch)
tree7452bd5c3545c2fa67a28aa013835fb4fa071baf /app/assets/javascripts/vue_merge_request_widget
parentee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff)
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget')
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue20
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue31
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog.vue66
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue60
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue45
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue99
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/constants.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js120
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/issues.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/terraform/index.js30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/i18n.js17
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql1
19 files changed, 405 insertions, 121 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue
index 5ef7c2f72e0..7ba387c79b1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue
@@ -1,5 +1,6 @@
<script>
import createFlash from '~/flash';
+import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { visitUrl } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -79,6 +80,7 @@ export default {
[STOPPING]: {
actionName: STOPPING,
buttonText: s__('MrDeploymentActions|Stop environment'),
+ buttonVariant: 'danger',
busyText: __('This environment is being deployed'),
confirmMessage: __('Are you sure you want to stop this environment?'),
errorMessage: __('Something went wrong while stopping this environment. Please try again.'),
@@ -86,6 +88,7 @@ export default {
[DEPLOYING]: {
actionName: DEPLOYING,
buttonText: s__('MrDeploymentActions|Deploy'),
+ buttonVariant: 'confirm',
busyText: __('This environment is being deployed'),
confirmMessage: __('Are you sure you want to deploy this environment?'),
errorMessage: __('Something went wrong while deploying this environment. Please try again.'),
@@ -93,14 +96,27 @@ export default {
[REDEPLOYING]: {
actionName: REDEPLOYING,
buttonText: s__('MrDeploymentActions|Re-deploy'),
+ buttonVariant: 'confirm',
busyText: __('This environment is being re-deployed'),
confirmMessage: __('Are you sure you want to re-deploy this environment?'),
errorMessage: __('Something went wrong while deploying this environment. Please try again.'),
},
},
methods: {
- executeAction(endpoint, { actionName, confirmMessage, errorMessage }) {
- const isConfirmed = confirm(confirmMessage); //eslint-disable-line
+ async executeAction(
+ endpoint,
+ {
+ actionName,
+ buttonText: primaryBtnText,
+ buttonVariant: primaryBtnVariant,
+ confirmMessage,
+ errorMessage,
+ },
+ ) {
+ const isConfirmed = await confirmAction(confirmMessage, {
+ primaryBtnVariant,
+ primaryBtnText,
+ });
if (isConfirmed) {
this.actionInProgress = actionName;
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
index 7322958e6df..a25b4ab54e5 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue
@@ -128,10 +128,12 @@ export default {
api.trackRedisHllUserEvent(this.$options.expandEvent);
}
}),
- toggleCollapsed() {
- this.isCollapsed = !this.isCollapsed;
+ toggleCollapsed(e) {
+ if (!e?.target?.closest('.btn:not(.btn-icon),a')) {
+ this.isCollapsed = !this.isCollapsed;
- this.triggerRedisTracking();
+ this.triggerRedisTracking();
+ }
},
initExtensionPolling() {
const poll = new Poll({
@@ -139,7 +141,7 @@ export default {
fetchData: () => this.fetchCollapsedData(this.$props),
},
method: 'fetchData',
- successCallback: (data) => {
+ successCallback: ({ data }) => {
if (Object.keys(data).length > 0) {
poll.stop();
this.setCollapsedData(data);
@@ -207,6 +209,19 @@ export default {
this.showFade = true;
}
},
+ onRowMouseDown() {
+ this.down = Number(new Date());
+ },
+ onRowMouseUp(e) {
+ const up = Number(new Date());
+
+ // To allow for text to be selected we check if the the user is clicking
+ // or selecting, if they are selecting the time difference should be
+ // more than 200ms
+ if (up - this.down < 200) {
+ this.toggleCollapsed(e);
+ }
+ },
generateText,
},
EXTENSION_ICON_CLASS,
@@ -215,7 +230,7 @@ export default {
<template>
<section class="media-section" data-testid="widget-extension">
- <div class="media gl-p-5">
+ <div class="media gl-p-5 gl-cursor-pointer" @mousedown="onRowMouseDown" @mouseup="onRowMouseUp">
<status-icon
:name="$options.label || $options.name"
:is-loading="isLoadingSummary"
@@ -253,7 +268,7 @@ export default {
category="tertiary"
data-testid="toggle-button"
size="small"
- @click="toggleCollapsed"
+ @click.self="toggleCollapsed"
/>
</div>
</div>
@@ -317,9 +332,13 @@ export default {
<div v-if="data.link">
<gl-link :href="data.link.href">{{ data.link.text }}</gl-link>
</div>
+ <div v-if="data.supportingText">
+ <p v-safe-html="generateText(data.supportingText)" class="gl-m-0"></p>
+ </div>
<gl-badge v-if="data.badge" :variant="data.badge.variant || 'info'">
{{ data.badge.text }}
</gl-badge>
+
<actions
:widget="$options.label || $options.name"
:tertiary-buttons="data.actions"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
index cd5b7c3110d..8b410926c46 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
@@ -90,7 +90,7 @@ export default {
</template>
<div class="row">
<div
- class="col-md-5 order-md-last col-12 gl-mt-5 gl-mt-md-n2! gl-pt-md-2 svg-content svg-225"
+ class="col-md-5 order-md-last col-12 gl-mt-5 gl-md-mt-n2! gl-md-pt-2 svg-content svg-225"
>
<img data-testid="pipeline-image" :src="pipelineSvgPath" />
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog.vue
new file mode 100644
index 00000000000..7279ad971be
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/merge_failed_pipeline_confirmation_dialog.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlModal, GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ name: 'MergeFailedPipelineConfirmationDialog',
+ i18n: {
+ primary: __('Merge unverified changes'),
+ cancel: __('Cancel'),
+ info: __(
+ 'The latest pipeline for this merge request did not succeed. The latest changes are unverified.',
+ ),
+ confirmation: __('Are you sure you want to attempt to merge?'),
+ title: __('Merge unverified changes?'),
+ },
+ components: {
+ GlModal,
+ GlButton,
+ },
+ props: {
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ methods: {
+ hide() {
+ this.$refs.modal.hide();
+ },
+ cancel() {
+ this.hide();
+ this.$emit('cancel');
+ },
+ focusCancelButton() {
+ this.$refs.cancelButton.$el.focus();
+ },
+ mergeChanges() {
+ this.$emit('mergeWithFailedPipeline');
+ this.hide();
+ },
+ },
+};
+</script>
+<template>
+ <gl-modal
+ ref="modal"
+ size="sm"
+ modal-id="merge-train-failed-pipeline-confirmation-dialog"
+ :title="$options.i18n.title"
+ :visible="visible"
+ data-testid="merge-failed-pipeline-confirmation-dialog"
+ @shown="focusCancelButton"
+ @hide="$emit('cancel')"
+ >
+ <p>{{ $options.i18n.info }}</p>
+ <p>{{ $options.i18n.confirmation }}</p>
+ <template #modal-footer>
+ <gl-button ref="cancelButton" data-testid="merge-cancel-btn" @click="cancel">{{
+ $options.i18n.cancel
+ }}</gl-button>
+ <gl-button variant="danger" data-testid="merge-unverified-changes" @click="mergeChanges">
+ {{ $options.i18n.primary }}
+ </gl-button>
+ </template>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
index 247877a8235..e0c4679b983 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
@@ -1,7 +1,14 @@
<script>
-import { MERGE_ACTIVE_STATUS_PHRASES } from '../../constants';
+import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
+import simplePoll from '~/lib/utils/simple_poll';
+import MergeRequest from '../../../merge_request';
+import eventHub from '../../event_hub';
+import { MERGE_ACTIVE_STATUS_PHRASES, STATE_MACHINE } from '../../constants';
import statusIcon from '../mr_widget_status_icon.vue';
+const { transitions } = STATE_MACHINE;
+const { MERGE_FAILURE } = transitions;
+
export default {
name: 'MRWidgetMerging',
components: {
@@ -12,6 +19,10 @@ export default {
type: Object,
required: true,
},
+ service: {
+ type: Object,
+ required: true,
+ },
},
data() {
const statusCount = MERGE_ACTIVE_STATUS_PHRASES.length;
@@ -20,6 +31,53 @@ export default {
mergeStatus: MERGE_ACTIVE_STATUS_PHRASES[Math.floor(Math.random() * statusCount)],
};
},
+ mounted() {
+ this.initiateMergePolling();
+ },
+ methods: {
+ initiateMergePolling() {
+ simplePoll(
+ (continuePolling, stopPolling) => {
+ this.handleMergePolling(continuePolling, stopPolling);
+ },
+ { timeout: 0 },
+ );
+ },
+ handleMergePolling(continuePolling, stopPolling) {
+ this.service
+ .poll()
+ .then((res) => res.data)
+ .then((data) => {
+ if (data.state === 'merged') {
+ // If state is merged we should update the widget and stop the polling
+ eventHub.$emit('MRWidgetUpdateRequested');
+ eventHub.$emit('FetchActionsContent');
+ MergeRequest.hideCloseButton();
+ MergeRequest.decreaseCounter();
+ stopPolling();
+
+ refreshUserMergeRequestCounts();
+
+ // If user checked remove source branch and we didn't remove the branch yet
+ // we should start another polling for source branch remove process
+ if (this.removeSourceBranch && data.source_branch_exists) {
+ this.initiateRemoveSourceBranchPolling();
+ }
+ } else if (data.merge_error) {
+ eventHub.$emit('FailedToMerge', data.merge_error);
+ this.mr.transitionStateMachine({ transition: MERGE_FAILURE });
+ stopPolling();
+ } else {
+ // MR is not merged yet, continue polling until the state becomes 'merged'
+ continuePolling();
+ }
+ })
+ .catch(() => {
+ this.mr.transitionStateMachine({ transition: MERGE_FAILURE });
+ stopPolling();
+ });
+ },
+ },
};
</script>
<template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
index 5b03eda2eac..cadbd9c28a9 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
@@ -1,9 +1,14 @@
<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { sprintf, s__ } from '~/locale';
+import { GlIcon, GlTooltipDirective, GlSprintf } from '@gitlab/ui';
+import { sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables';
import missingBranchQuery from '../../queries/states/missing_branch.query.graphql';
+import {
+ MR_WIDGET_MISSING_BRANCH_WHICH,
+ MR_WIDGET_MISSING_BRANCH_RESTORE,
+ MR_WIDGET_MISSING_BRANCH_MANUALCLI,
+} from '../../i18n';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
@@ -13,6 +18,7 @@ export default {
},
components: {
GlIcon,
+ GlSprintf,
statusIcon,
},
mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin],
@@ -45,26 +51,20 @@ export default {
return this.mr.sourceBranchRemoved;
},
- missingBranchName() {
+ type() {
return this.sourceBranchRemoved ? 'source' : 'target';
},
- missingBranchNameMessage() {
- return sprintf(
- s__('mrWidget| Please restore it or use a different %{missingBranchName} branch'),
- {
- missingBranchName: this.missingBranchName,
- },
- );
+ name() {
+ return this.type === 'source' ? this.mr.sourceBranch : this.mr.targetBranch;
+ },
+ warning() {
+ return sprintf(MR_WIDGET_MISSING_BRANCH_WHICH, { type: this.type, name: this.name });
+ },
+ restore() {
+ return sprintf(MR_WIDGET_MISSING_BRANCH_RESTORE, { type: this.type });
},
message() {
- return sprintf(
- s__(
- 'mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line',
- ),
- {
- missingBranchName: this.missingBranchName,
- },
- );
+ return sprintf(MR_WIDGET_MISSING_BRANCH_MANUALCLI, { type: this.type });
},
},
};
@@ -79,9 +79,14 @@ export default {
'gl-ml-0! gl-text-body!': glFeatures.restructuredMrWidget,
}"
class="bold js-branch-text"
+ data-testid="widget-content"
>
- <span class="capitalize" data-testid="missingBranchName"> {{ missingBranchName }} </span>
- {{ s__('mrWidget|branch does not exist.') }} {{ missingBranchNameMessage }}
+ <gl-sprintf :message="warning">
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ {{ restore }}
<gl-icon
v-gl-tooltip
:title="message"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
index d88dad2e086..d204befef58 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
@@ -20,7 +20,7 @@ export default {
},
i18n: {
failedMessage: s__(
- `mrWidget|The pipeline for this merge request did not complete. Push a new commit to fix the failure, or check the %{linkStart}troubleshooting documentation%{linkEnd} to see other possible actions.`,
+ `mrWidget|Merge blocked: pipeline must succeed. Push a commit that fixes the failure, or %{linkStart}learn about other solutions.%{linkEnd}`,
),
},
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index 06ce312bd4c..bc094501e89 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -14,7 +14,6 @@ import {
import { isEmpty } from 'lodash';
import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge';
import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql';
-import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import createFlash from '~/flash';
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
import simplePoll from '~/lib/utils/simple_poll';
@@ -22,11 +21,8 @@ import { __, s__ } from '~/locale';
import SmartInterval from '~/smart_interval';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { helpPagePath } from '~/helpers/help_page_helper';
-import MergeRequest from '../../../merge_request';
import {
AUTO_MERGE_STRATEGIES,
- DANGER,
- CONFIRM,
WARNING,
MT_MERGE_STRATEGY,
PIPELINE_FAILED_STATE,
@@ -42,6 +38,7 @@ import CommitEdit from './commit_edit.vue';
import CommitMessageDropdown from './commit_message_dropdown.vue';
import CommitsHeader from './commits_header.vue';
import SquashBeforeMerge from './squash_before_merge.vue';
+import MergeFailedPipelineConfirmationDialog from './merge_failed_pipeline_confirmation_dialog.vue';
const PIPELINE_RUNNING_STATE = 'running';
const PIPELINE_PENDING_STATE = 'pending';
@@ -52,7 +49,7 @@ const MERGE_SUCCESS_STATUS = 'success';
const MERGE_HOOK_VALIDATION_ERROR_STATUS = 'hook_validation_error';
const { transitions } = STATE_MACHINE;
-const { MERGE, MERGED, MERGE_FAILURE, AUTO_MERGE } = transitions;
+const { MERGE, MERGE_FAILURE, AUTO_MERGE, MERGING } = transitions;
export default {
name: 'ReadyToMerge',
@@ -106,6 +103,7 @@ export default {
GlDropdownItem,
GlFormCheckbox,
GlSkeletonLoader,
+ MergeFailedPipelineConfirmationDialog,
MergeTrainHelperIcon: () =>
import('ee_component/vue_merge_request_widget/components/merge_train_helper_icon.vue'),
MergeImmediatelyConfirmationDialog: () =>
@@ -138,7 +136,8 @@ export default {
squashBeforeMerge: this.mr.squashIsSelected,
isSquashReadOnly: this.mr.squashIsReadonly,
squashCommitMessage: this.mr.squashCommitMessage,
- isPipelineFailedModalVisible: false,
+ isPipelineFailedModalVisibleMergeTrain: false,
+ isPipelineFailedModalVisibleNormalMerge: false,
editCommitMessage: false,
};
},
@@ -166,6 +165,9 @@ export default {
return this.mr.isPipelineFailed;
},
+ showMergeFailedPipelineConfirmationDialog() {
+ return this.status === PIPELINE_FAILED_STATE && this.isPipelineFailed;
+ },
isMergeAllowed() {
if (this.glFeatures.mergeRequestWidgetGraphql) {
return this.state.mergeable;
@@ -248,13 +250,6 @@ export default {
return PIPELINE_SUCCESS_STATE;
},
- mergeButtonVariant() {
- if (this.status === PIPELINE_FAILED_STATE || this.isPipelineFailed) {
- return DANGER;
- }
-
- return CONFIRM;
- },
iconClass() {
if (this.shouldRenderMergeTrainHelperIcon && !this.mr.preventMerge) {
return PIPELINE_RUNNING_STATE;
@@ -279,6 +274,10 @@ export default {
return this.autoMergeText;
}
+ if (this.status === PIPELINE_FAILED_STATE || this.isPipelineFailed) {
+ return __('Merge...');
+ }
+
return __('Merge');
},
hasPipelineMustSucceedConflict() {
@@ -361,8 +360,13 @@ export default {
return this.$apollo.queries.state.refetch();
},
handleMergeButtonClick(useAutoMerge, mergeImmediately = false, confirmationClicked = false) {
- if (this.showFailedPipelineModal && !confirmationClicked) {
- this.isPipelineFailedModalVisible = true;
+ if (this.showMergeFailedPipelineConfirmationDialog && !confirmationClicked) {
+ this.isPipelineFailedModalVisibleNormalMerge = true;
+ return;
+ }
+
+ if (this.showFailedPipelineModalMergeTrain && !confirmationClicked) {
+ this.isPipelineFailedModalVisibleMergeTrain = true;
return;
}
@@ -406,7 +410,7 @@ export default {
eventHub.$emit('MRWidgetUpdateRequested');
this.mr.transitionStateMachine({ transition: AUTO_MERGE });
} else if (data.status === MERGE_SUCCESS_STATUS) {
- this.initiateMergePolling();
+ this.mr.transitionStateMachine({ transition: MERGING });
} else if (hasError) {
eventHub.$emit('FailedToMerge', data.merge_error);
this.mr.transitionStateMachine({ transition: MERGE_FAILURE });
@@ -434,51 +438,8 @@ export default {
onMergeImmediatelyConfirmation() {
this.handleMergeButtonClick(false, true, true);
},
- initiateMergePolling() {
- simplePoll(
- (continuePolling, stopPolling) => {
- this.handleMergePolling(continuePolling, stopPolling);
- },
- { timeout: 0 },
- );
- },
- handleMergePolling(continuePolling, stopPolling) {
- this.service
- .poll()
- .then((res) => res.data)
- .then((data) => {
- if (data.state === 'merged') {
- // If state is merged we should update the widget and stop the polling
- eventHub.$emit('MRWidgetUpdateRequested');
- eventHub.$emit('FetchActionsContent');
- MergeRequest.hideCloseButton();
- MergeRequest.decreaseCounter();
- this.mr.transitionStateMachine({ transition: MERGED });
- stopPolling();
-
- refreshUserMergeRequestCounts();
-
- // If user checked remove source branch and we didn't remove the branch yet
- // we should start another polling for source branch remove process
- if (this.removeSourceBranch && data.source_branch_exists) {
- this.initiateRemoveSourceBranchPolling();
- }
- } else if (data.merge_error) {
- eventHub.$emit('FailedToMerge', data.merge_error);
- this.mr.transitionStateMachine({ transition: MERGE_FAILURE });
- stopPolling();
- } else {
- // MR is not merged yet, continue polling until the state becomes 'merged'
- continuePolling();
- }
- })
- .catch(() => {
- createFlash({
- message: __('Something went wrong while merging this merge request. Please try again.'),
- });
- this.mr.transitionStateMachine({ transition: MERGE_FAILURE });
- stopPolling();
- });
+ onMergeWithFailedPipelineConfirmation() {
+ this.handleMergeButtonClick(false, true, true);
},
initiateRemoveSourceBranchPolling() {
// We need to show source branch is being removed spinner in another component
@@ -559,7 +520,7 @@ export default {
category="primary"
class="accept-merge-request"
data-testid="merge-button"
- :variant="mergeButtonVariant"
+ variant="confirm"
:disabled="isMergeButtonDisabled"
:loading="isMakingRequest"
data-qa-selector="merge_button"
@@ -570,7 +531,7 @@ export default {
v-if="shouldShowMergeImmediatelyDropdown"
v-gl-tooltip.hover.focus="__('Select merge moment')"
:disabled="isMergeButtonDisabled"
- :variant="mergeButtonVariant"
+ variant="confirm"
data-qa-selector="merge_moment_dropdown"
toggle-class="btn-icon js-merge-moment"
>
@@ -593,18 +554,22 @@ export default {
/>
</gl-dropdown>
<merge-train-failed-pipeline-confirmation-dialog
- :visible="isPipelineFailedModalVisible"
+ :visible="isPipelineFailedModalVisibleMergeTrain"
@startMergeTrain="onStartMergeTrainConfirmation"
- @cancel="isPipelineFailedModalVisible = false"
+ @cancel="isPipelineFailedModalVisibleMergeTrain = false"
+ />
+ <merge-failed-pipeline-confirmation-dialog
+ :visible="isPipelineFailedModalVisibleNormalMerge"
+ @mergeWithFailedPipeline="onMergeWithFailedPipelineConfirmation"
+ @cancel="isPipelineFailedModalVisibleNormalMerge = false"
/>
</gl-button-group>
+ <merge-train-helper-icon v-if="shouldRenderMergeTrainHelperIcon" class="gl-mx-3" />
<div
v-if="shouldShowMergeControls"
:class="{ 'gl-w-full gl-order-n1 gl-mb-5': glFeatures.restructuredMrWidget }"
class="gl-display-flex gl-align-items-center gl-flex-wrap"
>
- <merge-train-helper-icon v-if="shouldRenderMergeTrainHelperIcon" class="gl-mx-3" />
-
<gl-form-checkbox
v-if="canRemoveSourceBranch"
id="remove-source-branch-input"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
index 8cf6383c26a..25ba4bf12af 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
@@ -43,8 +43,8 @@ export default {
class="gl-ml-3"
size="small"
:icon="glFeatures.restructuredMrWidget ? undefined : 'comment-next'"
- :variant="glFeatures.restructuredMrWidget && 'confirm'"
- :category="glFeatures.restructuredMrWidget && 'secondary'"
+ :variant="glFeatures.restructuredMrWidget ? 'confirm' : 'default'"
+ :category="glFeatures.restructuredMrWidget ? 'secondary' : 'primary'"
@click="jumpToFirstUnresolvedDiscussion"
>
{{ s__('mrWidget|Jump to first unresolved thread') }}
diff --git a/app/assets/javascripts/vue_merge_request_widget/constants.js b/app/assets/javascripts/vue_merge_request_widget/constants.js
index 32effb91043..d337a554663 100644
--- a/app/assets/javascripts/vue_merge_request_widget/constants.js
+++ b/app/assets/javascripts/vue_merge_request_widget/constants.js
@@ -68,6 +68,7 @@ const STATE_MACHINE = {
states: {
IDLE: 'IDLE',
MERGING: 'MERGING',
+ MERGED: 'MERGED',
AUTO_MERGE: 'AUTO_MERGE',
},
transitions: {
@@ -75,6 +76,7 @@ const STATE_MACHINE = {
AUTO_MERGE: 'start-auto-merge',
MERGE_FAILURE: 'merge-failed',
MERGED: 'merge-done',
+ MERGING: 'merging',
},
};
const { states, transitions } = STATE_MACHINE;
@@ -86,11 +88,12 @@ STATE_MACHINE.definition = {
on: {
[transitions.MERGE]: states.MERGING,
[transitions.AUTO_MERGE]: states.AUTO_MERGE,
+ [transitions.MERGING]: states.MERGING,
},
},
[states.MERGING]: {
on: {
- [transitions.MERGED]: states.IDLE,
+ [transitions.MERGED]: states.MERGED,
[transitions.MERGE_FAILURE]: states.IDLE,
},
},
@@ -110,6 +113,7 @@ export const stateToTransitionMap = {
};
export const stateToComponentMap = {
[states.MERGING]: classStateMap[stateKey.merging],
+ [states.MERGED]: classStateMap[stateKey.merged],
[states.AUTO_MERGE]: classStateMap[stateKey.autoMergeEnabled],
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js
new file mode 100644
index 00000000000..168f10bd148
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js
@@ -0,0 +1,120 @@
+import { uniqueId } from 'lodash';
+import { __, n__, s__, sprintf } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+import { EXTENSION_ICONS } from '../../constants';
+
+export default {
+ name: 'WidgetAccessibility',
+ enablePolling: true,
+ i18n: {
+ loading: s__('Reports|Accessibility scanning results are being parsed'),
+ error: s__('Reports|Accessibility scanning failed loading results'),
+ },
+ props: ['accessibilityReportPath'],
+ computed: {
+ statusIcon() {
+ return this.collapsedData.status === 'failed'
+ ? EXTENSION_ICONS.warning
+ : EXTENSION_ICONS.success;
+ },
+ },
+ methods: {
+ summary() {
+ const numOfResults = this.collapsedData?.summary?.errored || 0;
+
+ const successText = s__(
+ 'Reports|Accessibility scanning detected no issues for the source branch only',
+ );
+ const warningText = sprintf(
+ n__(
+ 'Reports|Accessibility scanning detected %{strong_start}%{number}%{strong_end} issue for the source branch only',
+ 'Reports|Accessibility scanning detected %{strong_start}%{number}%{strong_end} issues for the source branch only',
+ numOfResults,
+ ),
+ {
+ number: numOfResults,
+ },
+ false,
+ );
+
+ return numOfResults === 0 ? successText : warningText;
+ },
+ fetchCollapsedData() {
+ return axios.get(this.accessibilityReportPath);
+ },
+ fetchFullData() {
+ return Promise.resolve(this.prepareReports());
+ },
+ parsedTECHSCode(code) {
+ /*
+ * In issue code looks like "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
+ * or "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent"
+ *
+ * The TECHS code is the "G18", "G168", "H91", etc. from the code which is used for the documentation.
+ * Here we simply split the string on `.` and get the code in the 5th position
+ */
+ return code?.split('.')[4];
+ },
+ formatLearnMoreUrl(code) {
+ const parsed = this.parsedTECHSCode(code);
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ return `https://www.w3.org/TR/WCAG20-TECHS/${parsed || 'Overview'}.html`;
+ },
+ formatText(code) {
+ return sprintf(
+ s__(
+ 'AccessibilityReport|The accessibility scanning found an error of the following type: %{code}',
+ ),
+ { code },
+ );
+ },
+ formatMessage(message) {
+ return sprintf(s__('AccessibilityReport|Message: %{message}'), { message });
+ },
+ prepareReports() {
+ const { new_errors, existing_errors, resolved_errors } = this.collapsedData;
+
+ const newErrors = new_errors.map((error) => {
+ return {
+ header: __('New'),
+ id: uniqueId('new-error-'),
+ text: this.formatText(error.code),
+ icon: { name: EXTENSION_ICONS.failed },
+ link: {
+ href: this.formatLearnMoreUrl(error.code),
+ text: __('Learn more'),
+ },
+ supportingText: this.formatMessage(error.message),
+ };
+ });
+
+ const existingErrors = existing_errors.map((error) => {
+ return {
+ id: uniqueId('existing-error-'),
+ text: this.formatText(error.code),
+ icon: { name: EXTENSION_ICONS.failed },
+ link: {
+ href: this.formatLearnMoreUrl(error.code),
+ text: __('Learn more'),
+ },
+ supportingText: this.formatMessage(error.message),
+ };
+ });
+
+ const resolvedErrors = resolved_errors.map((error) => {
+ return {
+ id: uniqueId('resolved-error-'),
+ text: this.formatText(error.code),
+ icon: { name: EXTENSION_ICONS.success },
+ link: {
+ href: this.formatLearnMoreUrl(error.code),
+ text: __('Learn more'),
+ },
+ supportingText: this.formatMessage(error.message),
+ };
+ });
+
+ return [...newErrors, ...existingErrors, ...resolvedErrors];
+ },
+ },
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js
index ba3336df2eb..4aeebf095c4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/issues.js
@@ -25,9 +25,9 @@ export default {
n__(
'ciReport|Load performance test metrics detected %{strong_start}%{changesFound}%{strong_end} change',
'ciReport|Load performance test metrics detected %{strong_start}%{changesFound}%{strong_end} changes',
- changesFound,
+ count,
),
- { changesFound },
+ { changesFound: count },
);
},
// Status icon to be used next to the summary text
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/terraform/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/terraform/index.js
index a564acada02..8fcc4f818ec 100644
--- a/app/assets/javascripts/vue_merge_request_widget/extensions/terraform/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/terraform/index.js
@@ -73,26 +73,30 @@ export default {
return `${title}${subtitle}`;
},
fetchCollapsedData() {
- return Promise.resolve(this.fetchPlans().then(this.prepareReports));
- },
- fetchFullData() {
- const { valid, invalid } = this.collapsedData;
- return Promise.resolve([...valid, ...invalid]);
- },
- // Custom methods
- fetchPlans() {
return axios
.get(this.terraformReportsPath)
- .then(({ data }) => {
- return Object.keys(data).map((key) => {
- return data[key];
+ .then((res) => {
+ const reports = Object.keys(res.data).map((key) => {
+ return res.data[key];
});
+
+ const formattedData = this.prepareReports(reports);
+
+ return {
+ ...res,
+ data: formattedData,
+ };
})
.catch(() => {
- const invalidData = { tf_report_error: 'api_error' };
- return [invalidData];
+ const formattedData = this.prepareReports([{ tf_report_error: 'api_error' }]);
+
+ return { data: formattedData };
});
},
+ fetchFullData() {
+ const { valid, invalid } = this.collapsedData;
+ return Promise.resolve([...valid, ...invalid]);
+ },
createReportRow(report, iconName) {
const addNum = Number(report.create);
const changeNum = Number(report.update);
diff --git a/app/assets/javascripts/vue_merge_request_widget/i18n.js b/app/assets/javascripts/vue_merge_request_widget/i18n.js
index c88e795e5f3..454a14faabb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/i18n.js
+++ b/app/assets/javascripts/vue_merge_request_widget/i18n.js
@@ -1,4 +1,14 @@
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
+
+export const MR_WIDGET_MISSING_BRANCH_WHICH = s__(
+ 'mrWidget|The %{type} branch %{codeStart}%{name}%{codeEnd} does not exist.',
+);
+export const MR_WIDGET_MISSING_BRANCH_RESTORE = s__(
+ 'mrWidget|Please restore it or use a different %{type} branch.',
+);
+export const MR_WIDGET_MISSING_BRANCH_MANUALCLI = s__(
+ 'mrWidget|If the %{type} branch exists in your local repository, you can merge this merge request manually using the command line.',
+);
export const SQUASH_BEFORE_MERGE = {
tooltipTitle: __('Required in this project.'),
@@ -10,3 +20,8 @@ export const I18N_SHA_MISMATCH = {
warningMessage: __('Merge blocked: new changes were just added.'),
actionButtonLabel: __('Review changes'),
};
+
+export const MERGE_TRAIN_BUTTON_TEXT = {
+ failed: __('Start merge train...'),
+ passed: __('Start merge train'),
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
index fa618756bb5..247a3711fc8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
@@ -48,7 +48,7 @@ export default {
pipelineId() {
return this.pipeline.id;
},
- showFailedPipelineModal() {
+ showFailedPipelineModalMergeTrain() {
return false;
},
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index 83a07240403..11de58aa344 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -45,6 +45,7 @@ import eventHub from './event_hub';
import mergeRequestQueryVariablesMixin from './mixins/merge_request_query_variables';
import getStateQuery from './queries/get_state.query.graphql';
import terraformExtension from './extensions/terraform';
+import accessibilityExtension from './extensions/accessibility';
export default {
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25
@@ -205,7 +206,7 @@ export default {
);
},
shouldShowAccessibilityReport() {
- return this.mr.accessibilityReportPath;
+ return Boolean(this.mr?.accessibilityReportPath);
},
formattedHumanAccess() {
return (this.mr.humanAccess || '').toLowerCase();
@@ -240,6 +241,11 @@ export default {
this.registerTerraformPlans();
}
},
+ shouldShowAccessibilityReport(newVal) {
+ if (newVal) {
+ this.registerAccessibilityExtension();
+ }
+ },
},
mounted() {
MRWidgetService.fetchInitialData()
@@ -478,6 +484,11 @@ export default {
registerExtension(terraformExtension);
}
},
+ registerAccessibilityExtension() {
+ if (this.shouldShowAccessibilityReport && this.shouldShowExtension) {
+ registerExtension(accessibilityExtension);
+ }
+ },
},
};
</script>
@@ -567,7 +578,7 @@ export default {
:endpoint="mr.accessibilityReportPath"
/>
- <div class="mr-widget-section">
+ <div class="mr-widget-section" data-qa-selector="mr_widget_content">
<component :is="componentName" :mr="mr" :service="service" />
<ready-to-merge
v-if="isRestructuredMrWidgetEnabled && mr.commitsCount"
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
index 0b8396b4461..25c44beaf18 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
@@ -3,7 +3,6 @@ query getState($projectPath: ID!, $iid: String!) {
id
archived
onlyAllowMergeIfPipelineSucceeds
-
mergeRequest(iid: $iid) {
id
autoMergeEnabled
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql
index 2d79d35cf24..ad93a3a7371 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql
@@ -4,6 +4,7 @@ query autoMergeEnabled($projectPath: ID!, $iid: String!) {
project(fullPath: $projectPath) {
id
mergeRequest(iid: $iid) {
+ id
...autoMergeEnabled
}
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql
index f713739f65a..556ecee254d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql
@@ -2,6 +2,7 @@
query readyToMerge($projectPath: ID!, $iid: String!) {
project(fullPath: $projectPath) {
+ id
...ReadyToMerge
}
}