Commit cd5a113e authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'ee-54331-pipeline-graph-extend-scroll-area' into 'master'

Extend pipeline graph scroll area to full width (EE)

See merge request gitlab-org/gitlab!14870
parents b0a3500c 211f3dbe
...@@ -2,35 +2,43 @@ ...@@ -2,35 +2,43 @@
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import StageColumnComponent from './stage_column_component.vue'; import StageColumnComponent from './stage_column_component.vue';
import GraphMixin from '../../mixins/graph_component_mixin'; import GraphMixin from '../../mixins/graph_component_mixin';
import GraphWidthMixin from '~/pipelines/mixins/graph_width_mixin';
export default { export default {
components: { components: {
StageColumnComponent, StageColumnComponent,
GlLoadingIcon, GlLoadingIcon,
}, },
mixins: [GraphMixin], mixins: [GraphMixin, GraphWidthMixin],
}; };
</script> </script>
<template> <template>
<div class="build-content middle-block js-pipeline-graph"> <div class="build-content middle-block js-pipeline-graph">
<div class="pipeline-visualization pipeline-graph pipeline-tab-content"> <div class="pipeline-visualization pipeline-graph pipeline-tab-content">
<div v-if="isLoading" class="m-auto"><gl-loading-icon :size="3" /></div> <div
:style="{
paddingLeft: `${graphLeftPadding}px`,
paddingRight: `${graphRightPadding}px`,
}"
>
<gl-loading-icon v-if="isLoading" class="m-auto" :size="3" />
<ul v-if="!isLoading" class="stage-column-list"> <ul v-if="!isLoading" class="stage-column-list">
<stage-column-component <stage-column-component
v-for="(stage, index) in graph" v-for="(stage, index) in graph"
:key="stage.name" :key="stage.name"
:class="{ :class="{
'append-right-48': shouldAddRightMargin(index), 'append-right-48': shouldAddRightMargin(index),
}" }"
:title="capitalizeStageName(stage.name)" :title="capitalizeStageName(stage.name)"
:groups="stage.groups" :groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)" :stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)" :is-first-column="isFirstColumn(index)"
:action="stage.status.action" :action="stage.status.action"
@refreshPipelineGraph="refreshPipelineGraph" @refreshPipelineGraph="refreshPipelineGraph"
/> />
</ul> </ul>
</div>
</div> </div>
</div> </div>
</template> </template>
export const CANCEL_REQUEST = 'CANCEL_REQUEST'; export const CANCEL_REQUEST = 'CANCEL_REQUEST';
export const PIPELINES_TABLE = 'PIPELINES_TABLE'; export const PIPELINES_TABLE = 'PIPELINES_TABLE';
export const LAYOUT_CHANGE_DELAY = 300;
import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
import { LAYOUT_CHANGE_DELAY } from '~/pipelines/constants';
export default {
debouncedResize: null,
sidebarMutationObserver: null,
data() {
return {
graphLeftPadding: 0,
graphRightPadding: 0,
};
},
beforeDestroy() {
window.removeEventListener('resize', this.$options.debouncedResize);
if (this.$options.sidebarMutationObserver) {
this.$options.sidebarMutationObserver.disconnect();
}
},
created() {
this.$options.debouncedResize = debounceByAnimationFrame(this.setGraphPadding);
window.addEventListener('resize', this.$options.debouncedResize);
},
mounted() {
this.setGraphPadding();
this.$options.sidebarMutationObserver = new MutationObserver(this.handleLayoutChange);
this.$options.sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
attributes: true,
childList: false,
subtree: false,
});
},
methods: {
setGraphPadding() {
// only add padding to main graph (not inline upstream/downstream graphs)
if (this.type && this.type !== 'main') return;
const container = document.querySelector('.js-pipeline-container');
if (!container) return;
this.graphLeftPadding = container.offsetLeft;
this.graphRightPadding = window.innerWidth - container.offsetLeft - container.offsetWidth;
},
handleLayoutChange() {
// wait until animations finish, then recalculate padding
window.setTimeout(this.setGraphPadding, LAYOUT_CHANGE_DELAY);
},
},
};
...@@ -453,7 +453,7 @@ ...@@ -453,7 +453,7 @@
display: flex; display: flex;
width: 100%; width: 100%;
background-color: $gray-light; background-color: $gray-light;
padding: $gl-padding; padding: $gl-padding 0;
overflow: auto; overflow: auto;
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
= render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project = render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
.tab-content .tab-content
#js-tab-pipeline.tab-pane #js-tab-pipeline.tab-pane.position-absolute.position-left-0.w-100
#js-pipeline-graph-vue #js-pipeline-graph-vue
#js-tab-builds.tab-pane #js-tab-builds.tab-pane
......
---
title: Extend pipeline graph scroll area to full width
merge_request: 14870
author:
type: changed
...@@ -3,6 +3,7 @@ import _ from 'underscore'; ...@@ -3,6 +3,7 @@ import _ from 'underscore';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue'; import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
import GraphMixin from '~/pipelines/mixins/graph_component_mixin'; import GraphMixin from '~/pipelines/mixins/graph_component_mixin';
import GraphWidthMixin from '~/pipelines/mixins/graph_width_mixin';
import LinkedPipelinesColumn from 'ee/pipelines/components/graph/linked_pipelines_column.vue'; import LinkedPipelinesColumn from 'ee/pipelines/components/graph/linked_pipelines_column.vue';
import GraphEEMixin from 'ee/pipelines/mixins/graph_pipeline_bundle_mixin'; import GraphEEMixin from 'ee/pipelines/mixins/graph_pipeline_bundle_mixin';
...@@ -13,7 +14,7 @@ export default { ...@@ -13,7 +14,7 @@ export default {
GlLoadingIcon, GlLoadingIcon,
LinkedPipelinesColumn, LinkedPipelinesColumn,
}, },
mixins: [GraphMixin, GraphEEMixin], mixins: [GraphMixin, GraphWidthMixin, GraphEEMixin],
props: { props: {
isLoading: { isLoading: {
type: Boolean, type: Boolean,
...@@ -102,82 +103,89 @@ export default { ...@@ -102,82 +103,89 @@ export default {
class="pipeline-visualization pipeline-graph" class="pipeline-visualization pipeline-graph"
:class="{ 'pipeline-tab-content': !isLinkedPipeline }" :class="{ 'pipeline-tab-content': !isLinkedPipeline }"
> >
<div v-if="isLoading" class="m-auto"><gl-loading-icon :size="3" /></div> <div
:style="{
paddingLeft: `${graphLeftPadding}px`,
paddingRight: `${graphRightPadding}px`,
}"
>
<gl-loading-icon v-if="isLoading" class="m-auto" :size="3" />
<pipeline-graph <pipeline-graph
v-if="type !== $options.downstream && expandedTriggeredBy" v-if="type !== $options.downstream && expandedTriggeredBy"
type="upstream" type="upstream"
class="d-inline-block upstream-pipeline" class="d-inline-block upstream-pipeline"
:class="`js-upstream-pipeline-${expandedTriggeredBy.id}`" :class="`js-upstream-pipeline-${expandedTriggeredBy.id}`"
:is-loading="false" :is-loading="false"
:pipeline="expandedTriggeredBy" :pipeline="expandedTriggeredBy"
:is-linked-pipeline="true" :is-linked-pipeline="true"
:mediator="mediator" :mediator="mediator"
@onClickTriggeredBy=" @onClickTriggeredBy="
(parentPipeline, pipeline) => clickTriggeredByPipeline(parentPipeline, pipeline) (parentPipeline, pipeline) => clickTriggeredByPipeline(parentPipeline, pipeline)
" "
@refreshPipelineGraph="requestRefreshPipelineGraph" @refreshPipelineGraph="requestRefreshPipelineGraph"
/> />
<linked-pipelines-column <linked-pipelines-column
v-if="hasTriggeredBy" v-if="hasTriggeredBy"
:linked-pipelines="triggeredByPipelines" :linked-pipelines="triggeredByPipelines"
:column-title="__('Upstream')" :column-title="__('Upstream')"
graph-position="left" graph-position="left"
@linkedPipelineClick=" @linkedPipelineClick="
linkedPipeline => $emit('onClickTriggeredBy', pipeline, linkedPipeline) linkedPipeline => $emit('onClickTriggeredBy', pipeline, linkedPipeline)
" "
/> />
<ul <ul
v-if="!isLoading" v-if="!isLoading"
:class="{
'inline js-has-linked-pipelines': hasTriggered || hasTriggeredBy,
}"
class="stage-column-list align-top"
>
<stage-column-component
v-for="(stage, index) in graph"
:key="stage.name"
:class="{ :class="{
'has-upstream prepend-left-64': index === 0 && hasTriggeredBy, 'inline js-has-linked-pipelines': hasTriggered || hasTriggeredBy,
'has-downstream': index === graph.length - 1 && hasTriggered,
'has-only-one-job': hasOnlyOneJob(stage),
'append-right-46': shouldAddRightMargin(index),
}" }"
:title="capitalizeStageName(stage.name)" class="stage-column-list align-top"
:groups="stage.groups" >
:stage-connector-class="stageConnectorClass(index, stage)" <stage-column-component
:is-first-column="isFirstColumn(index)" v-for="(stage, index) in graph"
:has-triggered-by="hasTriggeredBy" :key="stage.name"
:action="stage.status.action" :class="{
@refreshPipelineGraph="refreshPipelineGraph" 'has-upstream prepend-left-64': index === 0 && hasTriggeredBy,
/> 'has-downstream': index === graph.length - 1 && hasTriggered,
</ul> 'has-only-one-job': hasOnlyOneJob(stage),
'append-right-46': shouldAddRightMargin(index),
}"
:title="capitalizeStageName(stage.name)"
:groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
:has-triggered-by="hasTriggeredBy"
:action="stage.status.action"
@refreshPipelineGraph="refreshPipelineGraph"
/>
</ul>
<linked-pipelines-column <linked-pipelines-column
v-if="hasTriggered" v-if="hasTriggered"
:linked-pipelines="triggeredPipelines" :linked-pipelines="triggeredPipelines"
:column-title="__('Downstream')" :column-title="__('Downstream')"
graph-position="right" graph-position="right"
@linkedPipelineClick="handleClickedDownstream" @linkedPipelineClick="handleClickedDownstream"
/> />
<pipeline-graph <pipeline-graph
v-if="type !== $options.upstream && expandedTriggered" v-if="type !== $options.upstream && expandedTriggered"
type="downstream" type="downstream"
class="d-inline-block" class="d-inline-block"
:class="`js-downstream-pipeline-${expandedTriggered.id}`" :class="`js-downstream-pipeline-${expandedTriggered.id}`"
:is-loading="false" :is-loading="false"
:pipeline="expandedTriggered" :pipeline="expandedTriggered"
:is-linked-pipeline="true" :is-linked-pipeline="true"
:style="{ 'margin-top': marginTop }" :style="{ 'margin-top': marginTop }"
:mediator="mediator" :mediator="mediator"
@onClickTriggered=" @onClickTriggered="
(parentPipeline, pipeline) => clickTriggeredPipeline(parentPipeline, pipeline) (parentPipeline, pipeline) => clickTriggeredPipeline(parentPipeline, pipeline)
" "
@refreshPipelineGraph="requestRefreshPipelineGraph" @refreshPipelineGraph="requestRefreshPipelineGraph"
/> />
</div>
</div> </div>
</div> </div>
</template> </template>
...@@ -14,6 +14,12 @@ describe('graph component', () => { ...@@ -14,6 +14,12 @@ describe('graph component', () => {
let component; let component;
beforeEach(() => {
setFixtures(`
<div class="layout-page"></div>
`);
});
afterEach(() => { afterEach(() => {
component.$destroy(); component.$destroy();
}); });
...@@ -130,7 +136,7 @@ describe('graph component', () => { ...@@ -130,7 +136,7 @@ describe('graph component', () => {
}); });
describe('with expanded pipeline', () => { describe('with expanded pipeline', () => {
it('should render expanded pipeline', () => { it('should render expanded pipeline', done => {
// expand the pipeline // expand the pipeline
store.state.pipeline.triggered_by[0].isExpanded = true; store.state.pipeline.triggered_by[0].isExpanded = true;
...@@ -140,7 +146,12 @@ describe('graph component', () => { ...@@ -140,7 +146,12 @@ describe('graph component', () => {
mediator, mediator,
}); });
expect(component.$el.querySelector('.js-upstream-pipeline-12')).not.toBeNull(); Vue.nextTick()
.then(() => {
expect(component.$el.querySelector('.js-upstream-pipeline-12')).not.toBeNull();
})
.then(done)
.catch(done.fail);
}); });
}); });
}); });
...@@ -161,7 +172,7 @@ describe('graph component', () => { ...@@ -161,7 +172,7 @@ describe('graph component', () => {
}); });
describe('with expanded pipeline', () => { describe('with expanded pipeline', () => {
it('should render expanded pipeline', () => { it('should render expanded pipeline', done => {
// expand the pipeline // expand the pipeline
store.state.pipeline.triggered[0].isExpanded = true; store.state.pipeline.triggered[0].isExpanded = true;
...@@ -171,7 +182,14 @@ describe('graph component', () => { ...@@ -171,7 +182,14 @@ describe('graph component', () => {
mediator, mediator,
}); });
expect(component.$el.querySelector('.js-downstream-pipeline-34993051')).not.toBeNull(); Vue.nextTick()
.then(() => {
expect(
component.$el.querySelector('.js-downstream-pipeline-34993051'),
).not.toBeNull();
})
.then(done)
.catch(done.fail);
}); });
}); });
}); });
......
...@@ -7,6 +7,12 @@ describe('graph component', () => { ...@@ -7,6 +7,12 @@ describe('graph component', () => {
const GraphComponent = Vue.extend(graphComponent); const GraphComponent = Vue.extend(graphComponent);
let component; let component;
beforeEach(() => {
setFixtures(`
<div class="layout-page"></div>
`);
});
afterEach(() => { afterEach(() => {
component.$destroy(); component.$destroy();
}); });
......
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