Commit bc5a9206 authored by Clement Ho's avatar Clement Ho Committed by Filipa Lacerda

Redesign for mr widget info and pipelines section

parent e0c0ce28
......@@ -79,66 +79,62 @@ export default {
</script>
<template>
<div class="mr-widget-heading deploy-heading">
<div class="mr-widget-heading deploy-heading append-bottom-default">
<div class="ci-widget media">
<div class="ci-status-icon ci-status-icon-success">
<span class="js-icon-link icon-link">
<status-icon status="success" />
</span>
</div>
<div class="media-body">
<div class="deploy-body">
<template v-if="hasDeploymentMeta">
<span>
Deployed to
</span>
<a
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="deploy-link js-deploy-meta"
<div class="deployment-info">
<template v-if="hasDeploymentMeta">
<span>
Deployed to
</span>
<a
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="deploy-link js-deploy-meta"
>
{{ deployment.name }}
</a>
</template>
<span
v-tooltip
v-if="hasDeploymentTime"
:title="deployment.deployed_at_formatted"
class="js-deploy-time"
>
{{ deployment.name }}
</a>
</template>
<template v-if="hasExternalUrls">
<span>
on
{{ deployTimeago }}
</span>
<memory-usage
v-if="hasMetrics"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
<div>
<a
v-if="hasExternalUrls"
:href="deployment.external_url"
target="_blank"
rel="noopener noreferrer nofollow"
class="deploy-link js-deploy-url"
class="deploy-link js-deploy-url btn btn-default btn-sm inline"
>
{{ deployment.external_url_formatted }}
<icon
:size="16"
name="external-link"
/>
<span>
View app
<icon name="external-link" />
</span>
</a>
</template>
<span
v-tooltip
v-if="hasDeploymentTime"
:title="deployment.deployed_at_formatted"
class="js-deploy-time"
>
{{ deployTimeago }}
</span>
<loading-button
v-if="deployment.stop_url"
:loading="isStopping"
container-class="btn btn-default btn-sm prepend-left-default"
label="Stop environment"
@click="stopEnvironment"
/>
<loading-button
v-if="deployment.stop_url"
:loading="isStopping"
container-class="btn btn-default btn-sm inline prepend-left-4"
title="Stop environment"
@click="stopEnvironment"
>
<icon name="stop" />
</loading-button>
</div>
</div>
<memory-usage
v-if="hasMetrics"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
</div>
</div>
......
......@@ -2,7 +2,7 @@
import tooltip from '~/vue_shared/directives/tooltip';
import { n__ } from '~/locale';
import { webIDEUrl } from '~/lib/utils/url_utility';
import icon from '~/vue_shared/components/icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default {
......@@ -11,7 +11,7 @@ export default {
tooltip,
},
components: {
icon,
Icon,
clipboardButton,
},
props: {
......@@ -54,104 +54,114 @@ export default {
};
</script>
<template>
<div class="mr-source-target">
<div class="normal">
<strong>
{{ s__("mrWidget|Request to merge") }}
<span
:class="{ 'label-truncated': isSourceBranchLong }"
:title="isSourceBranchLong ? mr.sourceBranch : ''"
:v-tooltip="isSourceBranchLong"
class="label-branch js-source-branch"
data-placement="bottom"
v-html="mr.sourceBranchLink"
>
</span>
<div class="mr-source-target append-bottom-default">
<div class="git-merge-icon-container append-right-default">
<icon name="git-merge" />
</div>
<div class="git-merge-container d-flex">
<div class="normal">
<strong>
{{ s__("mrWidget|Request to merge") }}
<span
:class="{ 'label-truncated': isSourceBranchLong }"
:title="isSourceBranchLong ? mr.sourceBranch : ''"
:v-tooltip="isSourceBranchLong"
class="label-branch js-source-branch"
data-placement="bottom"
v-html="mr.sourceBranchLink"
>
</span>
<clipboard-button
:text="branchNameClipboardData"
:title="__('Copy branch name to clipboard')"
css-class="btn-default btn-transparent btn-clipboard"
/>
<clipboard-button
:text="branchNameClipboardData"
:title="__('Copy branch name to clipboard')"
css-class="btn-default btn-transparent btn-clipboard"
/>
{{ s__("mrWidget|into") }}
{{ s__("mrWidget|into") }}
<span
:v-tooltip="isTargetBranchLong"
:class="{ 'label-truncatedtooltip': isTargetBranchLong }"
:title="isTargetBranchLong ? mr.targetBranch : ''"
class="label-branch"
data-placement="bottom"
>
<a
:href="mr.targetBranchTreePath"
class="js-target-branch"
<span
:v-tooltip="isTargetBranchLong"
:class="{ 'label-truncatedtooltip': isTargetBranchLong }"
:title="isTargetBranchLong ? mr.targetBranch : ''"
class="label-branch"
data-placement="bottom"
>
{{ mr.targetBranch }}
</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count"
>
(<a :href="mr.targetBranchPath">{{ commitsText }}</a>)
</span>
</div>
<a
:href="mr.targetBranchTreePath"
class="js-target-branch"
>
{{ mr.targetBranch }}
</a>
</span>
</strong>
<div
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count"
>
<span class="monospace">{{ mr.sourceBranch }}</span>
is {{ commitsText }}
<span class="monospace">{{ mr.targetBranch }}</span>
</div>
</div>
<div v-if="mr.isOpen">
<a
v-if="!mr.sourceBranchRemoved"
:href="webIdePath"
class="btn btn-sm btn-default inline js-web-ide"
>
{{ s__("mrWidget|Web IDE") }}
</a>
<button
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
class="btn btn-sm btn-default inline js-check-out-branch"
type="button"
<div
v-if="mr.isOpen"
class="branch-actions"
>
{{ s__("mrWidget|Check out branch") }}
</button>
<span class="dropdown prepend-left-10">
<a
v-if="!mr.sourceBranchRemoved"
:href="webIdePath"
class="btn btn-default inline js-web-ide d-none d-md-inline-block"
>
{{ s__("mrWidget|Open in Web IDE") }}
</a>
<button
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
class="btn btn-default inline js-check-out-branch"
type="button"
class="btn btn-sm inline dropdown-toggle"
data-toggle="dropdown"
aria-label="Download as"
aria-haspopup="true"
aria-expanded="false"
>
<icon name="download" />
<i
class="fa fa-caret-down"
aria-hidden="true">
</i>
{{ s__("mrWidget|Check out branch") }}
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a
:href="mr.emailPatchesPath"
class="js-download-email-patches"
download
>
{{ s__("mrWidget|Email patches") }}
</a>
</li>
<li>
<a
:href="mr.plainDiffPath"
class="js-download-plain-diff"
download
>
{{ s__("mrWidget|Plain diff") }}
</a>
</li>
</ul>
</span>
<span class="dropdown prepend-left-10">
<button
type="button"
class="btn inline dropdown-toggle"
data-toggle="dropdown"
aria-label="Download as"
aria-haspopup="true"
aria-expanded="false"
>
<icon name="download" />
<i
class="fa fa-caret-down"
aria-hidden="true">
</i>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a
:href="mr.emailPatchesPath"
class="js-download-email-patches"
download
>
{{ s__("mrWidget|Email patches") }}
</a>
</li>
<li>
<a
:href="mr.plainDiffPath"
class="js-download-plain-diff"
download
>
{{ s__("mrWidget|Plain diff") }}
</a>
</li>
</ul>
</span>
</div>
</div>
</div>
</template>
......@@ -26,6 +26,10 @@ export default {
type: String,
required: false,
},
sourceBranchLink: {
type: String,
required: false,
},
},
computed: {
hasPipeline() {
......@@ -54,12 +58,18 @@ export default {
<template>
<div
v-if="hasPipeline || hasCIError"
class="mr-widget-heading"
class="mr-widget-heading append-bottom-default"
>
<div class="ci-widget media">
<template v-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
<icon name="status_failed" />
<div
class="add-border ci-status-icon ci-status-icon-failed ci-error
js-ci-error append-right-default"
>
<icon
:size="32"
name="status_failed_borderless"
/>
</div>
<div class="media-body">
Could not connect to the CI server. Please check your settings and try again
......@@ -68,50 +78,66 @@ export default {
<template v-else-if="hasPipeline">
<a
:href="status.details_path"
class="append-right-10"
class="align-self-start append-right-default"
>
<ci-icon :status="status" />
<ci-icon
:status="status"
:size="32"
:borderless="true"
class="add-border"
/>
</a>
<div class="ci-widget-container d-flex">
<div class="ci-widget-content">
<div class="media-body">
<div class="font-weight-bold">
Pipeline
<a
:href="pipeline.path"
class="pipeline-id font-weight-normal pipeline-number"
>#{{ pipeline.id }}</a>
<div class="media-body">
Pipeline
<a
:href="pipeline.path"
class="pipeline-id"
>
#{{ pipeline.id }}
</a>
{{ pipeline.details.status.label }}
{{ pipeline.details.status.label }}
<template v-if="hasCommitInfo">
for
<a
:href="pipeline.commit.commit_path"
class="commit-sha js-commit-link"
>
{{ pipeline.commit.short_id }}</a>.
</template>
<span class="mr-widget-pipeline-graph">
<span
v-if="hasStages"
class="stage-cell"
>
<template v-if="hasCommitInfo">
for
<a
:href="pipeline.commit.commit_path"
class="commit-sha js-commit-link font-weight-normal"
>
{{ pipeline.commit.short_id }}</a>
on
<span
class="label-branch"
v-html="sourceBranchLink"
>
</span>
</template>
</div>
<div
v-for="(stage, i) in pipeline.details.stages"
:key="i"
class="stage-container dropdown js-mini-pipeline-graph"
v-if="pipeline.coverage"
class="coverage"
>
<pipeline-stage :stage="stage" />
Coverage {{ pipeline.coverage }}%
</div>
</div>
</div>
<div>
<span class="mr-widget-pipeline-graph">
<span
v-if="hasStages"
class="stage-cell"
>
<div
v-for="(stage, i) in pipeline.details.stages"
:key="i"
class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages"
>
<pipeline-stage :stage="stage" />
</div>
</span>
</span>
</span>
<template v-if="pipeline.coverage">
Coverage {{ pipeline.coverage }}%
</template>
</div>
</div>
</template>
</div>
......
......@@ -43,6 +43,7 @@
<ci-icon
v-else
:status="statusObj"
:size="24"
/>
<button
......
......@@ -252,41 +252,44 @@ export default {
:pipeline="mr.pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
:source-branch-link="mr.sourceBranchLink"
/>
<deployment
v-for="deployment in mr.deployments"
:key="deployment.id"
:deployment="deployment"
/>
<div class="mr-widget-section">
<component
:is="componentName"
:mr="mr"
:service="service"
/>
<div class="mr-section-container">
<div class="mr-widget-section">
<component
:is="componentName"
:mr="mr"
:service="service"
/>
<section
v-if="mr.allowCollaboration"
class="mr-info-list mr-links"
>
{{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
</section>
<section
v-if="mr.allowCollaboration"
class="mr-info-list mr-links"
>
{{ s__("mrWidget|Allows commits from members who can merge to the target branch") }}
</section>
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:state="mr.state"
:related-links="mr.relatedLinks"
/>
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:state="mr.state"
:related-links="mr.relatedLinks"
/>
<source-branch-removal-status
v-if="shouldRenderSourceBranchRemovalStatus"
/>
</div>
<div
v-if="shouldRenderMergeHelp"
class="mr-widget-footer"
>
<mr-widget-merge-help />
<source-branch-removal-status
v-if="shouldRenderSourceBranchRemovalStatus"
/>
</div>
<div
v-if="shouldRenderMergeHelp"
class="mr-widget-footer"
>
<mr-widget-merge-help />
</div>
</div>
</div>
</template>
......@@ -3,12 +3,20 @@
svg {
fill: $green-500;
}
&.add-border {
@include borderless-status-icon($green-500);
}
}
.ci-status-icon-failed {
svg {
fill: $gl-danger;
}
&.add-border {
@include borderless-status-icon($red-500);
}
}
.ci-status-icon-pending,
......@@ -17,12 +25,20 @@
svg {
fill: $orange-500;
}
&.add-border {
@include borderless-status-icon($orange-500);
}
}
.ci-status-icon-running {
svg {
fill: $blue-400;
}
&.add-border {
@include borderless-status-icon($blue-400);
}
}
.ci-status-icon-canceled,
......@@ -30,6 +46,10 @@
svg {
fill: $gl-text-color;
}
&.add-border {
@include borderless-status-icon($gl-text-color);
}
}
.ci-status-icon-created,
......@@ -38,6 +58,10 @@
svg {
fill: $gray-darkest;
}
&.add-border {
@include borderless-status-icon($gray-darkest);
}
}
.ci-status-icon-manual {
......
......@@ -232,3 +232,10 @@
word-break: break-word;
max-width: 100%;
}
@mixin borderless-status-icon($color) {
svg {
border: 1px solid $color;
border-radius: 50%;
}
}
......@@ -350,7 +350,8 @@ code {
}
.commit-sha,
.ref-name {
.ref-name,
.pipeline-number {
@extend .monospace;
font-size: 95%;
}
......
......@@ -743,6 +743,7 @@ Pipeline Graph
*/
$stage-hover-bg: $gray-darker;
$ci-action-icon-size: 22px;
$ci-action-icon-size-lg: 24px;
$pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size: 18px;
$ci-action-dropdown-button-size: 24px;
......
......@@ -15,16 +15,38 @@
}
}
.mr-widget-heading {
position: relative;
border: 1px solid $border-color;
border-radius: 4px;
&:not(.deploy-heading)::before {
content: '';
border-left: 1px solid $theme-gray-200;
position: absolute;
left: 32px;
top: -17px;
height: 16px;
}
}
.mr-section-container {
border: 1px solid $border-color;
border-radius: $border-radius-default;
border-top: 0;
}
.mr-widget-heading,
.mr-widget-section,
.mr-widget-footer {
padding: $gl-padding;
}
.mr-state-widget {
color: $gl-text-color;
border: 1px solid $border-color;
border-radius: 2px;
line-height: 28px;
.mr-widget-heading,
.mr-widget-section,
.mr-widget-footer {
padding: $gl-padding;
border-top: solid 1px $border-color;
}
......@@ -124,10 +146,17 @@
.ci-widget {
color: $gl-text-color;
display: flex;
align-items: center;
justify-content: space-between;
@include media-breakpoint-down(xs) {
flex-wrap: wrap;
}
.ci-widget-content {
display: flex;
align-items: center;
}
}
.mr-widget-icon {
......@@ -136,8 +165,6 @@
}
.ci-status-icon svg {
width: $status-icon-size;
height: $status-icon-size;
margin: 3px 0;
position: relative;
overflow: visible;
......@@ -145,8 +172,6 @@
}
.mr-widget-pipeline-graph {
padding: 0 4px;
.dropdown-menu {
z-index: 300;
}
......@@ -157,7 +182,7 @@
}
.normal {
line-height: 28px;
flex: 1;
}
.capitalize {
......@@ -168,7 +193,7 @@
@extend .ref-name;
color: $gl-text-color;
font-weight: $gl-font-weight-bold;
font-weight: normal;
overflow: hidden;
word-break: break-all;
......@@ -192,6 +217,8 @@
}
.mr-widget-body {
line-height: 28px;
@include clearfix;
&.media > *:first-child {
......@@ -474,18 +501,66 @@
}
}
.merge-request-details .content-block {
border-bottom: 0;
}
.mr-source-target {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
background-color: $gray-light;
border-radius: $border-radius-default $border-radius-default 0 0;
padding: $gl-padding / 2 $gl-padding;
border-radius: $border-radius-default;
padding: $gl-padding;
border: 1px solid $border-color;
min-height: 69px;
@include media-breakpoint-up(md) {
align-items: center;
}
.dropdown-toggle .fa {
color: $gl-text-color;
}
.git-merge-icon-container {
border: 1px solid $theme-gray-400;
border-radius: 50%;
height: 32px;
width: 32px;
color: $theme-gray-700;
line-height: 28px;
.ic-git-merge {
vertical-align: middle;
width: 31px;
}
}
.git-merge-container {
justify-content: space-between;
flex: 1;
flex-direction: row;
align-items: center;
@include media-breakpoint-down(md) {
flex-direction: column;
align-items: flex-start;
.branch-actions {
margin-top: 16px;
}
}
@include media-breakpoint-up(lg) {
.branch-actions {
align-self: center;
}
}
}
.diverged-commits-count {
color: $gl-text-color-secondary;
font-size: 12px;
}
}
.card-new-merge-request {
......@@ -720,13 +795,25 @@
}
.deploy-heading {
margin-top: -19px;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-color: $gray-light;
@include media-breakpoint-up(md) {
padding: $gl-padding-8 $gl-padding;
}
.media-body {
min-width: 0;
font-size: 12px;
margin-left: 48px;
}
}
.deploy-body {
display: flex;
align-items: center;
flex-wrap: wrap;
@include media-breakpoint-up(xs) {
......@@ -734,6 +821,15 @@
white-space: nowrap;
}
@include media-breakpoint-down(md) {
flex-direction: column;
align-items: flex-start;
.deployment-info {
margin-bottom: $gl-padding;
}
}
> *:not(:last-child) {
margin-right: .3em;
}
......@@ -741,18 +837,22 @@
svg {
vertical-align: text-top;
}
}
.deploy-link {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 100px;
max-width: 150px;
.deployment-info {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 100px;
@include media-breakpoint-up(xs) {
min-width: 0;
max-width: 100%;
@include media-breakpoint-up(xs) {
min-width: 0;
max-width: 100%;
}
}
.btn svg {
fill: $theme-gray-700;
}
}
......@@ -772,3 +872,33 @@
}
}
}
.ci-widget-container {
justify-content: space-between;
flex: 1;
flex-direction: row;
@include media-breakpoint-down(md) {
flex-direction: column;
.stage-cell .stage-container {
margin-top: 16px;
}
.dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu {
transform: initial;
}
}
.coverage {
font-size: 12px;
color: $theme-gray-700;
line-height: initial;
}
.mini-pipeline-graph-dropdown-toggle,
.stage-cell .mini-pipeline-graph-dropdown-toggle svg {
height: $ci-action-icon-size-lg;
width: $ci-action-icon-size-lg;
}
}
......@@ -301,6 +301,21 @@
border-bottom: 2px solid $border-color;
}
}
//delete when all pipelines are updated to new size
&.mr-widget-pipeline-stages {
+ .stage-container {
margin-left: 4px;
}
&:not(:last-child) {
&::after {
width: 4px;
right: -4px;
top: 11px;
}
}
}
}
}
......
......@@ -153,7 +153,7 @@ describe('Deployment component', () => {
it('renders external URL', () => {
expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url);
expect(el.querySelector('.js-deploy-url').innerText).toContain(deploymentMockData.external_url_formatted);
expect(el.querySelector('.js-deploy-url').innerText).toContain('View app');
});
it('renders stop button', () => {
......
......@@ -145,7 +145,7 @@ describe('MRWidgetHeader', () => {
it('renders web ide button', () => {
const button = vm.$el.querySelector('.js-web-ide');
expect(button.textContent.trim()).toEqual('Web IDE');
expect(button.textContent.trim()).toEqual('Open in Web IDE');
expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
});
......@@ -154,7 +154,7 @@ describe('MRWidgetHeader', () => {
const button = vm.$el.querySelector('.js-web-ide');
expect(button.textContent.trim()).toEqual('Web IDE');
expect(button.textContent.trim()).toEqual('Open in Web IDE');
expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
});
......@@ -253,8 +253,8 @@ describe('MRWidgetHeader', () => {
});
it('renders diverged commits info', () => {
expect(vm.$el.querySelector('.diverged-commits-count').textContent.trim()).toEqual(
'(12 commits behind)',
expect(vm.$el.querySelector('.diverged-commits-count').textContent).toMatch(
/(mr-widget-refactor[\s\S]+?is 12 commits behind[\s\S]+?master)/,
);
});
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment