Commit cf26610f authored by Phil Hughes's avatar Phil Hughes
Browse files

Merge branch '50904-stages-sidebar' into 'master'

Moves stages dropdown into the new vue app

See merge request gitlab-org/gitlab-ce!21971
parents c4d9f402 f72a1bf0
...@@ -24,7 +24,6 @@ export default class Job extends LogOutputBehaviours { ...@@ -24,7 +24,6 @@ export default class Job extends LogOutputBehaviours {
this.$document = $(document); this.$document = $(document);
this.$window = $(window); this.$window = $(window);
this.logBytes = 0; this.logBytes = 0;
this.updateDropdown = this.updateDropdown.bind(this);
this.$buildTrace = $('#build-trace'); this.$buildTrace = $('#build-trace');
this.$buildRefreshAnimation = $('.js-build-refresh'); this.$buildRefreshAnimation = $('.js-build-refresh');
...@@ -35,18 +34,12 @@ export default class Job extends LogOutputBehaviours { ...@@ -35,18 +34,12 @@ export default class Job extends LogOutputBehaviours {
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.initSidebar(); this.initSidebar();
this.populateJobs(this.buildStage);
this.updateStageDropdownText(this.buildStage);
this.sidebarOnResize(); this.sidebarOnResize();
this.$document this.$document
.off('click', '.js-sidebar-build-toggle') .off('click', '.js-sidebar-build-toggle')
.on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this)); .on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
this.$document
.off('click', '.stage-item')
.on('click', '.stage-item', this.updateDropdown);
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
this.$window this.$window
...@@ -194,20 +187,4 @@ export default class Job extends LogOutputBehaviours { ...@@ -194,20 +187,4 @@ export default class Job extends LogOutputBehaviours {
if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
} }
// eslint-disable-next-line class-methods-use-this
populateJobs(stage) {
$('.build-job').hide();
$(`.build-job[data-stage="${stage}"]`).show();
}
// eslint-disable-next-line class-methods-use-this
updateStageDropdownText(stage) {
$('.stage-selection').text(stage);
}
updateDropdown(e) {
e.preventDefault();
const stage = e.currentTarget.text;
this.updateStageDropdownText(stage);
this.populateJobs(stage);
}
} }
<script> <script>
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
...@@ -16,26 +17,39 @@ ...@@ -16,26 +17,39 @@
type: Array, type: Array,
required: true, required: true,
}, },
jobId: {
type: Number,
required: true,
},
},
methods: {
isJobActive(currentJobId) {
return this.jobId === currentJobId;
},
tooltipText(job) {
return `${_.escape(job.name)} - ${job.status.tooltip}`;
},
}, },
}; };
</script> </script>
<template> <template>
<div class="builds-container"> <div class="js-jobs-container builds-container">
<div <div
v-for="job in jobs"
:key="job.id"
class="build-job" class="build-job"
:class="{ retried: job.retried, active: isJobActive(job.id) }"
> >
<a <a
v-for="job in jobs"
:key="job.id"
v-tooltip v-tooltip
:href="job.path" :href="job.status.details_path"
:title="job.tooltip" :title="tooltipText(job)"
:class="{ active: job.active, retried: job.retried }" data-container="body"
> >
<icon <icon
v-if="job.active" v-if="isJobActive(job.id)"
name="arrow-right" name="arrow-right"
class="js-arrow-right" class="js-arrow-right icon-arrow-right"
/> />
<ci-icon :status="job.status" /> <ci-icon :status="job.status" />
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import timeagoMixin from '~/vue_shared/mixins/timeago'; import timeagoMixin from '~/vue_shared/mixins/timeago';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility'; import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -7,26 +8,22 @@ ...@@ -7,26 +8,22 @@
import ArtifactsBlock from './artifacts_block.vue'; import ArtifactsBlock from './artifacts_block.vue';
import TriggerBlock from './trigger_block.vue'; import TriggerBlock from './trigger_block.vue';
import CommitBlock from './commit_block.vue'; import CommitBlock from './commit_block.vue';
import StagesDropdown from './stages_dropdown.vue';
import JobsContainer from './jobs_container.vue';
export default { export default {
name: 'SidebarDetailsBlock', name: 'JobSidebar',
components: { components: {
ArtifactsBlock, ArtifactsBlock,
CommitBlock, CommitBlock,
DetailRow, DetailRow,
Icon, Icon,
TriggerBlock, TriggerBlock,
StagesDropdown,
JobsContainer,
}, },
mixins: [timeagoMixin], mixins: [timeagoMixin],
props: { props: {
job: {
type: Object,
required: true,
},
isLoading: {
type: Boolean,
required: true,
},
runnerHelpUrl: { runnerHelpUrl: {
type: String, type: String,
required: false, required: false,
...@@ -39,9 +36,7 @@ ...@@ -39,9 +36,7 @@
}, },
}, },
computed: { computed: {
shouldRenderContent() { ...mapState(['job', 'isLoading', 'stages', 'jobs']),
return !this.isLoading && Object.keys(this.job).length > 0;
},
coverage() { coverage() {
return `${this.job.coverage}%`; return `${this.job.coverage}%`;
}, },
...@@ -97,180 +92,206 @@ ...@@ -97,180 +92,206 @@
}, },
hasStages() { hasStages() {
return ( return (
this.job && (this.job &&
this.job.pipeline && this.job.pipeline &&
this.job.pipeline.stages && this.job.pipeline.stages &&
this.job.pipeline.stages.length > 0 this.job.pipeline.stages.length > 0) ||
) || false; false
);
}, },
commit() { commit() {
return this.job.pipeline.commit || {}; return this.job.pipeline.commit || {};
}, },
}, },
methods: {
...mapActions(['fetchJobsForStage']),
},
}; };
</script> </script>
<template> <template>
<div> <aside
<div class="block"> class="right-sidebar right-sidebar-expanded build-sidebar"
<strong class="inline prepend-top-8"> data-offset-top="101"
{{ job.name }} data-spy="affix"
</strong> >
<a <div class="sidebar-container">
v-if="job.retry_path" <div class="blocks-container">
:class="retryButtonClass" <template v-if="!isLoading">
:href="job.retry_path" <div class="block">
data-method="post" <strong class="inline prepend-top-8">
rel="nofollow" {{ job.name }}
> </strong>
{{ __('Retry') }} <a
</a> v-if="job.retry_path"
<a :class="retryButtonClass"
v-if="terminalPath" :href="job.retry_path"
:href="terminalPath" data-method="post"
class="js-terminal-link pull-right btn btn-primary rel="nofollow"
btn-inverted visible-md-block visible-lg-block" >
target="_blank" {{ __('Retry') }}
> </a>
{{ __('Debug') }} <a
<icon name="external-link" /> v-if="terminalPath"
</a> :href="terminalPath"
<button class="js-terminal-link pull-right btn btn-primary
:aria-label="__('Toggle Sidebar')" btn-inverted visible-md-block visible-lg-block"
type="button" target="_blank"
class="btn btn-blank gutter-toggle float-right d-block d-md-none js-sidebar-build-toggle" >
> {{ __('Debug') }}
<i <icon name="external-link" />
aria-hidden="true" </a>
data-hidden="true" <button
class="fa fa-angle-double-right" :aria-label="__('Toggle Sidebar')"
></i> type="button"
</button> class="btn btn-blank gutter-toggle
</div> float-right d-block d-md-none js-sidebar-build-toggle"
<template v-if="shouldRenderContent"> >
<div <i
v-if="job.retry_path || job.new_issue_path" aria-hidden="true"
class="block retry-link" data-hidden="true"
> class="fa fa-angle-double-right"
<a ></i>
v-if="job.new_issue_path" </button>
:href="job.new_issue_path" </div>
class="js-new-issue btn btn-success btn-inverted" <div
> v-if="job.retry_path || job.new_issue_path"
{{ __('New issue') }} class="block retry-link"
</a> >
<a <a
v-if="job.retry_path" v-if="job.new_issue_path"
:href="job.retry_path" :href="job.new_issue_path"
class="js-retry-job btn btn-inverted-secondary" class="js-new-issue btn btn-success btn-inverted"
data-method="post" >
rel="nofollow" {{ __('New issue') }}
> </a>
{{ __('Retry') }} <a
</a> v-if="job.retry_path"
</div> :href="job.retry_path"
<div :class="{block : renderBlock }"> class="js-retry-job btn btn-inverted-secondary"
<p data-method="post"
v-if="job.merge_request" rel="nofollow"
class="build-detail-row js-job-mr" >
> {{ __('Retry') }}
<span class="build-light-text"> </a>
{{ __('Merge Request:') }} </div>
</span> <div :class="{ block : renderBlock }">
<a :href="job.merge_request.path"> <p
!{{ job.merge_request.iid }} v-if="job.merge_request"
</a> class="build-detail-row js-job-mr"
</p> >
<span class="build-light-text">
{{ __('Merge Request:') }}
</span>
<a :href="job.merge_request.path">
!{{ job.merge_request.iid }}
</a>
</p>
<detail-row <detail-row
v-if="job.duration" v-if="job.duration"
:value="duration" :value="duration"
class="js-job-duration" class="js-job-duration"
title="Duration" title="Duration"
/> />
<detail-row <detail-row
v-if="job.finished_at" v-if="job.finished_at"
:value="timeFormated(job.finished_at)" :value="timeFormated(job.finished_at)"
class="js-job-finished" class="js-job-finished"
title="Finished" title="Finished"
/> />
<detail-row <detail-row
v-if="job.erased_at" v-if="job.erased_at"
:value="timeFormated(job.erased_at)" :value="timeFormated(job.erased_at)"
class="js-job-erased" class="js-job-erased"
title="Erased" title="Erased"
/> />
<detail-row <detail-row
v-if="job.queued" v-if="job.queued"
:value="queued" :value="queued"
class="js-job-queued" class="js-job-queued"
title="Queued" title="Queued"
/> />
<detail-row <detail-row
v-if="hasTimeout" v-if="hasTimeout"
:help-url="runnerHelpUrl" :help-url="runnerHelpUrl"
:value="timeout" :value="timeout"
class="js-job-timeout" class="js-job-timeout"
title="Timeout" title="Timeout"
/> />
<detail-row <detail-row
v-if="job.runner" v-if="job.runner"
:value="runnerId" :value="runnerId"
class="js-job-runner" class="js-job-runner"
title="Runner" title="Runner"
/> />
<detail-row <detail-row
v-if="job.coverage" v-if="job.coverage"
:value="coverage" :value="coverage"
class="js-job-coverage" class="js-job-coverage"
title="Coverage" title="Coverage"
/> />
<p <p
v-if="job.tags.length" v-if="job.tags.length"
class="build-detail-row js-job-tags" class="build-detail-row js-job-tags"
> >
<span class="build-light-text"> <span class="build-light-text">
{{ __('Tags:') }} {{ __('Tags:') }}
</span> </span>
<span <span
v-for="(tag, i) in job.tags" v-for="(tag, i) in job.tags"
:key="i" :key="i"
class="label label-primary"> class="label label-primary">
{{ tag }} {{ tag }}
</span> </span>
</p> </p>
<div <div
v-if="job.cancel_path" v-if="job.cancel_path"
class="btn-group prepend-top-5" class="btn-group prepend-top-5"
role="group"> role="group">
<a <a
:href="job.cancel_path" :href="job.cancel_path"
class="js-cancel-job btn btn-sm btn-default" class="js-cancel-job btn btn-sm btn-default"
data-method="post" data-method="post"
rel="nofollow" rel="nofollow"
> >
{{ __('Cancel') }} {{ __('Cancel') }}
</a> </a>
</div> </div>
</div>
<artifacts-block
v-if="hasArtifact"
:artifact="job.artifact"
/>
<trigger-block
v-if="hasTriggers"
:trigger="job.trigger"
/>
<commit-block
:is-last-block="hasStages"
:commit="commit"
:merge-request="job.merge_request"
/>
<stages-dropdown
:stages="stages"
:pipeline="job.pipeline"
@requestSidebarStageDropdown="fetchJobsForStage"
/>
</template>
<gl-loading-icon
v-else
:size="2"
class="prepend-top-10"
/>
</div> </div>
<artifacts-block
v-if="hasArtifact" <jobs-container
:artifact="job.artifact" v-if="!isLoading && jobs.length"
/> :jobs="jobs"
<trigger-block :job-id="job.id"
v-if="hasTriggers"
:trigger="job.trigger"
/>
<commit-block
:is-last-block="hasStages"
:commit="commit"
:merge-request="job.merge_request"
/> />
</template> </div>
<gl-loading-icon </aside>
v-if="isLoading"
:size="2"
class="prepend-top-10"
/>
</div>
</template> </template>
<script> <script>
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import { sprintf, __ } from '~/locale';
export default { export default {
components: { components: {
...@@ -10,30 +10,14 @@ ...@@ -10,30 +10,14 @@
Icon, Icon,
}, },
props: { props: {
pipelineId: { pipeline: {
type: Number, type: Object,
required: true,
},
pipelinePath: {
type: String,
required: true,
},
pipelineRef: {
type: String,
required: true,
},
pipelineRefPath: {
type: String,
required: true, required: true,
}, },