Commit 28f1931a authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 4c39dd11
import { REST, GRAPHQL } from './constants';
export const accessors = {
[REST]: {
groupId: 'id',
},
[GRAPHQL]: {
groupId: 'name',
},
};
......@@ -87,7 +87,7 @@ export default {
:title="tooltipText"
:class="cssClass"
:disabled="isDisabled"
class="js-ci-action ci-action-icon-container ci-action-icon-wrapper gl-display-flex gl-align-items-center gl-justify-content-center"
class="js-ci-action gl-ci-action-icon-container ci-action-icon-container ci-action-icon-wrapper gl-display-flex gl-align-items-center gl-justify-content-center"
@click.stop="onClickAction"
>
<gl-loading-icon v-if="isLoading" class="js-action-icon-loading" />
......
export const DOWNSTREAM = 'downstream';
export const MAIN = 'main';
export const UPSTREAM = 'upstream';
export const REST = 'rest';
export const GRAPHQL = 'graphql';
<script>
import { escape, capitalize } from 'lodash';
import StageColumnComponent from './stage_column_component.vue';
import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
import { MAIN } from './constants';
export default {
......@@ -9,7 +7,6 @@ export default {
components: {
StageColumnComponent,
},
mixins: [GraphBundleMixin],
props: {
isLinkedPipeline: {
type: Boolean,
......@@ -31,96 +28,21 @@ export default {
return this.pipeline.stages;
},
},
methods: {
capitalizeStageName(name) {
const escapedName = escape(name);
return capitalize(escapedName);
},
isFirstColumn(index) {
return index === 0;
},
stageConnectorClass(index, stage) {
let className;
// If it's the first stage column and only has one job
if (this.isFirstColumn(index) && stage.groups.length === 1) {
className = 'no-margin';
} else if (index > 0) {
// If it is not the first column
className = 'left-margin';
}
return className;
},
refreshPipelineGraph() {
this.$emit('refreshPipelineGraph');
},
/**
* CSS class is applied:
* - if pipeline graph contains only one stage column component
*
* @param {number} index
* @returns {boolean}
*/
shouldAddRightMargin(index) {
return !(index === this.graph.length - 1);
},
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 15
*/
this.downstreamMarginTop = this.calculateMarginTop(downstreamNode, 15);
/**
* If the expanded trigger is defined and the id is different than the
* pipeline we clicked, then it means we clicked on a sibling downstream link
* 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.expandedDownstream?.id !== pipeline.id) {
this.$emit('onResetDownstream', this.pipeline, pipeline);
}
this.$emit('onClickDownstreamPipeline', pipeline);
},
calculateMarginTop(downstreamNode, pixelDiff) {
return `${downstreamNode.offsetTop - downstreamNode.offsetParent.offsetTop - pixelDiff}px`;
},
hasOnlyOneJob(stage) {
return stage.groups.length === 1;
},
hasUpstreamColumn(index) {
return index === 0 && this.hasUpstream;
},
},
};
</script>
<template>
<div class="build-content middle-block js-pipeline-graph">
<div class="js-pipeline-graph">
<div
class="pipeline-visualization pipeline-graph"
:class="{ 'pipeline-tab-content': !isLinkedPipeline }"
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 }"
>
<div>
<ul class="stage-column-list align-top">
<stage-column-component
v-for="(stage, index) in graph"
:key="stage.name"
:class="{
'has-only-one-job': hasOnlyOneJob(stage),
'gl-mr-26': shouldAddRightMargin(index),
}"
:title="capitalizeStageName(stage.name)"
:groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
:action="stage.status.action"
@refreshPipelineGraph="refreshPipelineGraph"
/>
</ul>
</div>
<stage-column-component
v-for="stage in graph"
:key="stage.name"
:title="stage.name"
:groups="stage.groups"
:action="stage.status.action"
/>
</div>
</div>
</template>
<script>
import { escape, capitalize } from 'lodash';
import { GlLoadingIcon } from '@gitlab/ui';
import StageColumnComponent from './stage_column_component.vue';
import StageColumnComponentLegacy from './stage_column_component_legacy.vue';
import GraphWidthMixin from '../../mixins/graph_width_mixin';
import LinkedPipelinesColumn from './linked_pipelines_column.vue';
import GraphBundleMixin from '../../mixins/graph_pipeline_bundle_mixin';
......@@ -10,7 +10,7 @@ import { UPSTREAM, DOWNSTREAM, MAIN } from './constants';
export default {
name: 'PipelineGraphLegacy',
components: {
StageColumnComponent,
StageColumnComponentLegacy,
GlLoadingIcon,
LinkedPipelinesColumn,
},
......@@ -220,7 +220,7 @@ export default {
}"
class="stage-column-list align-top"
>
<stage-column-component
<stage-column-component-legacy
v-for="(stage, index) in graph"
:key="stage.name"
:class="{
......
......@@ -44,17 +44,19 @@ export default {
type="button"
data-toggle="dropdown"
data-display="static"
class="dropdown-menu-toggle build-content"
class="dropdown-menu-toggle build-content gl-build-content"
>
<ci-icon :status="group.status" />
<div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
<span class="gl-display-flex gl-align-items-center">
<ci-icon :status="group.status" :size="24" />
<span
class="gl-text-truncate mw-70p gl-pl-2 gl-display-inline-block gl-vertical-align-bottom"
>
{{ group.name }}
</span>
<span class="gl-text-truncate mw-70p gl-pl-3 gl-display-inline-block">
{{ group.name }}
</span>
</span>
<span class="dropdown-counter-badge"> {{ group.size }} </span>
<span class="gl-font-weight-100 gl-font-size-lg gl-pr-2"> {{ group.size }} </span>
</div>
</button>
<ul class="dropdown-menu big-pipeline-graph-dropdown-menu js-grouped-pipeline-dropdown">
......
......@@ -129,19 +129,23 @@ export default {
};
</script>
<template>
<div class="ci-job-component" data-qa-selector="job_item_container">
<div
class="ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between"
data-qa-selector="job_item_container"
>
<gl-link
v-if="status.has_details"
v-gl-tooltip="{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }"
:href="status.details_path"
:title="tooltipText"
:class="jobClasses"
class="js-pipeline-graph-job-link qa-job-link menu-item"
class="js-pipeline-graph-job-link qa-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none
gl-focus-text-decoration-none"
data-testid="job-with-link"
@click.stop="hideTooltips"
@mouseout="hideTooltips"
>
<job-name-component :name="job.name" :status="job.status" />
<job-name-component :name="job.name" :status="job.status" :icon-size="24" />
</gl-link>
<div
......@@ -153,7 +157,7 @@ export default {
data-testid="job-without-link"
@mouseout="hideTooltips"
>
<job-name-component :name="job.name" :status="job.status" />
<job-name-component :name="job.name" :status="job.status" :icon-size="24" />
</div>
<action-component
......
......@@ -16,18 +16,22 @@ export default {
type: String,
required: true,
},
status: {
type: Object,
required: true,
},
iconSize: {
type: Number,
required: false,
default: 16,
},
},
};
</script>
<template>
<span class="ci-job-name-component mw-100">
<ci-icon :status="status" />
<span class="gl-text-truncate mw-70p gl-pl-2 gl-display-inline-block gl-vertical-align-bottom">
<span class="ci-job-name-component mw-100 gl-display-flex gl-align-items-center">
<ci-icon :size="iconSize" :status="status" />
<span class="gl-text-truncate mw-70p gl-pl-3 gl-display-inline-block">
{{ name }}
</span>
</span>
......
<script>
import { isEmpty, escape } from 'lodash';
import stageColumnMixin from '../../mixins/stage_column_mixin';
import { capitalize, escape, isEmpty } from 'lodash';
import MainGraphWrapper from '../graph_shared/main_graph_wrapper.vue';
import JobItem from './job_item.vue';
import JobGroupDropdown from './job_group_dropdown.vue';
import ActionComponent from './action_component.vue';
import { GRAPHQL } from './constants';
import { accessors } from './accessors';
export default {
components: {
JobItem,
JobGroupDropdown,
ActionComponent,
JobGroupDropdown,
JobItem,
MainGraphWrapper,
},
mixins: [stageColumnMixin],
props: {
title: {
type: String,
......@@ -21,16 +23,6 @@ export default {
type: Array,
required: true,
},
isFirstColumn: {
type: Boolean,
required: false,
default: false,
},
stageConnectorClass: {
type: String,
required: false,
default: '',
},
action: {
type: Object,
required: false,
......@@ -47,62 +39,67 @@ export default {
default: () => ({}),
},
},
accessors,
titleClasses: [
'gl-font-weight-bold',
'gl-pipeline-job-width',
'gl-text-truncate',
'gl-line-height-36',
'gl-pl-3',
],
computed: {
formattedTitle() {
return capitalize(escape(this.title));
},
hasAction() {
return !isEmpty(this.action);
},
},
methods: {
getAccessor(property) {
return accessors[GRAPHQL][property];
},
groupId(group) {
return `ci-badge-${escape(group.name)}`;
},
pipelineActionRequestComplete() {
this.$emit('refreshPipelineGraph');
},
},
};
</script>
<template>
<li :class="stageConnectorClass" class="stage-column">
<div class="stage-name position-relative" data-testid="stage-column-title">
{{ title }}
<action-component
v-if="hasAction"
:action-icon="action.icon"
:tooltip-text="action.title"
:link="action.path"
class="js-stage-action stage-action rounded"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</div>
<div class="builds-container">
<ul>
<li
v-for="(group, index) in groups"
:id="groupId(group)"
:key="group.id"
:class="buildConnnectorClass(index)"
class="build"
>
<div class="curve"></div>
<job-item
v-if="group.size === 1"
:job="group.jobs[0]"
:job-hovered="jobHovered"
:pipeline-expanded="pipelineExpanded"
css-class-job-name="build-content"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
<job-group-dropdown
v-if="group.size > 1"
:group="group"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</li>
</ul>
</div>
</li>
<main-graph-wrapper>
<template #stages>
<div
data-testid="stage-column-title"
class="gl-display-flex gl-justify-content-space-between gl-relative"
:class="$options.titleClasses"
>
<div>{{ formattedTitle }}</div>
<action-component
v-if="hasAction"
:action-icon="action.icon"
:tooltip-text="action.title"
:link="action.path"
class="js-stage-action stage-action rounded"
/>
</div>
</template>
<template #jobs>
<div
v-for="group in groups"
:id="groupId(group)"
:key="group[getAccessor('groupId')]"
data-testid="stage-column-group"
class="gl-relative gl-mb-3 gl-white-space-normal gl-pipeline-job-width"
>
<job-item
v-if="group.size === 1"
:job="group.jobs[0]"
:job-hovered="jobHovered"
:pipeline-expanded="pipelineExpanded"
css-class-job-name="gl-build-content"
/>
<job-group-dropdown v-else :group="group" />
</div>
</template>
</main-graph-wrapper>
</template>
<script>
import { isEmpty, escape } from 'lodash';
import stageColumnMixin from '../../mixins/stage_column_mixin';
import JobItem from './job_item.vue';
import JobGroupDropdown from './job_group_dropdown.vue';
import ActionComponent from './action_component.vue';
export default {
components: {
JobItem,
JobGroupDropdown,
ActionComponent,
},
mixins: [stageColumnMixin],
props: {
title: {
type: String,
required: true,
},
groups: {
type: Array,
required: true,
},
isFirstColumn: {
type: Boolean,
required: false,
default: false,
},
stageConnectorClass: {
type: String,
required: false,
default: '',
},
action: {
type: Object,
required: false,
default: () => ({}),
},
jobHovered: {
type: String,
required: false,
default: '',
},
pipelineExpanded: {
type: Object,
required: false,
default: () => ({}),
},
},
computed: {
hasAction() {
return !isEmpty(this.action);
},
},
methods: {
groupId(group) {
return `ci-badge-${escape(group.name)}`;
},
pipelineActionRequestComplete() {
this.$emit('refreshPipelineGraph');
},
},
};
</script>
<template>
<li :class="stageConnectorClass" class="stage-column">
<div class="stage-name position-relative" data-testid="stage-column-title">
{{ title }}
<action-component
v-if="hasAction"
:action-icon="action.icon"
:tooltip-text="action.title"
:link="action.path"
class="js-stage-action stage-action rounded"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</div>
<div class="builds-container">
<ul>
<li
v-for="(group, index) in groups"
:id="groupId(group)"
:key="group.id"
:class="buildConnnectorClass(index)"
class="build"
>
<div class="curve"></div>
<job-item
v-if="group.size === 1"
:job="group.jobs[0]"
:job-hovered="jobHovered"
:pipeline-expanded="pipelineExpanded"
css-class-job-name="build-content"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
<job-group-dropdown
v-if="group.size > 1"
:group="group"
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</li>
</ul>
</div>
</li>
</template>
<script>
export default {
props: {
stageClasses: {
type: String,
required: false,
default: '',
},
jobClasses: {
type: String,
required: false,
default: '',
},
},
};
</script>
<template>
<div>
<div
class="gl-display-flex gl-align-items-center gl-w-full gl-px-8 gl-py-4 gl-mb-5"
:class="stageClasses"