diff options
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/components/states')
8 files changed, 211 insertions, 77 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue index b6722de5277..f17e409d996 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue @@ -1,10 +1,10 @@ <script> -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { GlDeprecatedDropdown, GlDeprecatedDropdownItem } from '@gitlab/ui'; export default { components: { - GlDropdown, - GlDropdownItem, + GlDeprecatedDropdown, + GlDeprecatedDropdownItem, }, props: { commits: { @@ -18,20 +18,20 @@ export default { <template> <div> - <gl-dropdown + <gl-deprecated-dropdown right text="Use an existing commit message" variant="link" class="mr-commit-dropdown" > - <gl-dropdown-item + <gl-deprecated-dropdown-item v-for="commit in commits" :key="commit.short_id" class="text-nowrap text-truncate" @click="$emit('input', commit.message)" > <span class="monospace mr-2">{{ commit.short_id }}</span> {{ commit.title }} - </gl-dropdown-item> - </gl-dropdown> + </gl-deprecated-dropdown-item> + </gl-deprecated-dropdown> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue index f02e0ac84da..12f65a4c235 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue @@ -1,6 +1,6 @@ <script> import autoMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/auto_merge'; -import Flash from '../../../flash'; +import { deprecatedCreateFlash as Flash } from '../../../flash'; import statusIcon from '../mr_widget_status_icon.vue'; import MrWidgetAuthor from '../mr_widget_author.vue'; import eventHub from '../../event_hub'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue index 1a6e186a371..166700dbcbf 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue @@ -1,7 +1,7 @@ <script> /* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLoadingIcon } from '@gitlab/ui'; -import Flash from '~/flash'; +import { deprecatedCreateFlash as Flash } from '~/flash'; import tooltip from '~/vue_shared/directives/tooltip'; import { s__, __ } from '~/locale'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index c3cc30a1a6f..794c994bffe 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -4,7 +4,7 @@ import { escape } from 'lodash'; import simplePoll from '../../../lib/utils/simple_poll'; import eventHub from '../../event_hub'; import statusIcon from '../mr_widget_status_icon.vue'; -import Flash from '../../../flash'; +import { deprecatedCreateFlash as Flash } from '../../../flash'; import { __, sprintf } from '~/locale'; export default { 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 cc43135f50a..930a2b68d8e 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 @@ -8,14 +8,23 @@ import simplePoll from '~/lib/utils/simple_poll'; import { __, sprintf } from '~/locale'; import MergeRequest from '../../../merge_request'; import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; -import Flash from '../../../flash'; +import { deprecatedCreateFlash as Flash } from '../../../flash'; import statusIcon from '../mr_widget_status_icon.vue'; import eventHub from '../../event_hub'; import SquashBeforeMerge from './squash_before_merge.vue'; import CommitsHeader from './commits_header.vue'; import CommitEdit from './commit_edit.vue'; import CommitMessageDropdown from './commit_message_dropdown.vue'; -import { AUTO_MERGE_STRATEGIES } from '../../constants'; +import { AUTO_MERGE_STRATEGIES, DANGER, INFO, WARNING } from '../../constants'; + +const PIPELINE_RUNNING_STATE = 'running'; +const PIPELINE_FAILED_STATE = 'failed'; +const PIPELINE_PENDING_STATE = 'pending'; +const PIPELINE_SUCCESS_STATE = 'success'; + +const MERGE_FAILED_STATUS = 'failed'; +const MERGE_SUCCESS_STATUS = 'success'; +const MERGE_HOOK_VALIDATION_ERROR_STATUS = 'hook_validation_error'; export default { name: 'ReadyToMerge', @@ -29,6 +38,8 @@ export default { GlSprintf, GlLink, GlDeprecatedButton, + MergeTrainHelperText: () => + import('ee_component/vue_merge_request_widget/components/merge_train_helper_text.vue'), MergeImmediatelyConfirmationDialog: () => import( 'ee_component/vue_merge_request_widget/components/merge_immediately_confirmation_dialog.vue' @@ -60,35 +71,45 @@ export default { const { pipeline, isPipelineFailed, hasCI, ciStatus } = this.mr; if ((hasCI && !ciStatus) || this.hasPipelineMustSucceedConflict) { - return 'failed'; - } else if (this.isAutoMergeAvailable) { - return 'pending'; - } else if (!pipeline) { - return 'success'; - } else if (isPipelineFailed) { - return 'failed'; + return PIPELINE_FAILED_STATE; + } + + if (this.isAutoMergeAvailable) { + return PIPELINE_PENDING_STATE; + } + + if (pipeline && isPipelineFailed) { + return PIPELINE_FAILED_STATE; } - return 'success'; + return PIPELINE_SUCCESS_STATE; }, mergeButtonVariant() { - if (this.status === 'failed') { - return 'danger'; - } else if (this.status === 'pending') { - return 'info'; + if (this.status === PIPELINE_FAILED_STATE) { + return DANGER; } - return 'success'; + + if (this.status === PIPELINE_PENDING_STATE) { + return INFO; + } + + return PIPELINE_SUCCESS_STATE; }, iconClass() { + if (this.shouldRenderMergeTrainHelperText && !this.mr.preventMerge) { + return PIPELINE_RUNNING_STATE; + } + if ( - this.status === 'failed' || + this.status === PIPELINE_FAILED_STATE || !this.commitMessage.length || !this.mr.isMergeAllowed || this.mr.preventMerge ) { - return 'warning'; + return WARNING; } - return 'success'; + + return PIPELINE_SUCCESS_STATE; }, mergeButtonText() { if (this.isMergingImmediately) { @@ -167,11 +188,13 @@ export default { .merge(options) .then(res => res.data) .then(data => { - const hasError = data.status === 'failed' || data.status === 'hook_validation_error'; + const hasError = + data.status === MERGE_FAILED_STATUS || + data.status === MERGE_HOOK_VALIDATION_ERROR_STATUS; if (AUTO_MERGE_STRATEGIES.includes(data.status)) { eventHub.$emit('MRWidgetUpdateRequested'); - } else if (data.status === 'success') { + } else if (data.status === MERGE_SUCCESS_STATUS) { this.initiateMergePolling(); } else if (hasError) { eventHub.$emit('FailedToMerge', data.merge_error); @@ -269,7 +292,7 @@ export default { <template> <div> - <div class="mr-widget-body media"> + <div class="mr-widget-body media" :class="{ 'gl-pb-3': shouldRenderMergeTrainHelperText }"> <status-icon :status="iconClass" /> <div class="media-body"> <div class="mr-widget-body-controls media space-children"> @@ -358,6 +381,7 @@ export default { <div v-if="hasPipelineMustSucceedConflict" class="gl-display-flex gl-align-items-center" + data-testid="pipeline-succeed-conflict" > <gl-sprintf :message="pipelineMustSucceedConflictText" /> <gl-link @@ -379,6 +403,13 @@ export default { </div> </div> </div> + <merge-train-helper-text + v-if="shouldRenderMergeTrainHelperText" + :pipeline-id="mr.pipeline.id" + :pipeline-link="mr.pipeline.path" + :merge-train-length="mr.mergeTrainsCount" + :merge-train-when-pipeline-succeeds-docs-path="mr.mergeTrainWhenPipelineSucceedsDocsPath" + /> <template v-if="shouldShowMergeControls"> <div v-if="mr.ffOnlyEnabled" class="mr-fast-forward-message"> {{ __('Fast-forward merge without a merge commit') }} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue index efd58341a2d..3cf7dc3c4d1 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue @@ -38,7 +38,7 @@ export default { <div class="inline"> <label v-tooltip - :class="{ 'gl-text-gray-600': isDisabled }" + :class="{ 'gl-text-gray-400': isDisabled }" data-testid="squashLabel" :data-title="tooltipTitle" > 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 d4a5fdb4b97..78e69b9ff9b 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 @@ -1,10 +1,13 @@ <script> +import { GlButton } from '@gitlab/ui'; import statusIcon from '../mr_widget_status_icon.vue'; +import notesEventHub from '~/notes/event_hub'; export default { name: 'UnresolvedDiscussions', components: { statusIcon, + GlButton, }, props: { mr: { @@ -12,23 +15,39 @@ export default { required: true, }, }, + methods: { + jumpToFirstUnresolvedDiscussion() { + notesEventHub.$emit('jumpToFirstUnresolvedDiscussion'); + }, + }, }; </script> <template> - <div class="mr-widget-body media"> + <div class="mr-widget-body media gl-flex-wrap"> <status-icon :show-disabled-button="true" status="warning" /> - <div class="media-body space-children"> - <span class="bold"> - {{ s__('mrWidget|There are unresolved threads. Please resolve these threads') }} - </span> - <a + <div class="media-body"> + <span class="gl-ml-3 gl-font-weight-bold gl-display-block gl-w-100">{{ + s__('mrWidget|Before this can be merged, one or more threads must be resolved.') + }}</span> + <gl-button + data-testid="jump-to-first" + class="gl-ml-3" + size="small" + icon="comment-next" + @click="jumpToFirstUnresolvedDiscussion" + > + {{ s__('mrWidget|Jump to first unresolved thread') }} + </gl-button> + <gl-button v-if="mr.createIssueToResolveDiscussionsPath" :href="mr.createIssueToResolveDiscussionsPath" - class="btn btn-default btn-sm js-create-issue" + class="js-create-issue gl-ml-3" + size="small" + icon="issue-new" > - {{ s__('mrWidget|Create an issue to resolve them later') }} - </a> + {{ s__('mrWidget|Resolve all threads in new issue') }} + </gl-button> </div> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue index 118caac84b9..44668170fe4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue @@ -1,8 +1,13 @@ <script> import $ from 'jquery'; -import { GlDeprecatedButton } from '@gitlab/ui'; -import { __, s__ } from '~/locale'; -import createFlash from '~/flash'; +import { GlButton } from '@gitlab/ui'; +import { __ } from '~/locale'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables'; +import getStateQuery from '../../queries/get_state.query.graphql'; +import workInProgressQuery from '../../queries/states/work_in_progress.query.graphql'; +import removeWipMutation from '../../queries/toggle_wip.mutation.graphql'; import StatusIcon from '../mr_widget_status_icon.vue'; import tooltip from '../../../vue_shared/directives/tooltip'; import eventHub from '../../event_hub'; @@ -11,72 +16,151 @@ export default { name: 'WorkInProgress', components: { StatusIcon, - GlDeprecatedButton, + GlButton, }, directives: { tooltip, }, + mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin], + apollo: { + userPermissions: { + query: workInProgressQuery, + skip() { + return !this.glFeatures.mergeRequestWidgetGraphql; + }, + variables() { + return this.mergeRequestQueryVariables; + }, + update: data => data.project.mergeRequest.userPermissions, + }, + }, props: { mr: { type: Object, required: true }, service: { type: Object, required: true }, }, data() { return { + userPermissions: {}, isMakingRequest: false, }; }, computed: { - wipInfoTooltip() { - return s__( - 'mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged', - ); + canUpdate() { + if (this.glFeatures.mergeRequestWidgetGraphql) { + return this.userPermissions.updateMergeRequest; + } + + return Boolean(this.mr.removeWIPPath); }, }, methods: { - handleRemoveWIP() { + removeWipMutation() { this.isMakingRequest = true; - this.service - .removeWIP() - .then(res => res.data) - .then(data => { - eventHub.$emit('UpdateWidgetData', data); + + this.$apollo + .mutate({ + mutation: removeWipMutation, + variables: { + ...this.mergeRequestQueryVariables, + wip: false, + }, + update( + store, + { + data: { + mergeRequestSetWip: { + errors, + mergeRequest: { workInProgress, title }, + }, + }, + }, + ) { + if (errors?.length) { + createFlash(__('Something went wrong. Please try again.')); + + return; + } + + const data = store.readQuery({ + query: getStateQuery, + variables: this.mergeRequestQueryVariables, + }); + data.project.mergeRequest.workInProgress = workInProgress; + data.project.mergeRequest.title = title; + store.writeQuery({ + query: getStateQuery, + data, + variables: this.mergeRequestQueryVariables, + }); + }, + optimisticResponse: { + // eslint-disable-next-line @gitlab/require-i18n-strings + __typename: 'Mutation', + mergeRequestSetWip: { + __typename: 'MergeRequestSetWipPayload', + errors: [], + mergeRequest: { + __typename: 'MergeRequest', + title: this.mr.title, + workInProgress: false, + }, + }, + }, + }) + .then(({ data: { mergeRequestSetWip: { mergeRequest: { title } } } }) => { createFlash(__('The merge request can now be merged.'), 'notice'); - $('.merge-request .detail-page-description .title').text(this.mr.title); + $('.merge-request .detail-page-description .title').text(title); }) - .catch(() => { + .catch(() => createFlash(__('Something went wrong. Please try again.'))) + .finally(() => { this.isMakingRequest = false; - createFlash(__('Something went wrong. Please try again.')); }); }, + handleRemoveWIP() { + if (this.glFeatures.mergeRequestWidgetGraphql) { + this.removeWipMutation(); + } else { + this.isMakingRequest = true; + this.service + .removeWIP() + .then(res => res.data) + .then(data => { + eventHub.$emit('UpdateWidgetData', data); + createFlash(__('The merge request can now be merged.'), 'notice'); + $('.merge-request .detail-page-description .title').text(this.mr.title); + }) + .catch(() => { + this.isMakingRequest = false; + createFlash(__('Something went wrong. Please try again.')); + }); + } + }, }, }; </script> <template> <div class="mr-widget-body media"> - <status-icon :show-disabled-button="Boolean(mr.removeWIPPath)" status="warning" /> - <div class="media-body space-children"> - <span class="bold"> - {{ __('This is a Work in Progress') }} - <i - v-tooltip - class="fa fa-question-circle" - :title="wipInfoTooltip" - :aria-label="wipInfoTooltip" - > - </i> - </span> - <gl-deprecated-button - v-if="mr.removeWIPPath" - size="sm" - variant="default" + <status-icon :show-disabled-button="canUpdate" status="warning" /> + <div class="media-body"> + <div class="gl-ml-3 float-left"> + <span class="gl-font-weight-bold"> + {{ __('This merge request is still a work in progress.') }} + </span> + <span class="gl-display-block text-muted">{{ + __("Draft merge requests can't be merged.") + }}</span> + </div> + <gl-button + v-if="canUpdate" + size="small" :disabled="isMakingRequest" :loading="isMakingRequest" - class="js-remove-wip" + class="js-remove-wip gl-ml-3" @click="handleRemoveWIP" > - {{ s__('mrWidget|Resolve WIP status') }} - </gl-deprecated-button> + {{ s__('mrWidget|Mark as ready') }} + </gl-button> </div> </div> </template> |