Commit 152d1286 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'parent-child-upstream-downstream-labels' into 'master'

Add child and parent labels to pipelines (3/3)

See merge request gitlab-org/gitlab!21332
parents 035c7ad7 e7b2d546
......@@ -43,7 +43,7 @@ export default {
downstream: 'downstream',
data() {
return {
triggeredTopIndex: 1,
downstreamMarginTop: null,
};
},
computed: {
......@@ -77,26 +77,34 @@ export default {
expandedTriggered() {
return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
},
/**
* Calculates the margin top of the clicked downstream pipeline by
* adding the height of each linked pipeline and the margin
*/
marginTop() {
return `${this.triggeredTopIndex * 52}px`;
},
pipelineTypeUpstream() {
return this.type !== this.$options.downstream && this.expandedTriggeredBy;
},
pipelineTypeDownstream() {
return this.type !== this.$options.upstream && this.expandedTriggered;
},
pipelineProjectId() {
return this.pipeline.project.id;
},
},
methods: {
handleClickedDownstream(pipeline, clickedIndex) {
this.triggeredTopIndex = clickedIndex;
handleClickedDownstream(pipeline, clickedIndex, downstreamNode) {
/**
* Calculates the margin top of the clicked downstream pipeline by
* subtracting the clicked downstream pipelines offsetTop by it's parent's
* offsetTop and then subtracting either 15 (if child) or 30 (if not a child)
* due to the height of node and stage name margin bottom.
*/
this.downstreamMarginTop = this.calculateMarginTop(
downstreamNode,
downstreamNode.classList.contains('child-pipeline') ? 15 : 30,
);
this.$emit('onClickTriggered', this.pipeline, pipeline);
},
calculateMarginTop(downstreamNode, pixelDiff) {
return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
},
hasOnlyOneJob(stage) {
return stage.groups.length === 1;
},
......@@ -139,6 +147,7 @@ export default {
v-if="hasTriggeredBy"
:linked-pipelines="triggeredByPipelines"
:column-title="__('Upstream')"
:project-id="pipelineProjectId"
graph-position="left"
@linkedPipelineClick="
linkedPipeline => $emit('onClickTriggeredBy', pipeline, linkedPipeline)
......@@ -174,6 +183,7 @@ export default {
v-if="hasTriggered"
:linked-pipelines="triggeredPipelines"
:column-title="__('Downstream')"
:project-id="pipelineProjectId"
graph-position="right"
@linkedPipelineClick="handleClickedDownstream"
/>
......@@ -186,7 +196,7 @@ export default {
:is-loading="false"
:pipeline="expandedTriggered"
:is-linked-pipeline="true"
:style="{ 'margin-top': marginTop }"
:style="{ 'margin-top': downstreamMarginTop }"
:mediator="mediator"
@onClickTriggered="
(parentPipeline, pipeline) => clickTriggeredPipeline(parentPipeline, pipeline)
......
<script>
import { GlLoadingIcon, GlTooltipDirective, GlButton } from '@gitlab/ui';
import CiStatus from '~/vue_shared/components/ci_icon.vue';
import { __ } from '~/locale';
export default {
directives: {
......@@ -16,6 +17,14 @@ export default {
type: Object,
required: true,
},
projectId: {
type: Number,
required: true,
},
columnTitle: {
type: String,
required: true,
},
},
computed: {
tooltipText() {
......@@ -30,18 +39,45 @@ export default {
projectName() {
return this.pipeline.project.name;
},
parentPipeline() {
// Refactor string match when BE returns Upstream/Downstream indicators
return this.projectId === this.pipeline.project.id && this.columnTitle === __('Upstream');
},
childPipeline() {
// Refactor string match when BE returns Upstream/Downstream indicators
return this.projectId === this.pipeline.project.id && this.columnTitle === __('Downstream');
},
label() {
return this.parentPipeline ? __('Parent') : __('Child');
},
childTooltipText() {
return __('This pipeline was triggered by a parent pipeline');
},
parentTooltipText() {
return __('This pipeline triggered a child pipeline');
},
labelToolTipText() {
return this.label === __('Parent') ? this.parentTooltipText : this.childTooltipText;
},
},
methods: {
onClickLinkedPipeline() {
this.$root.$emit('bv::hide::tooltip', this.buttonId);
this.$emit('pipelineClicked');
this.$emit('pipelineClicked', this.$refs.linkedPipeline);
},
hideTooltips() {
this.$root.$emit('bv::hide::tooltip');
},
},
};
</script>
<template>
<li class="linked-pipeline build">
<li
ref="linkedPipeline"
class="linked-pipeline build"
:class="{ 'child-pipeline': childPipeline }"
>
<gl-button
:id="buttonId"
v-gl-tooltip
......@@ -59,6 +95,15 @@ export default {
class="js-linked-pipeline-status"
/>
<span class="str-truncated align-bottom"> {{ projectName }} &#8226; #{{ pipeline.id }} </span>
<div v-if="parentPipeline || childPipeline" class="parent-child-label-container">
<span
v-gl-tooltip.bottom
:title="labelToolTipText"
class="badge badge-primary"
@mouseover="hideTooltips"
>{{ label }}</span
>
</div>
</gl-button>
</li>
</template>
......@@ -19,6 +19,10 @@ export default {
type: String,
required: true,
},
projectId: {
type: Number,
required: true,
},
},
computed: {
columnClass() {
......@@ -28,10 +32,16 @@ export default {
};
return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
},
// Refactor string match when BE returns Upstream/Downstream indicators
isUpstream() {
return this.columnTitle === __('Upstream');
},
},
methods: {
onPipelineClick(downstreamNode, pipeline, index) {
this.$emit('linkedPipelineClick', pipeline, index, downstreamNode);
},
},
};
</script>
......@@ -48,7 +58,9 @@ export default {
'left-connector': pipeline.isExpanded && graphPosition === 'left',
}"
:pipeline="pipeline"
@pipelineClicked="$emit('linkedPipelineClick', pipeline, index)"
:column-title="columnTitle"
:project-id="projectId"
@pipelineClicked="onPipelineClick($event, pipeline, index)"
/>
</ul>
</div>
......
......@@ -1093,3 +1093,7 @@ button.mini-pipeline-graph-dropdown-toggle {
.progress-bar.bg-primary {
background-color: $blue-500 !important;
}
.parent-child-label-container {
padding-top: $gl-padding-4;
}
---
title: Add child and parent labels to pipelines
merge_request: 21332
author:
type: added
......@@ -163,6 +163,10 @@
}
}
&.child-pipeline {
height: 68px;
}
.linked-pipeline-content {
@include build-content(0);
text-align: inherit;
......
......@@ -3344,6 +3344,9 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
msgid "Child"
msgstr ""
msgid "Child epic does not exist."
msgstr ""
......@@ -12879,6 +12882,9 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
msgid "Parent"
msgstr ""
msgid "Parent epic doesn't exist."
msgstr ""
......@@ -18878,6 +18884,12 @@ msgstr ""
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b>"
msgstr ""
msgid "This pipeline triggered a child pipeline"
msgstr ""
msgid "This pipeline was triggered by a parent pipeline"
msgstr ""
msgid "This project"
msgstr ""
......
......@@ -8,6 +8,13 @@ const mockPipeline = mockData.triggered[0];
describe('Linked pipeline', () => {
let wrapper;
const createWrapper = propsData => {
wrapper = mount(LinkedPipelineComponent, {
attachToDocument: true,
propsData,
});
};
afterEach(() => {
wrapper.destroy();
});
......@@ -15,13 +22,12 @@ describe('Linked pipeline', () => {
describe('rendered output', () => {
const props = {
pipeline: mockPipeline,
projectId: 20,
columnTitle: 'Downstream',
};
beforeEach(() => {
wrapper = mount(LinkedPipelineComponent, {
attachToDocument: true,
propsData: props,
});
createWrapper(props);
});
it('should render a list item as the containing element', () => {
......@@ -73,18 +79,50 @@ describe('Linked pipeline', () => {
it('does not render the loading icon when isLoading is false', () => {
expect(wrapper.find('.js-linked-pipeline-loading').exists()).toBe(false);
});
it('should not display child label when pipeline project id is not the same as triggered pipeline project id', () => {
const labelContainer = wrapper.find('.parent-child-label-container');
expect(labelContainer.exists()).toBe(false);
});
});
describe('parent/child', () => {
const downstreamProps = {
pipeline: mockPipeline,
projectId: 19,
columnTitle: 'Downstream',
};
const upstreamProps = {
...downstreamProps,
columnTitle: 'Upstream',
};
it('parent/child label container should exist', () => {
createWrapper(downstreamProps);
expect(wrapper.find('.parent-child-label-container').exists()).toBe(true);
});
it('should display child label when pipeline project id is the same as triggered pipeline project id', () => {
createWrapper(downstreamProps);
expect(wrapper.find('.parent-child-label-container').text()).toContain('Child');
});
it('should display parent label when pipeline project id is the same as triggered_by pipeline project id', () => {
createWrapper(upstreamProps);
expect(wrapper.find('.parent-child-label-container').text()).toContain('Parent');
});
});
describe('when isLoading is true', () => {
const props = {
pipeline: { ...mockPipeline, isLoading: true },
projectId: 19,
columnTitle: 'Downstream',
};
beforeEach(() => {
wrapper = mount(LinkedPipelineComponent, {
attachToDocument: true,
propsData: props,
});
createWrapper(props);
});
it('renders a loading icon', () => {
......@@ -95,20 +133,19 @@ describe('Linked pipeline', () => {
describe('on click', () => {
const props = {
pipeline: mockPipeline,
projectId: 19,
columnTitle: 'Downstream',
};
beforeEach(() => {
wrapper = mount(LinkedPipelineComponent, {
attachToDocument: true,
propsData: props,
});
createWrapper(props);
});
it('emits `pipelineClicked` event', () => {
jest.spyOn(wrapper.vm, '$emit');
wrapper.find('button').trigger('click');
expect(wrapper.vm.$emit).toHaveBeenCalledWith('pipelineClicked');
expect(wrapper.emitted().pipelineClicked).toBeTruthy();
});
it('should emit `bv::hide::tooltip` to close the tooltip', () => {
......
export default {
project: {
id: 19,
},
triggered_by: {
id: 129,
active: true,
......@@ -63,6 +66,7 @@ export default {
path: '/gitlab-org/gitlab-foss/pipelines/132',
project: {
name: 'GitLabCE',
id: 19,
},
details: {
status: {
......
......@@ -190,6 +190,7 @@ describe('graph component', () => {
describe('on click', () => {
it('should emit `onClickTriggered`', () => {
spyOn(component, '$emit');
spyOn(component, 'calculateMarginTop').and.callFake(() => '16px');
component.$el.querySelector('#js-linked-pipeline-34993051').click();
......
......@@ -9,6 +9,7 @@ describe('Linked Pipelines Column', () => {
columnTitle: 'Upstream',
linkedPipelines: mockData.triggered,
graphPosition: 'right',
projectId: 19,
};
let vm;
......
......@@ -341,6 +341,9 @@
"commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46",
"commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46"
},
"project": {
"id": 1794617
},
"triggered_by": {
"id": 12,
"user": {
......
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