diff options
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget')
10 files changed, 236 insertions, 45 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js b/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js index 66de4f8b682..29d067a46a6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/constants.js @@ -6,6 +6,7 @@ export const RUNNING = 'running'; export const SUCCESS = 'success'; export const FAILED = 'failed'; export const CANCELED = 'canceled'; +export const SKIPPED = 'skipped'; // ACTION STATUSES export const STOPPING = 'stopping'; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue index 2f922b990d9..390469dec24 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue @@ -4,7 +4,15 @@ import { __ } from '~/locale'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import MemoryUsage from './memory_usage.vue'; -import { MANUAL_DEPLOY, WILL_DEPLOY, RUNNING, SUCCESS, FAILED, CANCELED } from './constants'; +import { + MANUAL_DEPLOY, + WILL_DEPLOY, + RUNNING, + SUCCESS, + FAILED, + CANCELED, + SKIPPED, +} from './constants'; export default { name: 'DeploymentInfo', @@ -38,6 +46,7 @@ export default { [SUCCESS]: __('Deployed to'), [FAILED]: __('Failed to deploy to'), [CANCELED]: __('Canceled deployment to'), + [SKIPPED]: __('Skipped deployment to'), }, computed: { deployTimeago() { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue index d5fdbe726e9..6628ab7be83 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue @@ -7,12 +7,14 @@ import { GlDropdownSectionHeader, GlDropdownItem, GlTooltipDirective, + GlModalDirective, } from '@gitlab/ui'; import { n__, s__, sprintf } from '~/locale'; import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility'; import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import MrWidgetIcon from './mr_widget_icon.vue'; +import MrWidgetHowToMergeModal from './mr_widget_how_to_merge_modal.vue'; export default { name: 'MRWidgetHeader', @@ -20,6 +22,7 @@ export default { clipboardButton, TooltipOnTruncate, MrWidgetIcon, + MrWidgetHowToMergeModal, GlButton, GlDropdown, GlDropdownSectionHeader, @@ -27,6 +30,7 @@ export default { }, directives: { GlTooltip: GlTooltipDirective, + GlModalDirective, }, props: { mr: { @@ -82,6 +86,9 @@ export default { ) : ''; }, + isFork() { + return this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath; + }, }, }; </script> @@ -140,13 +147,22 @@ export default { </gl-button> </span> <gl-button + v-gl-modal-directive="'modal-merge-info'" :disabled="mr.sourceBranchRemoved" - data-target="#modal_merge_info" - data-toggle="modal" class="js-check-out-branch gl-mr-3" > {{ s__('mrWidget|Check out branch') }} </gl-button> + <mr-widget-how-to-merge-modal + :is-fork="isFork" + :can-merge="mr.canMerge" + :source-branch="mr.sourceBranch" + :source-project="mr.sourceProject" + :source-project-path="mr.sourceProjectFullPath" + :target-branch="mr.targetBranch" + :source-project-default-url="mr.sourceProjectDefaultUrl" + :reviewing-docs-path="mr.reviewingDocsPath" + /> </template> <gl-dropdown v-gl-tooltip diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue new file mode 100644 index 00000000000..957356eab27 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue @@ -0,0 +1,166 @@ +<script> +/* eslint-disable @gitlab/require-i18n-strings */ +import { GlModal, GlLink, GlSprintf } from '@gitlab/ui'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import { __ } from '~/locale'; + +export default { + i18n: { + steps: { + step1: { + label: __('Step 1.'), + help: __('Fetch and check out the branch for this merge request'), + }, + step2: { + label: __('Step 2.'), + help: __('Review the changes locally'), + }, + step3: { + label: __('Step 3.'), + help: __('Merge the branch and fix any conflicts that come up'), + }, + step4: { + label: __('Step 4.'), + help: __('Push the result of the merge to GitLab'), + }, + }, + copyCommands: __('Copy commands'), + tip: __( + '%{strongStart}Tip:%{strongEnd} You can also checkout merge requests locally by %{linkStart}following these guidelines%{linkEnd}', + ), + title: __('Check out, review, and merge locally'), + }, + components: { + GlModal, + ClipboardButton, + GlLink, + GlSprintf, + }, + props: { + canMerge: { + type: Boolean, + required: false, + default: false, + }, + isFork: { + type: Boolean, + required: false, + default: false, + }, + sourceBranch: { + type: String, + required: false, + default: '', + }, + sourceProjectPath: { + type: String, + required: false, + default: '', + }, + targetBranch: { + type: String, + required: false, + default: '', + }, + sourceProjectDefaultUrl: { + type: String, + required: false, + default: '', + }, + reviewingDocsPath: { + type: String, + required: false, + default: null, + }, + }, + computed: { + mergeInfo1() { + return this.isFork + ? `git fetch "${this.sourceProjectDefaultUrl}" ${this.sourceBranch}\ngit checkout -b "${this.sourceProjectPath}-${this.sourceBranch}" FETCH_HEAD` + : `git fetch origin\ngit checkout -b "${this.sourceBranch}" "origin/${this.sourceBranch}"`; + }, + mergeInfo2() { + return this.isFork + ? `git fetch origin\ngit checkout "${this.targetBranch}"\ngit merge --no-ff "${this.sourceProjectPath}-${this.sourceBranch}"` + : `git fetch origin\ngit checkout "${this.targetBranch}"\ngit merge --no-ff " ${this.sourceBranch}"`; + }, + mergeInfo3() { + return this.canMerge + ? `git push origin "${this.targetBranch}"` + : __('Note that pushing to GitLab requires write access to this repository.'); + }, + }, +}; +</script> + +<template> + <gl-modal modal-id="modal-merge-info" :title="$options.i18n.title" no-fade hide-footer> + <p> + <strong> + {{ $options.i18n.steps.step1.label }} + </strong> + {{ $options.i18n.steps.step1.help }} + </p> + <div class="gl-display-flex"> + <pre class="gl-overflow-scroll gl-w-full" data-testid="how-to-merge-instructions">{{ + mergeInfo1 + }}</pre> + <clipboard-button + :text="mergeInfo1" + :title="$options.i18n.copyCommands" + class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0" + /> + </div> + + <p> + <strong> + {{ $options.i18n.steps.step2.label }} + </strong> + {{ $options.i18n.steps.step2.help }} + </p> + <p> + <strong> + {{ $options.i18n.steps.step3.label }} + </strong> + {{ $options.i18n.steps.step3.help }} + </p> + <div class="gl-display-flex"> + <pre class="gl-overflow-scroll gl-w-full" data-testid="how-to-merge-instructions">{{ + mergeInfo2 + }}</pre> + <clipboard-button + :text="mergeInfo2" + :title="$options.i18n.copyCommands" + class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0" + /> + </div> + <p> + <strong> + {{ $options.i18n.steps.step4.label }} + </strong> + {{ $options.i18n.steps.step4.help }} + </p> + <div class="gl-display-flex"> + <pre class="gl-overflow-scroll gl-w-full" data-testid="how-to-merge-instructions">{{ + mergeInfo3 + }}</pre> + <clipboard-button + :text="mergeInfo3" + :title="$options.i18n.copyCommands" + class="gl-shadow-none! gl-bg-transparent! gl-flex-shrink-0" + /> + </div> + <p v-if="reviewingDocsPath"> + <gl-sprintf :message="$options.i18n.tip"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + <template #link="{ content }"> + <gl-link class="gl-display-inline-block" :href="reviewingDocsPath" target="_blank">{{ + content + }}</gl-link> + </template> + </gl-sprintf> + </p> + </gl-modal> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue index 55efd7e7d3b..dffe3cab904 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline_container.vue @@ -1,7 +1,6 @@ <script> import { isNumber } from 'lodash'; import ArtifactsApp from './artifacts_list_app.vue'; -import Deployment from './deployment/deployment.vue'; import MrWidgetContainer from './mr_widget_container.vue'; import MrWidgetPipeline from './mr_widget_pipeline.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -18,7 +17,7 @@ export default { name: 'MrWidgetPipelineContainer', components: { ArtifactsApp, - Deployment, + Deployment: () => import('./deployment/deployment.vue'), MrWidgetContainer, MrWidgetPipeline, MergeTrainPositionIndicator: () => diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue index 82566682bca..bc23ca6b1fc 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue @@ -1,10 +1,11 @@ <script> -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import ciIcon from '../../vue_shared/components/ci_icon.vue'; export default { components: { ciIcon, + GlButton, GlLoadingIcon, }, props: { @@ -32,21 +33,23 @@ export default { }; </script> <template> - <div class="d-flex align-self-start"> + <div class="gl-display-flex gl-align-self-start"> <div class="square s24 h-auto d-flex-center gl-mr-3"> - <div v-if="isLoading" class="mr-widget-icon d-inline-flex"> - <gl-loading-icon size="md" class="mr-loading-icon d-inline-flex" /> + <div v-if="isLoading" class="mr-widget-icon gl-display-inline-flex"> + <gl-loading-icon size="md" class="mr-loading-icon gl-display-inline-flex" /> </div> <ci-icon v-else :status="statusObj" :size="24" /> </div> - <button + <gl-button v-if="showDisabledButton" type="button" - class="js-disabled-merge-button btn btn-success btn-sm" - disabled="true" + category="primary" + variant="success" + class="js-disabled-merge-button" + :disabled="true" > {{ s__('mrWidget|Merge') }} - </button> + </gl-button> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue index d421b744fa1..2df03fbc679 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue @@ -1,6 +1,7 @@ <script> import $ from 'jquery'; import { escape } from 'lodash'; +import { GlButton, GlModalDirective } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; import { mouseenter, debouncedMouseleave, togglePopover } from '~/shared/popover'; import StatusIcon from '../mr_widget_status_icon.vue'; @@ -9,6 +10,10 @@ export default { name: 'MRWidgetConflicts', components: { StatusIcon, + GlButton, + }, + directives: { + GlModalDirective, }, props: { /* TODO: This is providing all store and service down when it @@ -89,22 +94,21 @@ To merge this request, first rebase locally.`) </span> </span> <span v-if="showResolveButton" ref="popover"> - <a + <gl-button :href="mr.conflictResolutionPath" :disabled="mr.sourceBranchProtected" - class="js-resolve-conflicts-button btn btn-default btn-sm" + class="js-resolve-conflicts-button" > {{ s__('mrWidget|Resolve conflicts') }} - </a> + </gl-button> </span> - <button + <gl-button v-if="mr.canMerge" - class="js-merge-locally-button btn btn-default btn-sm" - data-toggle="modal" - data-target="#modal_merge_info" + v-gl-modal-directive="'modal-merge-info'" + class="js-merge-locally-button" > {{ s__('mrWidget|Merge locally') }} - </button> + </gl-button> </template> </div> </div> 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 ff0d065c71d..1c9909e7178 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 @@ -1,10 +1,11 @@ <script> -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { GlIcon, GlTooltipDirective, GlFormCheckbox } from '@gitlab/ui'; import { SQUASH_BEFORE_MERGE } from '../../i18n'; export default { components: { GlIcon, + GlFormCheckbox, }, directives: { GlTooltip: GlTooltipDirective, @@ -32,32 +33,23 @@ export default { tooltipTitle() { return this.isDisabled ? this.$options.i18n.tooltipTitle : null; }, - tooltipFocusable() { - return this.isDisabled ? '0' : null; - }, }, }; </script> <template> - <div class="inline"> - <label + <div class="gl-display-flex gl-align-items-center"> + <gl-form-checkbox v-gl-tooltip - :class="{ 'gl-text-gray-400': isDisabled }" - :tabindex="tooltipFocusable" - data-testid="squashLabel" + :checked="value" + :disabled="isDisabled" + name="squash" + class="qa-squash-checkbox js-squash-checkbox gl-mb-0 gl-mr-2" :title="tooltipTitle" + @change="checked => $emit('input', checked)" > - <input - :checked="value" - :disabled="isDisabled" - type="checkbox" - name="squash" - class="qa-squash-checkbox js-squash-checkbox" - @change="$emit('input', $event.target.checked)" - /> {{ $options.i18n.checkboxLabel }} - </label> + </gl-form-checkbox> <a v-if="helpPath" v-gl-tooltip 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 190d790f584..433dcf2e219 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 @@ -16,7 +16,6 @@ import WidgetHeader from './components/mr_widget_header.vue'; import WidgetSuggestPipeline from './components/mr_widget_suggest_pipeline.vue'; import WidgetMergeHelp from './components/mr_widget_merge_help.vue'; import MrWidgetPipelineContainer from './components/mr_widget_pipeline_container.vue'; -import Deployment from './components/deployment/deployment.vue'; import WidgetRelatedLinks from './components/mr_widget_related_links.vue'; import MrWidgetAlertMessage from './components/mr_widget_alert_message.vue'; import MergedState from './components/states/mr_widget_merged.vue'; @@ -63,7 +62,6 @@ export default { 'mr-widget-suggest-pipeline': WidgetSuggestPipeline, 'mr-widget-merge-help': WidgetMergeHelp, MrWidgetPipelineContainer, - Deployment, 'mr-widget-related-links': WidgetRelatedLinks, MrWidgetAlertMessage, 'mr-widget-merged': MergedState, @@ -155,10 +153,7 @@ export default { }, shouldSuggestPipelines() { return ( - gon.features?.suggestPipeline && - !this.mr.hasCI && - this.mr.mergeRequestAddCiConfigPath && - !this.mr.isDismissedSuggestPipeline + !this.mr.hasCI && this.mr.mergeRequestAddCiConfigPath && !this.mr.isDismissedSuggestPipeline ); }, shouldRenderCodeQuality() { diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 8b235b20ad4..f50b6caf0f5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -220,6 +220,7 @@ export default class MergeRequestStore { this.sourceProjectFullPath = data.source_project_full_path; this.mergeRequestPipelinesHelpPath = data.merge_request_pipelines_docs_path; this.conflictsDocsPath = data.conflicts_docs_path; + this.reviewingDocsPath = data.reviewing_and_managing_merge_requests_docs_path; this.ciEnvironmentsStatusPath = data.ci_environments_status_path; this.securityApprovalsHelpPagePath = data.security_approvals_help_page_path; this.eligibleApproversDocsPath = data.eligible_approvers_docs_path; @@ -229,6 +230,7 @@ export default class MergeRequestStore { this.pipelinesEmptySvgPath = data.pipelines_empty_svg_path; this.humanAccess = data.human_access; this.newPipelinePath = data.new_project_pipeline_path; + this.sourceProjectDefaultUrl = data.source_project_default_url; this.userCalloutsPath = data.user_callouts_path; this.suggestPipelineFeatureId = data.suggest_pipeline_feature_id; this.isDismissedSuggestPipeline = data.is_dismissed_suggest_pipeline; @@ -240,6 +242,10 @@ export default class MergeRequestStore { this.baseBlobPath = blobPath.base_path || ''; this.codequalityHelpPath = data.codequality_help_path; this.codeclimate = data.codeclimate; + + // Security reports + this.sastComparisonPath = data.sast_comparison_path; + this.secretScanningComparisonPath = data.secret_scanning_comparison_path; } get isNothingToMergeState() { |