Commit a55bafb3 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '276949-pre-graph-refactor' into 'master'

Pipeline Graph Structural Update: Pre Refactor I

See merge request gitlab-org/gitlab!47772
parents 4932154c 5bd4461a
export const DOWNSTREAM = 'downstream';
export const MAIN = 'main';
export const UPSTREAM = 'upstream';
......@@ -5,6 +5,7 @@ import StageColumnComponent from './stage_column_component.vue';
import GraphWidthMixin from '../../mixins/graph_width_mixin';
import LinkedPipelinesColumn from './linked_pipelines_column.vue';
import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
export default {
name: 'PipelineGraph',
......@@ -35,11 +36,11 @@ export default {
type: {
type: String,
required: false,
default: 'main',
default: MAIN,
},
},
upstream: 'upstream',
downstream: 'downstream',
upstream: UPSTREAM,
downstream: DOWNSTREAM,
data() {
return {
downstreamMarginTop: null,
......@@ -54,41 +55,41 @@ export default {
graph() {
return this.pipeline.details?.stages;
},
hasTriggeredBy() {
hasUpstream() {
return (
this.type !== this.$options.downstream &&
this.triggeredByPipelines &&
this.upstreamPipelines &&
this.pipeline.triggered_by !== null
);
},
triggeredByPipelines() {
upstreamPipelines() {
return this.pipeline.triggered_by;
},
hasTriggered() {
hasDownstream() {
return (
this.type !== this.$options.upstream &&
this.triggeredPipelines &&
this.downstreamPipelines &&
this.pipeline.triggered.length > 0
);
},
triggeredPipelines() {
downstreamPipelines() {
return this.pipeline.triggered;
},
expandedTriggeredBy() {
expandedUpstream() {
return (
this.pipeline.triggered_by &&
Array.isArray(this.pipeline.triggered_by) &&
this.pipeline.triggered_by.find(el => el.isExpanded)
);
},
expandedTriggered() {
expandedDownstream() {
return this.pipeline.triggered && this.pipeline.triggered.find(el => el.isExpanded);
},
pipelineTypeUpstream() {
return this.type !== this.$options.downstream && this.expandedTriggeredBy;
return this.type !== this.$options.downstream && this.expandedUpstream;
},
pipelineTypeDownstream() {
return this.type !== this.$options.upstream && this.expandedTriggered;
return this.type !== this.$options.upstream && this.expandedDownstream;
},
pipelineProjectId() {
return this.pipeline.project.id;
......@@ -142,11 +143,11 @@ export default {
* and we want to reset the pipeline store. Triggering the reset without
* this condition would mean not allowing downstreams of downstreams to expand
*/
if (this.expandedTriggered?.id !== pipeline.id) {
this.$emit('onResetTriggered', this.pipeline, pipeline);
if (this.expandedDownstream?.id !== pipeline.id) {
this.$emit('onResetDownstream', this.pipeline, pipeline);
}
this.$emit('onClickTriggered', pipeline);
this.$emit('onClickDownstreamPipeline', pipeline);
},
calculateMarginTop(downstreamNode, pixelDiff) {
return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
......@@ -154,8 +155,8 @@ export default {
hasOnlyOneJob(stage) {
return stage.groups.length === 1;
},
hasUpstream(index) {
return index === 0 && this.hasTriggeredBy;
hasUpstreamColumn(index) {
return index === 0 && this.hasUpstream;
},
setJob(jobName) {
this.jobName = jobName;
......@@ -192,30 +193,30 @@ export default {
<pipeline-graph
v-if="pipelineTypeUpstream"
type="upstream"
:type="$options.upstream"
class="d-inline-block upstream-pipeline"
:class="`js-upstream-pipeline-${expandedTriggeredBy.id}`"
:class="`js-upstream-pipeline-${expandedUpstream.id}`"
:is-loading="false"
:pipeline="expandedTriggeredBy"
:pipeline="expandedUpstream"
:is-linked-pipeline="true"
:mediator="mediator"
@onClickTriggeredBy="clickTriggeredByPipeline"
@onClickUpstreamPipeline="clickUpstreamPipeline"
@refreshPipelineGraph="requestRefreshPipelineGraph"
/>
<linked-pipelines-column
v-if="hasTriggeredBy"
:linked-pipelines="triggeredByPipelines"
v-if="hasUpstream"
:type="$options.upstream"
:linked-pipelines="upstreamPipelines"
:column-title="__('Upstream')"
:project-id="pipelineProjectId"
graph-position="left"
@linkedPipelineClick="$emit('onClickTriggeredBy', $event)"
@linkedPipelineClick="$emit('onClickUpstreamPipeline', $event)"
/>
<ul
v-if="!isLoading"
:class="{
'inline js-has-linked-pipelines': hasTriggered || hasTriggeredBy,
'inline js-has-linked-pipelines': hasDownstream || hasUpstream,
}"
class="stage-column-list align-top"
>
......@@ -223,7 +224,7 @@ export default {
v-for="(stage, index) in graph"
:key="stage.name"
:class="{
'has-upstream gl-ml-11': hasUpstream(index),
'has-upstream gl-ml-11': hasUpstreamColumn(index),
'has-only-one-job': hasOnlyOneJob(stage),
'gl-mr-26': shouldAddRightMargin(index),
}"
......@@ -231,7 +232,7 @@ export default {
:groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
:has-triggered-by="hasTriggeredBy"
:has-upstream="hasUpstream"
:action="stage.status.action"
:job-hovered="jobName"
:pipeline-expanded="pipelineExpanded"
......@@ -240,11 +241,11 @@ export default {
</ul>
<linked-pipelines-column
v-if="hasTriggered"
:linked-pipelines="triggeredPipelines"
v-if="hasDownstream"
:type="$options.downstream"
:linked-pipelines="downstreamPipelines"
:column-title="__('Downstream')"
:project-id="pipelineProjectId"
graph-position="right"
@linkedPipelineClick="handleClickedDownstream"
@downstreamHovered="setJob"
@pipelineExpandToggle="setPipelineExpanded"
......@@ -252,15 +253,15 @@ export default {
<pipeline-graph
v-if="pipelineTypeDownstream"
type="downstream"
:type="$options.downstream"
class="d-inline-block"
:class="`js-downstream-pipeline-${expandedTriggered.id}`"
:class="`js-downstream-pipeline-${expandedDownstream.id}`"
:is-loading="false"
:pipeline="expandedTriggered"
:pipeline="expandedDownstream"
:is-linked-pipeline="true"
:style="{ 'margin-top': downstreamMarginTop }"
:mediator="mediator"
@onClickTriggered="clickTriggeredPipeline"
@onClickDownstreamPipeline="clickDownstreamPipeline"
@refreshPipelineGraph="requestRefreshPipelineGraph"
/>
</div>
......
......@@ -2,6 +2,7 @@
import { GlTooltipDirective, GlButton, GlLink, GlLoadingIcon } from '@gitlab/ui';
import CiStatus from '~/vue_shared/components/ci_icon.vue';
import { __, sprintf } from '~/locale';
import { UPSTREAM, DOWNSTREAM } from './constants';
export default {
directives: {
......@@ -14,6 +15,10 @@ export default {
GlLoadingIcon,
},
props: {
columnTitle: {
type: String,
required: true,
},
pipeline: {
type: Object,
required: true,
......@@ -22,7 +27,7 @@ export default {
type: Number,
required: true,
},
columnTitle: {
type: {
type: String,
required: true,
},
......@@ -50,12 +55,10 @@ export default {
return this.childPipeline ? __('child-pipeline') : this.pipeline.project.name;
},
parentPipeline() {
// Refactor string match when BE returns Upstream/Downstream indicators
return this.projectId === this.pipeline.project.id && this.columnTitle === __('Upstream');
return this.isUpstream && this.isSameProject;
},
childPipeline() {
// Refactor string match when BE returns Upstream/Downstream indicators
return this.projectId === this.pipeline.project.id && this.isDownstream;
return this.isDownstream && this.isSameProject;
},
label() {
if (this.parentPipeline) {
......@@ -66,7 +69,13 @@ export default {
return __('Multi-project');
},
isDownstream() {
return this.columnTitle === __('Downstream');
return this.type === DOWNSTREAM;
},
isUpstream() {
return this.type === UPSTREAM;
},
isSameProject() {
return this.projectId === this.pipeline.project.id;
},
sourceJobInfo() {
return this.isDownstream
......@@ -74,13 +83,13 @@ export default {
: '';
},
expandedIcon() {
if (this.parentPipeline) {
if (this.isUpstream) {
return this.expanded ? 'angle-right' : 'angle-left';
}
return this.expanded ? 'angle-left' : 'angle-right';
},
expandButtonPosition() {
return this.parentPipeline ? 'gl-left-0 gl-border-r-1!' : 'gl-right-0 gl-border-l-1!';
return this.isUpstream ? 'gl-left-0 gl-border-r-1!' : 'gl-right-0 gl-border-l-1!';
},
},
methods: {
......@@ -116,7 +125,7 @@ export default {
>
<div
class="gl-relative gl-bg-white gl-p-3 gl-border-solid gl-border-gray-100 gl-border-1"
:class="{ 'gl-pl-9': parentPipeline }"
:class="{ 'gl-pl-9': isUpstream }"
>
<div class="gl-display-flex">
<ci-status
......
<script>
import LinkedPipeline from './linked_pipeline.vue';
import { __ } from '~/locale';
import { UPSTREAM } from './constants';
export default {
components: {
......@@ -15,7 +15,7 @@ export default {
type: Array,
required: true,
},
graphPosition: {
type: {
type: String,
required: true,
},
......@@ -32,9 +32,12 @@ export default {
};
return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
},
graphPosition() {
return this.isUpstream ? 'left' : 'right';
},
// Refactor string match when BE returns Upstream/Downstream indicators
isUpstream() {
return this.columnTitle === __('Upstream');
return this.type === UPSTREAM;
},
},
methods: {
......@@ -45,6 +48,11 @@ export default {
this.$emit('downstreamHovered', jobName);
},
onPipelineExpandToggle(jobName, expanded) {
// Highlighting only applies to downstream pipelines
if (this.isUpstream) {
return;
}
this.$emit('pipelineExpandToggle', jobName, expanded);
},
},
......@@ -66,6 +74,7 @@ export default {
:pipeline="pipeline"
:column-title="columnTitle"
:project-id="projectId"
:type="type"
@pipelineClicked="onPipelineClick($event, pipeline, index)"
@downstreamHovered="onDownstreamHovered"
@pipelineExpandToggle="onPipelineExpandToggle"
......
......@@ -41,13 +41,13 @@ export default {
this.mediator.poll.enable({ data: this.mediator.getExpandedParameters() });
}
},
resetTriggeredPipelines(parentPipeline, pipeline) {
resetDownstreamPipelines(parentPipeline, pipeline) {
this.mediator.store.resetTriggeredPipelines(parentPipeline, pipeline);
},
clickTriggeredByPipeline(pipeline) {
clickUpstreamPipeline(pipeline) {
this.clickPipeline(pipeline, 'openPipeline', 'closePipeline');
},
clickTriggeredPipeline(pipeline) {
clickDownstreamPipeline(pipeline) {
this.clickPipeline(pipeline, 'openPipeline', 'closePipeline');
},
requestRefreshPipelineGraph() {
......
export default {
props: {
hasTriggeredBy: {
hasUpstream: {
type: Boolean,
required: false,
default: false,
......@@ -8,7 +8,7 @@ export default {
},
methods: {
buildConnnectorClass(index) {
return index === 0 && (!this.isFirstColumn || this.hasTriggeredBy) ? 'left-connector' : '';
return index === 0 && (!this.isFirstColumn || this.hasUpstream) ? 'left-connector' : '';
},
},
};
......@@ -45,10 +45,10 @@ const createLegacyPipelinesDetailApp = mediator => {
},
on: {
refreshPipelineGraph: this.requestRefreshPipelineGraph,
onResetTriggered: (parentPipeline, pipeline) =>
this.resetTriggeredPipelines(parentPipeline, pipeline),
onClickTriggeredBy: pipeline => this.clickTriggeredByPipeline(pipeline),
onClickTriggered: pipeline => this.clickTriggeredPipeline(pipeline),
onResetDownstream: (parentPipeline, pipeline) =>
this.resetDownstreamPipelines(parentPipeline, pipeline),
onClickUpstreamPipeline: pipeline => this.clickUpstreamPipeline(pipeline),
onClickDownstreamPipeline: pipeline => this.clickDownstreamPipeline(pipeline),
},
});
},
......
......@@ -6,11 +6,8 @@ import { GlIcon } from '@gitlab/ui';
*
* Receives status object containing:
* status: {
* details_path: "/gitlab-org/gitlab-foss/pipelines/8150156" // url
* group:"running" // used for CSS class
* icon: "icon_status_running" // used to render the icon
* label:"running" // used for potential tooltip
* text:"running" // text rendered
* }
*
* Used in:
......
......@@ -159,13 +159,13 @@ describe('graph component', () => {
describe('triggered by', () => {
describe('on click', () => {
it('should emit `onClickTriggeredBy` when triggered by linked pipeline is clicked', () => {
it('should emit `onClickUpstreamPipeline` when triggered by linked pipeline is clicked', () => {
const btnWrapper = findExpandPipelineBtn();
btnWrapper.trigger('click');
btnWrapper.vm.$nextTick(() => {
expect(wrapper.emitted().onClickTriggeredBy).toEqual([
expect(wrapper.emitted().onClickUpstreamPipeline).toEqual([
store.state.pipeline.triggered_by,
]);
});
......
......@@ -2,11 +2,10 @@ import { mount } from '@vue/test-utils';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue';
import CiStatus from '~/vue_shared/components/ci_icon.vue';
import mockData from './linked_pipelines_mock_data';
import { UPSTREAM, DOWNSTREAM } from '~/pipelines/components/graph/constants';
const mockPipeline = mockData.triggered[0];
const validTriggeredPipelineId = mockPipeline.project.id;
const invalidTriggeredPipelineId = mockPipeline.project.id + 5;
......@@ -40,6 +39,7 @@ describe('Linked pipeline', () => {
pipeline: mockPipeline,
projectId: invalidTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
};
beforeEach(() => {
......@@ -104,11 +104,13 @@ describe('Linked pipeline', () => {
pipeline: mockPipeline,
projectId: validTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
};
const upstreamProps = {
...downstreamProps,
columnTitle: 'Upstream',
type: UPSTREAM,
};
it('parent/child label container should exist', () => {
......@@ -182,6 +184,7 @@ describe('Linked pipeline', () => {
pipeline: { ...mockPipeline, isLoading: true },
projectId: invalidTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
};
beforeEach(() => {
......@@ -198,6 +201,7 @@ describe('Linked pipeline', () => {
pipeline: mockPipeline,
projectId: validTriggeredPipelineId,
columnTitle: 'Downstream',
type: DOWNSTREAM,
};
beforeEach(() => {
......
import { shallowMount } from '@vue/test-utils';
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
import { UPSTREAM } from '~/pipelines/components/graph/constants';
import mockData from './linked_pipelines_mock_data';
describe('Linked Pipelines Column', () => {
......@@ -9,6 +10,7 @@ describe('Linked Pipelines Column', () => {
linkedPipelines: mockData.triggered,
graphPosition: 'right',
projectId: 19,
type: UPSTREAM,
};
let wrapper;
......
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