Commit a16a1ec9 authored by Sarah Groff Hennigh-Palermo's avatar Sarah Groff Hennigh-Palermo Committed by Andrew Fontaine

Add link capability to graph

Includes new component, specs
parent edfe34ba
......@@ -39,6 +39,7 @@ export default {
data() {
return {
hoveredJobName: '',
highlightedJobs: [],
measurements: {
width: 0,
height: 0,
......@@ -106,25 +107,17 @@ export default {
jobName: expanded ? jobName : '',
};
},
updateHighlightedJobs(jobs) {
this.highlightedJobs = jobs;
},
},
};
</script>
<template>
<div class="js-pipeline-graph">
<div
:id="containerId"
:ref="containerId"
class="gl-pipeline-min-h gl-display-flex gl-position-relative gl-overflow-auto gl-bg-gray-10 gl-white-space-nowrap"
:class="{ 'gl-py-5': !isLinkedPipeline }"
>
<links-layer
:pipeline-data="graph"
:pipeline-id="pipeline.id"
:container-id="containerId"
:container-measurements="measurements"
:highlighted-job="hoveredJobName"
default-link-color="gl-stroke-transparent"
@error="onError"
class="gl-display-flex gl-position-relative gl-overflow-auto gl-bg-gray-10 gl-white-space-nowrap"
:class="{ 'gl-pipeline-min-h gl-py-5': !isLinkedPipeline }"
>
<linked-graph-wrapper>
<template #upstream>
......@@ -137,18 +130,32 @@ export default {
/>
</template>
<template #main>
<div :id="containerId" :ref="containerId">
<links-layer
:pipeline-data="graph"
:pipeline-id="pipeline.id"
:container-id="containerId"
:container-measurements="measurements"
:highlighted-job="hoveredJobName"
default-link-color="gl-stroke-transparent"
@error="onError"
@highlightedJobsChange="updateHighlightedJobs"
>
<stage-column-component
v-for="stage in graph"
:key="stage.name"
:title="stage.name"
:groups="stage.groups"
:action="stage.status.action"
:highlighted-jobs="highlightedJobs"
:job-hovered="hoveredJobName"
:pipeline-expanded="pipelineExpanded"
:pipeline-id="pipeline.id"
@refreshPipelineGraph="$emit('refreshPipelineGraph')"
@jobHover="setJob"
/>
</links-layer>
</div>
</template>
<template #downstream>
<linked-pipelines-column
......@@ -162,7 +169,6 @@ export default {
/>
</template>
</linked-graph-wrapper>
</links-layer>
</div>
</div>
</template>
......@@ -42,8 +42,8 @@ export default {
computed: {
columnClass() {
const positionValues = {
right: 'gl-ml-11',
left: 'gl-mr-7',
right: 'gl-ml-6',
left: 'gl-mr-6',
};
return `graph-position-${this.graphPosition} ${positionValues[this.graphPosition]}`;
},
......
......@@ -16,10 +16,6 @@ export default {
MainGraphWrapper,
},
props: {
title: {
type: String,
required: true,
},
groups: {
type: Array,
required: true,
......@@ -28,11 +24,20 @@ export default {
type: Number,
required: true,
},
title: {
type: String,
required: true,
},
action: {
type: Object,
required: false,
default: () => ({}),
},
highlightedJobs: {
type: Array,
required: false,
default: () => [],
},
jobHovered: {
type: String,
required: false,
......@@ -69,11 +74,18 @@ export default {
groupId(group) {
return `ci-badge-${escape(group.name)}`;
},
isFadedOut(jobName) {
return (
this.jobHovered &&
this.highlightedJobs.length > 1 &&
!this.highlightedJobs.includes(jobName)
);
},
},
};
</script>
<template>
<main-graph-wrapper>
<main-graph-wrapper class="gl-px-6">
<template #stages>
<div
data-testid="stage-column-title"
......@@ -108,9 +120,15 @@ export default {
:pipeline-expanded="pipelineExpanded"
:pipeline-id="pipelineId"
css-class-job-name="gl-build-content"
:class="{ 'gl-opacity-3': isFadedOut(group.name) }"
@pipelineActionRequestComplete="$emit('refreshPipelineGraph')"
/>
<job-group-dropdown v-else :group="group" :pipeline-id="pipelineId" />
<job-group-dropdown
v-else
:group="group"
:pipeline-id="pipelineId"
:class="{ 'gl-opacity-3': isFadedOut(group.name) }"
/>
</div>
</template>
</main-graph-wrapper>
......
......@@ -64,7 +64,10 @@ export const generateLinksData = ({ links }, containerID, modifier = '') => {
// Make cross-stages lines a straight line all the way
// until we can safely draw the bezier to look nice.
const straightLineDestinationX = targetNodeX - 100;
// The adjustment number here is a magic number to make things
// look nice and should change if the padding changes. This goes well
// with gl-px-6. gl-px-8 is more like 100.
const straightLineDestinationX = targetNodeX - 60;
const controlPointX = straightLineDestinationX + (targetNodeX - straightLineDestinationX) / 2;
if (straightLineDestinationX > 0) {
......
......@@ -83,6 +83,9 @@ export default {
this.needsObject = generateJobNeedsDict(jobs) ?? {};
}
},
highlightedJobs(jobs) {
this.$emit('highlightedJobsChange', jobs);
},
},
mounted() {
if (!isEmpty(this.pipelineData)) {
......
......@@ -16,14 +16,11 @@ export default {
</script>
<template>
<div>
<div
class="gl-display-flex gl-align-items-center gl-w-full gl-px-8 gl-mb-5"
:class="stageClasses"
>
<div class="gl-display-flex gl-align-items-center gl-w-full gl-mb-5" :class="stageClasses">
<slot name="stages"> </slot>
</div>
<div
class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full gl-px-8"
class="gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full"
:class="jobClasses"
>
<slot name="jobs"> </slot>
......
......@@ -148,18 +148,13 @@
&:hover {
box-shadow: inset 0 0 0 0.0625rem $dropdown-toggle-active-border-color;
background-color: $gray-darker;
svg {
fill: $gl-text-color;
}
background-color: var(--gray-50, $gray-50);
}
.spinner,
svg {
width: $ci-action-dropdown-svg-size;
height: $ci-action-dropdown-svg-size;
fill: $gl-text-color-secondary;
position: relative;
top: 1px;
vertical-align: initial;
......
import { mount, shallowMount } from '@vue/test-utils';
import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import JobItem from '~/pipelines/components/graph/job_item.vue';
import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import { GRAPHQL } from '~/pipelines/components/graph/constants';
......@@ -21,17 +22,29 @@ describe('graph component', () => {
pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'),
};
const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
const createComponent = ({
data = {},
mountFn = shallowMount,
props = {},
stubOverride = {},
} = {}) => {
wrapper = mountFn(PipelineGraph, {
propsData: {
...defaultProps,
...props,
},
data() {
return { ...data };
},
provide: {
dataMethod: GRAPHQL,
},
stubs: {
'links-inner': true,
'linked-pipeline': true,
'job-item': true,
'job-group-dropdown': true,
...stubOverride,
},
});
};
......@@ -63,6 +76,23 @@ describe('graph component', () => {
expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
});
});
describe('when links are present', () => {
beforeEach(async () => {
createComponent({
mountFn: mount,
stubOverride: { 'job-item': false },
data: { hoveredJobName: 'test_a' },
});
findLinksLayer().vm.$emit('highlightedJobsChange', ['test_c', 'build_c']);
});
it('dims unrelated jobs', () => {
const unrelatedJob = wrapper.find(JobItem);
expect(findLinksLayer().emitted().highlightedJobsChange).toHaveLength(1);
expect(unrelatedJob.classes('gl-opacity-3')).toBe(true);
});
});
});
describe('when linked pipelines are not present', () => {
......
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