diff options
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/components')
19 files changed, 292 insertions, 140 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue index 0f9d1b8395b..7297f8f8677 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue @@ -1,6 +1,6 @@ <script> import { GlButton } from '@gitlab/ui'; -import createFlash from '~/flash'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; import { s__ } from '~/locale'; import eventHub from '../../event_hub'; import approvalsMixin from '../../mixins/approvals'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue index 66af0c5a83e..24cd9d6428d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary_optional.vue @@ -1,10 +1,6 @@ <script> import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; -import { - OPTIONAL, - OPTIONAL_CAN_APPROVE, -} from '~/vue_merge_request_widget/components/approvals/messages'; export default { components: { @@ -25,17 +21,12 @@ export default { default: '', }, }, - computed: { - message() { - return this.canApprove ? OPTIONAL_CAN_APPROVE : OPTIONAL; - }, - }, }; </script> <template> <div class="d-flex align-items-center"> - <span class="text-muted">{{ message }}</span> + <span class="text-muted">{{ s__('mrWidget|Approval is optional') }}</span> <gl-link v-if="canApprove && helpPath" v-gl-tooltip diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/messages.js b/app/assets/javascripts/vue_merge_request_widget/components/approvals/messages.js index 1d9368f71aa..0538c38307b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/messages.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/messages.js @@ -7,5 +7,3 @@ export const FETCH_ERROR = s__( export const APPROVE_ERROR = s__('mrWidget|An error occurred while submitting your approval.'); export const UNAPPROVE_ERROR = s__('mrWidget|An error occurred while removing your approval.'); export const APPROVED_MESSAGE = s__('mrWidget|Merge request approved.'); -export const OPTIONAL_CAN_APPROVE = s__('mrWidget|No approval required; you can still approve'); -export const OPTIONAL = s__('mrWidget|No approval required'); 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 573fc388cca..af0b4087d46 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,7 +1,7 @@ <script> import { GlIcon } from '@gitlab/ui'; import { __, s__ } from '~/locale'; -import createFlash from '~/flash'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; import { visitUrl } from '~/lib/utils/url_utility'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import MRWidgetService from '../../services/mr_widget_service'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue index bce25ca20ec..b12250d1d1c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_view_button.vue @@ -60,28 +60,24 @@ export default { :main-action-link="deploymentExternalUrl" filter-key="path" > - <template slot="mainAction" slot-scope="slotProps"> + <template #mainAction="{ className }"> <review-app-link :display="appButtonText" :link="deploymentExternalUrl" - :css-class="`deploy-link js-deploy-url inline ${slotProps.className}`" + :css-class="`deploy-link js-deploy-url inline ${className}`" /> </template> - <template slot="result" slot-scope="slotProps"> + <template #result="{ result }"> <gl-link - :href="slotProps.result.external_url" + :href="result.external_url" target="_blank" rel="noopener noreferrer nofollow" class="js-deploy-url-menu-item menu-item" > - <strong class="str-truncated-100 gl-mb-0 d-block"> - {{ slotProps.result.path }} - </strong> + <strong class="str-truncated-100 gl-mb-0 d-block">{{ result.path }}</strong> - <p class="text-secondary str-truncated-100 gl-mb-0 d-block"> - {{ slotProps.result.external_url }} - </p> + <p class="text-secondary str-truncated-100 gl-mb-0 d-block">{{ result.external_url }}</p> </gl-link> </template> </filtered-search-dropdown> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_expandable_section.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_expandable_section.vue index fd999540f4a..c368399ed6f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_expandable_section.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_expandable_section.vue @@ -1,6 +1,6 @@ <script> -import { __ } from '~/locale'; import { GlButton, GlCollapse, GlIcon } from '@gitlab/ui'; +import { __ } from '~/locale'; /** * Renders header section with icon and expand button diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index a096eb1a1fe..7326bd0804d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -84,9 +84,6 @@ export default { hasCommitInfo() { return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0; }, - isTriggeredByMergeRequest() { - return Boolean(this.pipeline.merge_request); - }, isMergeRequestPipeline() { return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline); }, 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 de01821a292..936fdc9aff5 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 @@ -2,28 +2,36 @@ import { GlLink, GlSprintf, GlButton } from '@gitlab/ui'; import MrWidgetIcon from './mr_widget_icon.vue'; import Tracking from '~/tracking'; -import { s__ } from '~/locale'; +import DismissibleContainer from '~/vue_shared/components/dismissible_container.vue'; +import { + SP_TRACK_LABEL, + SP_LINK_TRACK_EVENT, + SP_SHOW_TRACK_EVENT, + SP_LINK_TRACK_VALUE, + SP_SHOW_TRACK_VALUE, + SP_HELP_CONTENT, + SP_HELP_URL, + SP_ICON_NAME, +} from '../constants'; const trackingMixin = Tracking.mixin(); -const TRACK_LABEL = 'no_pipeline_noticed'; export default { name: 'MRWidgetSuggestPipeline', - iconName: 'status_notfound', - trackLabel: TRACK_LABEL, - linkTrackValue: 30, - linkTrackEvent: 'click_link', - showTrackValue: 10, - showTrackEvent: 'click_button', - helpContent: s__( - `mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd} by simply adding a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust.`, - ), - helpURL: 'https://about.gitlab.com/blog/2019/07/12/guide-to-ci-cd-pipelines/', + SP_ICON_NAME, + SP_TRACK_LABEL, + SP_LINK_TRACK_EVENT, + SP_SHOW_TRACK_EVENT, + SP_LINK_TRACK_VALUE, + SP_SHOW_TRACK_VALUE, + SP_HELP_CONTENT, + SP_HELP_URL, components: { GlLink, GlSprintf, GlButton, MrWidgetIcon, + DismissibleContainer, }, mixins: [trackingMixin], props: { @@ -39,11 +47,19 @@ export default { type: String, required: true, }, + userCalloutsPath: { + type: String, + required: true, + }, + userCalloutFeatureId: { + type: String, + required: true, + }, }, computed: { tracking() { return { - label: TRACK_LABEL, + label: SP_TRACK_LABEL, property: this.humanAccess, }; }, @@ -54,9 +70,14 @@ export default { }; </script> <template> - <div class="mr-widget-body mr-pipeline-suggest gl-mb-3"> - <div class="gl-display-flex gl-align-items-center"> - <mr-widget-icon :name="$options.iconName" /> + <dismissible-container + class="mr-widget-body mr-pipeline-suggest gl-mb-3" + :path="userCalloutsPath" + :feature-id="userCalloutFeatureId" + @dismiss="$emit('dismiss')" + > + <template #title> + <mr-widget-icon :name="$options.SP_ICON_NAME" /> <div> <gl-sprintf :message=" @@ -76,18 +97,18 @@ export default { class="gl-ml-1" data-testid="add-pipeline-link" :data-track-property="humanAccess" - :data-track-value="$options.linkTrackValue" - :data-track-event="$options.linkTrackEvent" - :data-track-label="$options.trackLabel" + :data-track-value="$options.SP_LINK_TRACK_VALUE" + :data-track-event="$options.SP_LINK_TRACK_EVENT" + :data-track-label="$options.SP_TRACK_LABEL" > {{ content }} </gl-link> </template> </gl-sprintf> </div> - </div> + </template> <div class="row"> - <div class="col-md-5 order-md-last col-12 gl-mt-5 mt-md-n3 svg-content svg-225"> + <div class="col-md-5 order-md-last col-12 gl-mt-5 mt-md-n1 pt-md-1 svg-content svg-225"> <img data-testid="pipeline-image" :src="pipelineSvgPath" /> </div> <div class="col-md-7 order-md-first col-12"> @@ -96,11 +117,11 @@ export default { {{ s__('mrWidget|Are you adding technical debt or code vulnerabilities?') }} </strong> <p class="gl-mt-2"> - <gl-sprintf :message="$options.helpContent"> + <gl-sprintf :message="$options.SP_HELP_CONTENT"> <template #link="{ content }"> <gl-link data-testid="help" - :href="$options.helpURL" + :href="$options.SP_HELP_URL" target="_blank" class="font-size-inherit" >{{ content }} @@ -115,14 +136,14 @@ export default { variant="info" :href="pipelinePath" :data-track-property="humanAccess" - :data-track-value="$options.showTrackValue" - :data-track-event="$options.showTrackEvent" - :data-track-label="$options.trackLabel" + :data-track-value="$options.SP_SHOW_TRACK_VALUE" + :data-track-event="$options.SP_SHOW_TRACK_EVENT" + :data-track-label="$options.SP_TRACK_LABEL" > {{ __('Show me how to add a pipeline') }} </gl-button> </div> </div> </div> - </div> + </dismissible-container> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue index a0e76b151f7..dab0540f44e 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue @@ -1,22 +1,37 @@ <script> +import { GlSprintf } from '@gitlab/ui'; import tooltip from '../../vue_shared/directives/tooltip'; import { __ } from '../../locale'; export default { + i18n: { + removesBranchText: __('%{strongStart}Deletes%{strongEnd} source branch'), + tooltipTitle: __('A user with write access to the source branch selected this option'), + }, + components: { + GlSprintf, + }, directives: { tooltip, }, - created() { - this.removesBranchText = __('<strong>Deletes</strong> source branch'); - this.tooltipTitle = __('A user with write access to the source branch selected this option'); - }, }; </script> <template> <p v-once class="mr-info-list mr-links gl-mb-0"> - <span class="status-text" v-html="removesBranchText"> </span> - <i v-tooltip :title="tooltipTitle" :aria-label="tooltipTitle" class="fa fa-question-circle"> + <span class="status-text"> + <gl-sprintf :message="$options.i18n.removesBranchText"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </span> + <i + v-tooltip + :title="$options.i18n.tooltipTitle" + :aria-label="$options.i18n.tooltipTitle" + class="fa fa-question-circle" + > </i> </p> </template> 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> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue index f6e21dc1ec1..c7d9453a5c9 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue @@ -1,6 +1,6 @@ <script> -import { n__ } from '~/locale'; import { GlSkeletonLoading, GlSprintf } from '@gitlab/ui'; +import { n__ } from '~/locale'; import axios from '~/lib/utils/axios_utils'; import MrWidgetExpanableSection from '../mr_widget_expandable_section.vue'; import Poll from '~/lib/utils/poll'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue b/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue index dc16d46dd8e..b74e036d9d9 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/terraform/terraform_plan.vue @@ -1,6 +1,6 @@ <script> -import { s__ } from '~/locale'; import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; export default { name: 'TerraformPlan', |