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:
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/components')
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue24
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/state_container.vue71
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue26
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue23
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue25
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue56
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue20
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue115
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue35
34 files changed, 387 insertions, 209 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue b/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue
index 38f40e8a3c8..30a0e7c383c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue
@@ -63,6 +63,12 @@ export default {
return btn.tooltipText;
},
+ actionButtonQaSelector(btn) {
+ if (btn.dataQaSelector) {
+ return btn.dataQaSelector;
+ }
+ return 'mr_widget_extension_actions_button';
+ },
},
};
</script>
@@ -105,7 +111,7 @@ export default {
:target="btn.target"
:class="[{ 'gl-mr-3': index !== tertiaryButtons.length - 1 }, btn.class]"
:data-clipboard-text="btn.dataClipboardText"
- :data-qa-selector="btn.dataQaSelector"
+ :data-qa-selector="actionButtonQaSelector(btn)"
:data-method="btn.dataMethod"
:icon="btn.icon"
:data-testid="btn.testId || 'extension-actions-button'"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue b/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue
index 254b280bf14..f377a185879 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSprintf } from '@gitlab/ui';
+import { GlSprintf, GlLink } from '@gitlab/ui';
import { escape } from 'lodash';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { n__, s__, sprintf } from '~/locale';
@@ -9,6 +9,7 @@ const mergeCommitCount = s__('mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} m
export default {
components: {
GlSprintf,
+ GlLink,
},
mixins: [glFeatureFlagMixin()],
props: {
@@ -40,6 +41,11 @@ export default {
required: false,
default: '',
},
+ mergeCommitPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
isMerged() {
@@ -124,7 +130,9 @@ export default {
</template>
</template>
<template #mergeCommitSha>
- <span class="label-branch">{{ mergeCommitSha }}</span>
+ <gl-link :href="mergeCommitPath" class="label-branch" data-testid="merge-commit-sha">{{
+ mergeCommitSha
+ }}</gl-link>
</template>
</gl-sprintf>
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
index b1c4f7c5a7c..d7255eb6ad2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
@@ -103,7 +103,7 @@ export default {
<span v-if="approvalLeftMessage">{{ message }}</span>
<span v-else class="gl-font-weight-bold">{{ message }}</span>
<user-avatar-list
- class="gl-display-inline-block gl-vertical-align-middle"
+ class="gl-display-inline-block gl-vertical-align-middle gl-pt-1"
:img-size="24"
:items="approvers"
/>
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 e115710b5d1..30098f7619a 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
@@ -74,16 +74,12 @@ export default {
<div class="js-deployment-info deployment-info">
<template v-if="hasDeploymentMeta">
<span>{{ deployedText }}</span>
- <tooltip-on-truncate
- :title="deployment.name"
- truncate-target="child"
- class="deploy-link label-truncate"
- >
+ <tooltip-on-truncate :title="deployment.name" truncate-target="child" class="label-truncate">
<gl-link
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
- class="js-deploy-meta gl-font-sm"
+ class="js-deploy-meta gl-font-sm gl-pb-1"
>
{{ deployment.name }}
</gl-link>
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 414c5bf9691..300e2a672cb 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
@@ -13,6 +13,7 @@ import Poll from '~/lib/utils/poll';
import { normalizeHeaders } from '~/lib/utils/common_utils';
import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants';
import Actions from '../action_buttons.vue';
+import StateContainer from '../state_container.vue';
import StatusIcon from './status_icon.vue';
import ChildContent from './child_content.vue';
import { createTelemetryHub } from './telemetry';
@@ -36,6 +37,7 @@ export default {
ChildContent,
DynamicScroller,
DynamicScrollerItem,
+ StateContainer,
},
directives: {
SafeHtml: GlSafeHtmlDirective,
@@ -307,19 +309,20 @@ export default {
</script>
<template>
- <section class="media-section" data-testid="widget-extension">
- <div
+ <section
+ class="media-section"
+ data-testid="widget-extension"
+ data-qa-selector="mr_widget_extension"
+ >
+ <state-container
+ :mr="mr"
+ :status="statusIconName"
+ :is-loading="isLoadingSummary"
:class="{ 'gl-cursor-pointer': isCollapsible }"
- class="media gl-p-5"
+ class="gl-p-5"
@mousedown="onRowMouseDown"
@mouseup="onRowMouseUp"
>
- <status-icon
- :level="1"
- :name="$options.label || $options.name"
- :is-loading="isLoadingSummary"
- :icon-name="statusIconName"
- />
<div
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
data-testid="widget-extension-top-level"
@@ -352,12 +355,13 @@ export default {
:icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
category="tertiary"
data-testid="toggle-button"
+ data-qa-selector="toggle_button"
size="small"
@click="toggleCollapsed"
/>
</div>
</div>
- </div>
+ </state-container>
<div
v-if="!isCollapsed"
class="mr-widget-grouped-section gl-relative"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue
index 1eccc7de660..52c9f047b76 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue
@@ -62,7 +62,9 @@ export default {
<strong v-else v-safe-html="generateText(data.header)"></strong>
</div>
<div class="gl-display-flex">
- <status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" class="gl-pl-0" />
+ <div v-if="data.icon" class="report-block-child-icon gl-display-flex">
+ <status-icon :icon-name="data.icon.name" :size="12" class="gl-m-auto" />
+ </div>
<div class="gl-w-full">
<div class="gl-display-flex gl-flex-nowrap">
<div class="gl-flex-wrap gl-display-flex gl-w-full">
@@ -109,6 +111,7 @@ export default {
:modal-id="modalId"
:level="3"
data-testid="child-content"
+ data-qa-selector="child_content"
@clickedAction="onClickedAction"
/>
</li>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue
index dc748ba44f2..f9d0986d60d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue
@@ -49,18 +49,28 @@ export default {
<div
:class="[
$options.EXTENSION_ICON_CLASS[iconName],
- { 'mr-widget-extension-icon gl-w-6': !isLoading && level === 1 },
+ { 'gl-w-6': !isLoading && level === 1 },
{ 'gl-p-2': isLoading || level === 1 },
]"
- class="gl-rounded-full gl-mr-3 gl-relative gl-p-2"
+ class="gl-mr-3 gl-p-2"
>
- <gl-loading-icon v-if="isLoading" size="sm" inline class="gl-display-block" />
- <gl-icon
- v-else
- :name="$options.EXTENSION_ICON_NAMES[iconName]"
- :size="size"
- :aria-label="iconAriaLabel"
- class="gl-display-block"
- />
+ <div
+ class="gl-rounded-full gl-relative gl-display-flex"
+ :class="{ 'mr-widget-extension-icon': !isLoading && level === 1 }"
+ >
+ <div class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto">
+ <div class="gl-display-flex gl-m-auto gl-translate-y-n50">
+ <gl-loading-icon v-if="isLoading" size="md" inline />
+ <gl-icon
+ v-else
+ :name="$options.EXTENSION_ICON_NAMES[iconName]"
+ :size="size"
+ :aria-label="iconAriaLabel"
+ :data-qa-selector="`status_${iconName}_icon`"
+ class="gl-display-block"
+ />
+ </div>
+ </div>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
index bc84459e298..d67ff11f297 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
@@ -24,7 +24,7 @@ const nonStandardEvents = {
},
issues: {
uniqueUser: {
- expand: ['i_testing_load_performance_widget_total'],
+ expand: ['i_testing_issues_widget_total'],
},
counter: {},
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue
index 437342bf438..0c36e1ccd7f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue
@@ -13,7 +13,7 @@ export default {
</script>
<template>
- <div class="circle-icon-container gl-mr-3 align-self-start">
+ <div class="circle-icon-container gl-mr-3 align-self-start gl-mt-2">
<gl-icon :name="name" :size="24" />
</div>
</template>
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 1e1a2049414..fe69e96bd87 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
@@ -9,11 +9,10 @@ import {
GlTooltipDirective,
GlSafeHtmlDirective,
} from '@gitlab/ui';
-import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline';
import { s__, n__ } from '~/locale';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
-import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { MT_MERGE_STRATEGY } from '../constants';
@@ -31,14 +30,11 @@ export default {
PipelineMiniGraph,
TimeAgoTooltip,
TooltipOnTruncate,
- LinkedPipelinesMiniList: () =>
- import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
SafeHtml: GlSafeHtmlDirective,
},
- mixins: [mrWidgetPipelineMixin],
props: {
pipeline: {
type: Object,
@@ -172,7 +168,7 @@ export default {
</p>
</template>
<template v-else-if="!hasPipeline">
- <gl-loading-icon size="lg" />
+ <gl-loading-icon size="md" />
<p
class="gl-flex-grow-1 gl-display-flex gl-ml-3 gl-mb-0"
data-testid="monitoring-pipeline-message"
@@ -276,17 +272,15 @@ export default {
</div>
</div>
<div>
- <span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph">
- <span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
- <linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" />
- <pipeline-mini-graph
- v-if="hasStages"
- stages-class="mr-widget-pipeline-stages"
- :stages="pipeline.details.stages"
- :is-merge-train="isMergeTrain"
- />
- </span>
- <linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" />
+ <span class="gl-align-items-center gl-display-inline-flex">
+ <pipeline-mini-graph
+ v-if="pipeline.details.stages"
+ :downstream-pipelines="pipeline.triggered"
+ :is-merge-train="isMergeTrain"
+ :stages="pipeline.details.stages"
+ :upstream-pipeline="pipeline.triggered_by"
+ stages-class="mr-widget-pipeline-stages"
+ />
<pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
</span>
</div>
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 5b8acb4ebf8..3239285e53e 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,11 +1,11 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import { GlIcon } from '@gitlab/ui';
+import StatusIcon from './extensions/status_icon.vue';
export default {
components: {
- ciIcon,
- GlLoadingIcon,
+ StatusIcon,
+ GlIcon,
},
props: {
status: {
@@ -17,22 +17,20 @@ export default {
isLoading() {
return this.status === 'loading';
},
- statusObj() {
- return {
- group: this.status,
- icon: `status_${this.status}`,
- };
- },
},
};
</script>
<template>
- <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 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 class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3">
+ <div class="gl-display-flex gl-m-auto">
+ <gl-icon v-if="status === 'merged'" name="merge" :size="16" class="gl-text-blue-500" />
+ <gl-icon
+ v-else-if="status === 'closed'"
+ name="merge-request-close"
+ :size="16"
+ class="gl-text-red-500"
+ />
+ <status-icon v-else :is-loading="isLoading" :icon-name="status" :level="1" class="gl-m-0!" />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue
index 4a5a03fb598..822c5a68093 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue
@@ -1,13 +1,23 @@
<script>
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
import StatusIcon from './mr_widget_status_icon.vue';
import Actions from './action_buttons.vue';
export default {
components: {
+ GlButton,
StatusIcon,
Actions,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
isLoading: {
type: Boolean,
required: false,
@@ -24,30 +34,67 @@ export default {
default: () => [],
},
},
+ i18n: {
+ expandDetailsTooltip: __('Expand merge details'),
+ collapseDetailsTooltip: __('Collapse merge details'),
+ },
+ computed: {
+ wrapperClasses() {
+ if (this.status === 'merged') return 'gl-bg-blue-50';
+ if (this.status === 'closed') return 'gl-bg-red-50';
+ return null;
+ },
+ },
};
</script>
<template>
- <div class="mr-widget-body media">
+ <div class="mr-widget-body media" :class="wrapperClasses" v-on="$listeners">
<div v-if="isLoading" class="gl-w-full mr-conflict-loader">
- <slot name="loading"></slot>
+ <slot name="loading">
+ <div class="gl-display-flex">
+ <status-icon status="loading" />
+ <div class="media-body">
+ <slot></slot>
+ </div>
+ </div>
+ </slot>
</div>
<template v-else>
<slot name="icon">
<status-icon :status="status" />
</slot>
- <div
- :class="{ 'gl-display-flex': actions.length, 'gl-md-display-flex': !actions.length }"
- class="media-body"
- >
- <slot></slot>
+ <div class="gl-display-flex gl-w-full">
+ <div
+ :class="{ 'gl-display-flex': actions.length, 'gl-md-display-flex': !actions.length }"
+ class="media-body"
+ >
+ <slot></slot>
+ <div
+ :class="{ 'gl-flex-direction-column-reverse': !actions.length }"
+ class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
+ >
+ <slot name="actions">
+ <actions v-if="actions.length" :tertiary-buttons="actions" />
+ </slot>
+ </div>
+ </div>
<div
- :class="{ 'gl-flex-direction-column-reverse': !actions.length }"
- class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto gl-mt-1"
+ class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
>
- <slot name="actions">
- <actions v-if="actions.length" :tertiary-buttons="actions" />
- </slot>
+ <gl-button
+ v-gl-tooltip
+ :title="
+ mr.mergeDetailsCollapsed
+ ? $options.i18n.expandDetailsTooltip
+ : $options.i18n.collapseDetailsTooltip
+ "
+ :icon="mr.mergeDetailsCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
+ category="tertiary"
+ size="small"
+ class="gl-vertical-align-top"
+ @click="() => mr.toggleMergeDetails()"
+ />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue
index a45823823f0..e2a9caf5419 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue
@@ -34,7 +34,7 @@ export default {
<template>
<div class="mr-widget-body media gl-flex-wrap">
- <status-icon status="warning" />
+ <status-icon status="failed" />
<p class="media-body gl-m-0! gl-font-weight-bold gl-text-black-normal!">
{{ failedText }}
</p>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
index f74826f95d3..79e878431ed 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
@@ -1,22 +1,24 @@
<script>
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetArchived',
components: {
- statusIcon,
+ StateContainer,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
},
};
</script>
+
<template>
- <div class="mr-widget-body media">
- <div class="space-children">
- <status-icon status="warning" show-disabled-button />
- </div>
- <div class="media-body">
- <span class="gl-ml-0! gl-text-body! bold">
- {{ s__('mrWidget|Merge unavailable: merge requests are read-only on archived projects.') }}
- </span>
- </div>
- </div>
+ <state-container :mr="mr" status="failed">
+ <span class="gl-font-weight-bold">
+ {{ s__('mrWidget|Merge unavailable: merge requests are read-only on archived projects.') }}
+ </span>
+ </state-container>
</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 690acc9a6dc..3c6c2a44e70 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,5 +1,5 @@
<script>
-import { GlSkeletonLoader, GlIcon, GlSprintf } from '@gitlab/ui';
+import { GlSkeletonLoader, GlSprintf } from '@gitlab/ui';
import autoMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/auto_merge';
import autoMergeEnabledQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql';
import createFlash from '~/flash';
@@ -28,7 +28,6 @@ export default {
components: {
MrWidgetAuthor,
GlSkeletonLoader,
- GlIcon,
GlSprintf,
StateContainer,
},
@@ -151,7 +150,7 @@ export default {
};
</script>
<template>
- <state-container status="scheduled" :is-loading="loading" :actions="actions">
+ <state-container :mr="mr" status="scheduled" :is-loading="loading" :actions="actions">
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="3" width="24" height="24" rx="4" />
@@ -168,8 +167,5 @@ export default {
</gl-sprintf>
</h4>
</template>
- <template v-if="!loading" #icon>
- <gl-icon name="status_scheduled" :size="24" class="gl-text-blue-500 gl-mr-3 gl-mt-1" />
- </template>
</state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
index b0cda85f361..39c56cbb93d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -58,8 +58,8 @@ export default {
};
</script>
<template>
- <state-container status="warning" :actions="actions">
- <span class="bold gl-ml-0!">
+ <state-container :mr="mr" status="failed" :actions="actions">
+ <span class="gl-font-weight-bold">
<template v-if="mergeError">{{ mergeError }}</template>
{{ s__('mrWidget|This merge request failed to be merged automatically') }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
index e2d87d8d536..922075516f3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -1,20 +1,23 @@
<script>
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetChecking',
components: {
- statusIcon,
+ StateContainer,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
},
};
</script>
<template>
- <div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="loading" />
- <div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold">
- {{ s__('mrWidget|Checking if merge request can be merged…') }}
- </span>
- </div>
- </div>
+ <state-container :mr="mr" status="loading">
+ <span class="gl-font-weight-bold">
+ {{ s__('mrWidget|Checking if merge request can be merged…') }}
+ </span>
+ </state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 61f7d26f51e..806f8f939a6 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,16 +1,14 @@
<script>
import MrWidgetAuthorTime from '../mr_widget_author_time.vue';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetClosed',
components: {
MrWidgetAuthorTime,
- statusIcon,
+ StateContainer,
},
props: {
- /* TODO: This is providing all store and service down when it
- only needs metrics and targetBranch */
mr: {
type: Object,
required: true,
@@ -19,15 +17,12 @@ export default {
};
</script>
<template>
- <div class="mr-widget-body media">
- <status-icon status="warning" />
- <div class="media-body">
- <mr-widget-author-time
- :action-text="s__('mrWidget|Closed by')"
- :author="mr.metrics.closedBy"
- :date-title="mr.metrics.closedAt"
- :date-readable="mr.metrics.readableClosedAt"
- />
- </div>
- </div>
+ <state-container :mr="mr" status="closed">
+ <mr-widget-author-time
+ :action-text="s__('mrWidget|Closed by')"
+ :author="mr.metrics.closedBy"
+ :date-title="mr.metrics.closedAt"
+ :date-readable="mr.metrics.readableClosedAt"
+ />
+ </state-container>
</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 8abd915b93e..d60d3cfc9ea 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
@@ -86,7 +86,7 @@ export default {
};
</script>
<template>
- <state-container status="warning" :is-loading="isLoading">
+ <state-container :mr="mr" status="failed" :is-loading="isLoading">
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="7" width="150" height="16" rx="4" />
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
index 18103ac4a0e..8a7f15d8d1a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
@@ -1,16 +1,14 @@
<script>
-import { GlButton } from '@gitlab/ui';
import { stripHtml } from '~/lib/utils/text_utility';
import { sprintf, s__, n__ } from '~/locale';
import eventHub from '../../event_hub';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetFailedToMerge',
components: {
- GlButton,
- statusIcon,
+ StateContainer,
},
props: {
@@ -47,6 +45,16 @@ export default {
this.timer,
);
},
+ actions() {
+ return [
+ {
+ text: s__('mrWidget|Refresh now'),
+ onClick: () => this.refresh(),
+ testId: 'merge-request-failed-refresh-button',
+ dataQaSelector: 'merge_request_error_content',
+ },
+ ];
+ },
},
mounted() {
@@ -87,30 +95,18 @@ export default {
};
</script>
<template>
- <div class="mr-widget-body media">
- <template v-if="isRefreshing">
- <status-icon status="loading" />
- <span class="media-body bold js-refresh-label"> {{ s__('mrWidget|Refreshing now') }} </span>
- </template>
- <template v-else>
- <status-icon :show-disabled-button="true" status="warning" />
- <div class="media-body space-children">
- <span class="bold">
- <span v-if="mr.mergeError" class="has-error-message" data-testid="merge-error">
- {{ mergeError }}
- </span>
- <span v-else> {{ s__('mrWidget|Merge failed.') }} </span>
- <span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span>
- </span>
- <gl-button
- size="small"
- data-testid="merge-request-failed-refresh-button"
- data-qa-selector="merge_request_error_content"
- @click="refresh"
- >
- {{ s__('mrWidget|Refresh now') }}
- </gl-button>
- </div>
- </template>
- </div>
+ <state-container v-if="isRefreshing" :mr="mr" status="loading">
+ <span class="gl-font-weight-bold">
+ {{ s__('mrWidget|Refreshing now') }}
+ </span>
+ </state-container>
+ <state-container v-else :mr="mr" status="failed" :actions="actions">
+ <span class="gl-font-weight-bold">
+ <span v-if="mr.mergeError" class="has-error-message" data-testid="merge-error">
+ {{ mergeError }}
+ </span>
+ <span v-else> {{ s__('mrWidget|Merge failed.') }} </span>
+ <span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span>
+ </span>
+ </state-container>
</template>
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 4416123cd51..e9298b0c856 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,5 +1,5 @@
<script>
-import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlTooltipDirective } from '@gitlab/ui';
import api from '~/api';
import createFlash from '~/flash';
import { s__, __ } from '~/locale';
@@ -16,7 +16,6 @@ export default {
},
components: {
MrWidgetAuthorTime,
- GlIcon,
StateContainer,
},
props: {
@@ -49,18 +48,6 @@ export default {
const { sourceBranchRemoved, isRemovingSourceBranch } = this.mr;
return !sourceBranchRemoved && (isRemovingSourceBranch || this.isMakingRequest);
},
- shouldShowMergedButtons() {
- const {
- canRevertInCurrentMR,
- canCherryPickInCurrentMR,
- revertInForkPath,
- cherryPickInForkPath,
- } = this.mr;
-
- return (
- canRevertInCurrentMR || canCherryPickInCurrentMR || revertInForkPath || cherryPickInForkPath
- );
- },
revertTitle() {
return s__('mrWidget|Revert this merge request in a new merge request');
},
@@ -163,10 +150,7 @@ export default {
};
</script>
<template>
- <state-container :actions="actions">
- <template #icon>
- <gl-icon name="merge" :size="24" class="gl-text-blue-500 gl-mr-3 gl-mt-1" />
- </template>
+ <state-container :mr="mr" :actions="actions" status="merged">
<mr-widget-author-time
:action-text="s__('mrWidget|Merged by')"
:author="mr.metrics.mergedBy"
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 c7574a41bb8..51ac2576f75 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
@@ -4,7 +4,7 @@ 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';
+import StatusIcon from '../mr_widget_status_icon.vue';
const { transitions } = STATE_MACHINE;
const { MERGE_FAILURE } = transitions;
@@ -12,7 +12,7 @@ const { MERGE_FAILURE } = transitions;
export default {
name: 'MRWidgetMerging',
components: {
- statusIcon,
+ StatusIcon,
},
props: {
mr: {
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 659d12d1160..214d1b49732 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
@@ -9,7 +9,7 @@ import {
MR_WIDGET_MISSING_BRANCH_RESTORE,
MR_WIDGET_MISSING_BRANCH_MANUALCLI,
} from '../../i18n';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetMissingBranch',
@@ -19,7 +19,7 @@ export default {
components: {
GlIcon,
GlSprintf,
- statusIcon,
+ StatusIcon,
},
mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin],
apollo: {
@@ -71,10 +71,10 @@ export default {
</script>
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="warning" />
+ <status-icon :show-disabled-button="true" status="failed" />
<div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold js-branch-text" data-testid="widget-content">
+ <span class="gl-font-weight-bold js-branch-text" data-testid="widget-content">
<gl-sprintf :message="warning">
<template #code="{ content }">
<code>{{ content }}</code>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
index c203d2824fa..d837551a813 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
@@ -11,9 +11,9 @@ export default {
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="success" />
+ <status-icon status="success" />
<div class="media-body space-children">
- <span class="bold">
+ <span class="gl-font-weight-bold">
{{
s__(`mrWidget|Ready to be merged automatically.
Ask someone with write access to this repository to merge this request`)
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
index e99ee59b877..13920daca15 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
@@ -12,9 +12,9 @@ export default {
</script>
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="warning" />
+ <status-icon status="failed" />
<div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold">
+ <span class="gl-font-weight-bold">
{{
s__(
`mrWidget|Merge blocked: pipeline must succeed. It's waiting for a manual action to continue.`,
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 6c5fc916799..37c8d5d15f3 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
@@ -81,16 +81,19 @@ export default {
return 'loading';
}
if (!this.canPushToSourceBranch && !this.rebaseInProgress) {
- return 'warning';
+ return 'failed';
}
return 'success';
},
- showDisabledButton() {
- return ['failed', 'loading'].includes(this.status);
- },
fastForwardMergeText() {
return __('Merge blocked: the source branch must be rebased onto the target branch.');
},
+ showRebaseWithoutPipeline() {
+ return (
+ !this.mr.onlyAllowMergeIfPipelineSucceeds ||
+ (this.mr.onlyAllowMergeIfPipelineSucceeds && this.mr.allowMergeOnSkippedPipeline)
+ );
+ },
},
methods: {
rebase({ skipCi = false } = {}) {
@@ -149,7 +152,7 @@ export default {
};
</script>
<template>
- <state-container :status="status" :is-loading="isLoading">
+ <state-container :mr="mr" :status="status" :is-loading="isLoading">
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="3" width="24" height="24" rx="4" />
@@ -192,6 +195,7 @@ export default {
</template>
<template v-if="!isLoading" #actions>
<gl-button
+ v-if="showRebaseWithoutPipeline"
:loading="isMakingRequest"
variant="confirm"
size="small"
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 d507e5f232b..3cbd171a035 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
@@ -2,14 +2,14 @@
import { GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'PipelineFailed',
components: {
GlLink,
GlSprintf,
- statusIcon,
+ StatusIcon,
},
computed: {
troubleshootingDocsPath() {
@@ -26,9 +26,9 @@ export default {
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="warning" />
+ <status-icon status="failed" />
<div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold">
+ <span class="gl-font-weight-bold">
<gl-sprintf :message="$options.i18n.failedMessage">
<template #link="{ content }">
<gl-link :href="troubleshootingDocsPath" target="_blank">
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 d2c85b14999..78430abcfe9 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
@@ -680,6 +680,7 @@ export default {
:is-fast-forward-enabled="!shouldShowMergeEdit"
:commits-count="commitsCount"
:target-branch="stateData.targetBranch"
+ :merge-commit-path="mr.mergeCommitPath"
/>
</li>
<li v-if="mr.state !== 'closed'" class="gl-line-height-normal">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
index d149f5208fc..27919f90cc3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
@@ -22,7 +22,7 @@ export default {
</script>
<template>
- <state-container status="warning">
+ <state-container :mr="mr" status="failed">
<span
class="gl-font-weight-bold gl-md-mr-3 gl-flex-grow-1 gl-ml-0! gl-text-body!"
data-qa-selector="head_mismatch_content"
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 035d62eaa59..8f2e4eb2131 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
@@ -24,7 +24,7 @@ export default {
</script>
<template>
- <state-container status="warning">
+ <state-container :mr="mr" status="failed">
<span
class="gl-ml-3 gl-font-weight-bold gl-w-100 gl-flex-grow-1 gl-md-mr-3 gl-ml-0! gl-text-body!"
>
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 cf7f83c014a..0458e9dfaf5 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
@@ -163,7 +163,7 @@ export default {
</script>
<template>
- <state-container status="warning">
+ <state-container :mr="mr" status="failed">
<span class="gl-font-weight-bold gl-ml-0! gl-text-body! gl-flex-grow-1">
{{ __("Merge blocked: merge request must be marked as ready. It's still marked as draft.") }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
index f1c1bde256f..2f52ac70833 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
@@ -15,7 +15,12 @@ export default {
</script>
<template>
- <section role="region" :aria-label="__('Merge request reports')" data-testid="mr-widget-app">
+ <section
+ v-if="widgets.length"
+ role="region"
+ :aria-label="__('Merge request reports')"
+ data-testid="mr-widget-app"
+ >
<component
:is="widget"
v-for="(widget, index) in widgets"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
index 9c8819327e6..c9fc2dde0bd 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue
@@ -1,21 +1,32 @@
<script>
+import { GlButton, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { normalizeHeaders } from '~/lib/utils/common_utils';
-import { __ } from '~/locale';
+import { sprintf, __ } from '~/locale';
import Poll from '~/lib/utils/poll';
import StatusIcon from '../extensions/status_icon.vue';
-import { EXTENSION_ICON_NAMES } from '../../constants';
+import ActionButtons from '../action_buttons.vue';
+import { EXTENSION_ICONS } from '../../constants';
+import ContentSection from './widget_content_section.vue';
const FETCH_TYPE_COLLAPSED = 'collapsed';
+const FETCH_TYPE_EXPANDED = 'expanded';
export default {
components: {
+ ActionButtons,
StatusIcon,
+ GlButton,
+ GlLoadingIcon,
+ ContentSection,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
},
props: {
/**
* @param {value.collapsed} Object
- * @param {value.extended} Object
+ * @param {value.expanded} Object
*/
value: {
type: Object,
@@ -35,7 +46,7 @@ export default {
type: Function,
required: true,
},
- fetchExtendedData: {
+ fetchExpandedData: {
type: Function,
required: false,
default: undefined,
@@ -61,7 +72,16 @@ export default {
type: String,
default: 'neutral',
required: false,
- validator: (value) => Object.keys(EXTENSION_ICON_NAMES).indexOf(value) > -1,
+ validator: (value) => Object.keys(EXTENSION_ICONS).indexOf(value) > -1,
+ },
+ isCollapsible: {
+ type: Boolean,
+ required: true,
+ },
+ actionButtons: {
+ type: Array,
+ required: false,
+ default: () => [],
},
widgetName: {
type: String,
@@ -70,10 +90,22 @@ export default {
},
data() {
return {
+ isExpandedForTheFirstTime: true,
+ isCollapsed: true,
isLoading: false,
- error: null,
+ isLoadingExpandedContent: false,
+ summaryError: null,
+ contentError: null,
};
},
+ computed: {
+ collapseButtonLabel() {
+ return sprintf(this.isCollapsed ? __('Show details') : __('Hide details'));
+ },
+ summaryStatusIcon() {
+ return this.summaryError ? this.$options.failedStatusIcon : this.statusIconName;
+ },
+ },
watch: {
isLoading(newValue) {
this.$emit('is-loading', newValue);
@@ -85,12 +117,36 @@ export default {
try {
await this.fetch(this.fetchCollapsedData, FETCH_TYPE_COLLAPSED);
} catch {
- this.error = this.errorText;
+ this.summaryError = this.errorText;
}
this.isLoading = false;
},
methods: {
+ toggleCollapsed() {
+ this.isCollapsed = !this.isCollapsed;
+
+ if (this.isExpandedForTheFirstTime && typeof this.fetchExpandedData === 'function') {
+ this.isExpandedForTheFirstTime = false;
+ this.fetchExpandedContent();
+ }
+ },
+ async fetchExpandedContent() {
+ this.isLoadingExpandedContent = true;
+ this.contentError = null;
+
+ try {
+ await this.fetch(this.fetchExpandedData, FETCH_TYPE_EXPANDED);
+ } catch {
+ this.contentError = this.errorText;
+
+ // Reset these values so that we allow refetching
+ this.isExpandedForTheFirstTime = true;
+ this.isCollapsed = true;
+ }
+
+ this.isLoadingExpandedContent = false;
+ },
fetch(handler, dataType) {
const requests = this.multiPolling ? handler() : [handler];
@@ -125,6 +181,7 @@ export default {
});
},
},
+ failedStatusIcon: EXTENSION_ICONS.failed,
};
</script>
@@ -135,24 +192,58 @@ export default {
:level="1"
:name="widgetName"
:is-loading="isLoading"
- :icon-name="statusIconName"
+ :icon-name="summaryStatusIcon"
/>
<div
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
data-testid="widget-extension-top-level"
>
<div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary">
- <slot name="summary">{{ isLoading ? loadingText : summary }}</slot>
+ <span v-if="summaryError">{{ summaryError }}</span>
+ <slot v-else name="summary">{{ isLoading ? loadingText : summary }}</slot>
+ </div>
+ <action-buttons
+ v-if="actionButtons.length > 0"
+ :widget="widgetName"
+ :tertiary-buttons="actionButtons"
+ />
+ <div
+ v-if="isCollapsible"
+ class="gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6"
+ >
+ <gl-button
+ v-gl-tooltip
+ :title="collapseButtonLabel"
+ :aria-expanded="`${!isCollapsed}`"
+ :aria-label="collapseButtonLabel"
+ :icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
+ category="tertiary"
+ data-testid="toggle-button"
+ size="small"
+ @click="toggleCollapsed"
+ />
</div>
- <!-- actions will go here -->
- <!-- toggle button will go here -->
</div>
</div>
<div
+ v-if="!isCollapsed || contentError"
class="mr-widget-grouped-section gl-relative"
data-testid="widget-extension-collapsed-section"
>
- <slot name="content">{{ content }}</slot>
+ <div v-if="isLoadingExpandedContent" class="report-block-container gl-text-center">
+ <gl-loading-icon size="sm" inline /> {{ __('Loading...') }}
+ </div>
+ <content-section
+ v-else-if="contentError"
+ class="report-block-container"
+ :status-icon-name="$options.failedStatusIcon"
+ :widget-name="widgetName"
+ >
+ {{ contentError }}
+ </content-section>
+ <slot v-else name="content">
+ {{ content }}
+ </slot>
</div>
</section>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue
new file mode 100644
index 00000000000..61e3744b5dc
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue
@@ -0,0 +1,35 @@
+<script>
+import { EXTENSION_ICONS } from '../../constants';
+import StatusIcon from '../extensions/status_icon.vue';
+
+export default {
+ components: {
+ StatusIcon,
+ },
+ props: {
+ statusIconName: {
+ type: String,
+ default: '',
+ required: false,
+ validator: (value) => value === '' || Object.keys(EXTENSION_ICONS).includes(value),
+ },
+ widgetName: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-px-7">
+ <div class="gl-pl-4 gl-display-flex">
+ <status-icon
+ v-if="statusIconName"
+ :level="2"
+ :name="widgetName"
+ :icon-name="statusIconName"
+ />
+ <slot name="default"></slot>
+ </div>
+ </div>
+</template>