Commit ef0ac7c2 authored by Phil Hughes's avatar Phil Hughes

Merge branch '34531-remove-scroll' into 'master'

Revert change to design. Go back to scrollable page

Closes #34531

See merge request !12587
parents 9e4aef26 2c1f000e
...@@ -13,25 +13,21 @@ window.Build = (function () { ...@@ -13,25 +13,21 @@ window.Build = (function () {
this.options = options || $('.js-build-options').data(); this.options = options || $('.js-build-options').data();
this.pageUrl = this.options.pageUrl; this.pageUrl = this.options.pageUrl;
this.buildUrl = this.options.buildUrl;
this.buildStatus = this.options.buildStatus; this.buildStatus = this.options.buildStatus;
this.state = this.options.logState; this.state = this.options.logState;
this.buildStage = this.options.buildStage; this.buildStage = this.options.buildStage;
this.$document = $(document); this.$document = $(document);
this.logBytes = 0; this.logBytes = 0;
this.scrollOffsetPadding = 30;
this.hasBeenScrolled = false; this.hasBeenScrolled = false;
this.updateDropdown = this.updateDropdown.bind(this); this.updateDropdown = this.updateDropdown.bind(this);
this.getBuildTrace = this.getBuildTrace.bind(this); this.getBuildTrace = this.getBuildTrace.bind(this);
this.scrollToBottom = this.scrollToBottom.bind(this);
this.$body = $('body');
this.$buildTrace = $('#build-trace'); this.$buildTrace = $('#build-trace');
this.$buildRefreshAnimation = $('.js-build-refresh'); this.$buildRefreshAnimation = $('.js-build-refresh');
this.$truncatedInfo = $('.js-truncated-info'); this.$truncatedInfo = $('.js-truncated-info');
this.$buildTraceOutput = $('.js-build-output'); this.$buildTraceOutput = $('.js-build-output');
this.$scrollContainer = $('.js-scroll-container'); this.$topBar = $('.js-top-bar');
// Scroll controllers // Scroll controllers
this.$scrollTopBtn = $('.js-scroll-up'); this.$scrollTopBtn = $('.js-scroll-up');
...@@ -63,13 +59,22 @@ window.Build = (function () { ...@@ -63,13 +59,22 @@ window.Build = (function () {
.off('click') .off('click')
.on('click', this.scrollToBottom.bind(this)); .on('click', this.scrollToBottom.bind(this));
const scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
this.$scrollContainer $(window)
.off('scroll') .off('scroll')
.on('scroll', () => { .on('scroll', () => {
const contentHeight = this.$buildTraceOutput.prop('scrollHeight');
if (contentHeight > this.windowSize) {
// means the user did not scroll, the content was updated.
this.windowSize = contentHeight;
} else {
// User scrolled
this.hasBeenScrolled = true; this.hasBeenScrolled = true;
scrollThrottled(); this.toggleScrollAnimation(false);
}
this.scrollThrottled();
}); });
$(window) $(window)
...@@ -77,59 +82,73 @@ window.Build = (function () { ...@@ -77,59 +82,73 @@ window.Build = (function () {
.on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100)); .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
this.updateArtifactRemoveDate(); this.updateArtifactRemoveDate();
this.initAffixTopArea();
// eslint-disable-next-line this.getBuildTrace();
this.getBuildTrace()
.then(() => this.toggleScroll())
.then(() => {
if (!this.hasBeenScrolled) {
this.scrollToBottom();
}
})
.then(() => this.verifyTopPosition());
} }
Build.prototype.initAffixTopArea = function () {
/**
If the browser does not support position sticky, it returns the position as static.
If the browser does support sticky, then we allow the browser to handle it, if not
then we default back to Bootstraps affix
**/
if (this.$topBar.css('position') !== 'static') return;
const offsetTop = this.$buildTrace.offset().top;
this.$topBar.affix({
offset: {
top: offsetTop,
},
});
};
Build.prototype.canScroll = function () { Build.prototype.canScroll = function () {
return (this.$scrollContainer.prop('scrollHeight') - this.scrollOffsetPadding) > this.$scrollContainer.height(); return document.body.scrollHeight > window.innerHeight;
}; };
/**
* | | Up | Down |
* |--------------------------|----------|----------|
* | on scroll bottom | active | disabled |
* | on scroll top | disabled | active |
* | no scroll | disabled | disabled |
* | on.('scroll') is on top | disabled | active |
* | on('scroll) is on bottom | active | disabled |
*
*/
Build.prototype.toggleScroll = function () { Build.prototype.toggleScroll = function () {
const currentPosition = this.$scrollContainer.scrollTop(); const currentPosition = document.body.scrollTop;
const bottomScroll = currentPosition + this.$scrollContainer.innerHeight(); const windowHeight = window.innerHeight;
if (this.canScroll()) { if (this.canScroll()) {
if (currentPosition === 0) { if (currentPosition > 0 &&
(document.body.scrollHeight - currentPosition !== windowHeight)) {
// User is in the middle of the log
this.toggleDisableButton(this.$scrollTopBtn, false);
this.toggleDisableButton(this.$scrollBottomBtn, false);
} else if (currentPosition === 0) {
// User is at Top of Build Log
this.toggleDisableButton(this.$scrollTopBtn, true); this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, false); this.toggleDisableButton(this.$scrollBottomBtn, false);
} else if (bottomScroll === this.$scrollContainer.prop('scrollHeight')) { } else if (document.body.scrollHeight - currentPosition === windowHeight) {
// User is at the bottom of the build log.
this.toggleDisableButton(this.$scrollTopBtn, false); this.toggleDisableButton(this.$scrollTopBtn, false);
this.toggleDisableButton(this.$scrollBottomBtn, true); this.toggleDisableButton(this.$scrollBottomBtn, true);
} else {
this.toggleDisableButton(this.$scrollTopBtn, false);
this.toggleDisableButton(this.$scrollBottomBtn, false);
} }
} else {
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, true);
} }
}; };
Build.prototype.scrollToTop = function () { Build.prototype.scrollDown = function () {
document.body.scrollTop = document.body.scrollHeight;
};
Build.prototype.scrollToBottom = function () {
this.scrollDown();
this.hasBeenScrolled = true; this.hasBeenScrolled = true;
this.$scrollContainer.scrollTop(0);
this.toggleScroll(); this.toggleScroll();
}; };
Build.prototype.scrollToBottom = function () { Build.prototype.scrollToTop = function () {
document.body.scrollTop = 0;
this.hasBeenScrolled = true; this.hasBeenScrolled = true;
this.$scrollContainer.scrollTop(this.$scrollContainer.prop('scrollHeight'));
this.toggleScroll(); this.toggleScroll();
}; };
...@@ -142,47 +161,6 @@ window.Build = (function () { ...@@ -142,47 +161,6 @@ window.Build = (function () {
this.$scrollBottomBtn.toggleClass('animate', toggle); this.$scrollBottomBtn.toggleClass('animate', toggle);
}; };
/**
* Build trace top position depends on the space ocupied by the elments rendered before
*/
Build.prototype.verifyTopPosition = function () {
const $buildPage = $('.build-page');
const $flashError = $('.alert-wrapper');
const $header = $('.build-header', $buildPage);
const $runnersStuck = $('.js-build-stuck', $buildPage);
const $startsEnvironment = $('.js-environment-container', $buildPage);
const $erased = $('.js-build-erased', $buildPage);
const prependTopDefault = 20;
// header + navigation + margin
let topPostion = 168;
if ($header.length) {
topPostion += $header.outerHeight();
}
if ($runnersStuck.length) {
topPostion += $runnersStuck.outerHeight();
}
if ($startsEnvironment.length) {
topPostion += $startsEnvironment.outerHeight() + prependTopDefault;
}
if ($erased.length) {
topPostion += $erased.outerHeight() + prependTopDefault;
}
if ($flashError.length) {
topPostion += $flashError.outerHeight() + prependTopDefault;
}
this.$buildTrace.css({
top: topPostion,
});
};
Build.prototype.initSidebar = function () { Build.prototype.initSidebar = function () {
this.$sidebar = $('.js-build-sidebar'); this.$sidebar = $('.js-build-sidebar');
this.$sidebar.niceScroll(); this.$sidebar.niceScroll();
...@@ -200,6 +178,8 @@ window.Build = (function () { ...@@ -200,6 +178,8 @@ window.Build = (function () {
this.state = log.state; this.state = log.state;
} }
this.windowSize = this.$buildTraceOutput.prop('scrollHeight');
if (log.append) { if (log.append) {
this.$buildTraceOutput.append(log.html); this.$buildTraceOutput.append(log.html);
this.logBytes += log.size; this.logBytes += log.size;
...@@ -227,14 +207,7 @@ window.Build = (function () { ...@@ -227,14 +207,7 @@ window.Build = (function () {
} }
Build.timeout = setTimeout(() => { Build.timeout = setTimeout(() => {
//eslint-disable-next-line this.getBuildTrace();
this.getBuildTrace()
.then(() => {
if (!this.hasBeenScrolled) {
this.scrollToBottom();
}
})
.then(() => this.verifyTopPosition());
}, 4000); }, 4000);
} else { } else {
this.$buildRefreshAnimation.remove(); this.$buildRefreshAnimation.remove();
...@@ -247,7 +220,13 @@ window.Build = (function () { ...@@ -247,7 +220,13 @@ window.Build = (function () {
}) })
.fail(() => { .fail(() => {
this.$buildRefreshAnimation.remove(); this.$buildRefreshAnimation.remove();
}); })
.then(() => {
if (!this.hasBeenScrolled) {
this.scrollDown();
}
})
.then(() => this.toggleScroll());
}; };
Build.prototype.shouldHideSidebarForViewport = function () { Build.prototype.shouldHideSidebarForViewport = function () {
...@@ -259,14 +238,11 @@ window.Build = (function () { ...@@ -259,14 +238,11 @@ window.Build = (function () {
const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
const $toggleButton = $('.js-sidebar-build-toggle-header'); const $toggleButton = $('.js-sidebar-build-toggle-header');
this.$buildTrace
.toggleClass('sidebar-expanded', shouldShow)
.toggleClass('sidebar-collapsed', shouldHide);
this.$sidebar this.$sidebar
.toggleClass('right-sidebar-expanded', shouldShow) .toggleClass('right-sidebar-expanded', shouldShow)
.toggleClass('right-sidebar-collapsed', shouldHide); .toggleClass('right-sidebar-collapsed', shouldHide);
$('.js-build-page') this.$topBar
.toggleClass('sidebar-expanded', shouldShow) .toggleClass('sidebar-expanded', shouldShow)
.toggleClass('sidebar-collapsed', shouldHide); .toggleClass('sidebar-collapsed', shouldHide);
...@@ -279,17 +255,10 @@ window.Build = (function () { ...@@ -279,17 +255,10 @@ window.Build = (function () {
Build.prototype.sidebarOnResize = function () { Build.prototype.sidebarOnResize = function () {
this.toggleSidebar(this.shouldHideSidebarForViewport()); this.toggleSidebar(this.shouldHideSidebarForViewport());
this.verifyTopPosition();
if (this.canScroll()) {
this.toggleScroll();
}
}; };
Build.prototype.sidebarOnClick = function () { Build.prototype.sidebarOnClick = function () {
if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
this.verifyTopPosition();
}; };
Build.prototype.updateArtifactRemoveDate = function () { Build.prototype.updateArtifactRemoveDate = function () {
......
...@@ -26,14 +26,6 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -26,14 +26,6 @@ document.addEventListener('DOMContentLoaded', () => {
mounted() { mounted() {
this.mediator.initBuildClass(); this.mediator.initBuildClass();
}, },
updated() {
// Wait for flash message to be appended
Vue.nextTick(() => {
if (this.mediator.build) {
this.mediator.build.verifyTopPosition();
}
});
},
render(createElement) { render(createElement) {
return createElement('job-header', { return createElement('job-header', {
props: { props: {
......
...@@ -37,43 +37,56 @@ ...@@ -37,43 +37,56 @@
} }
.build-page { .build-page {
.sticky { .build-trace-container {
position: absolute; position: relative;
left: 0;
right: 0;
} }
.build-trace-container { .build-trace {
position: absolute;
top: 225px;
left: 15px;
bottom: 10px;
background: $black; background: $black;
color: $gray-darkest; color: $gray-darkest;
font-family: $monospace_font; white-space: pre;
overflow-x: auto;
font-size: 12px; font-size: 12px;
border-radius: 0;
border: none;
&.sidebar-expanded { .bash {
right: 305px; display: block;
}
&.sidebar-collapsed {
right: 16px;
} }
code {
background: $black;
color: $gray-darkest;
} }
.top-bar { .top-bar {
top: 0;
height: 35px; height: 35px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
background: $gray-light; background: $gray-light;
border: 1px solid $border-color; border: 1px solid $border-color;
color: $gl-text-color; color: $gl-text-color;
position: sticky;
position: -webkit-sticky;
top: 50px;
&.affix {
top: 50px;
}
// with sidebar
&.affix.sidebar-expanded {
right: 306px;
left: 16px;
}
// without sidebar
&.affix.sidebar-collapsed {
right: 16px;
left: 16px;
}
&.affix-top {
position: absolute;
right: 0;
left: 0;
}
.truncated-info { .truncated-info {
margin: 0 auto; margin: 0 auto;
...@@ -89,13 +102,12 @@ ...@@ -89,13 +102,12 @@
text-decoration: underline; text-decoration: underline;
} }
} }
}
.controllers { .controllers {
display: flex; display: flex;
align-self: center;
font-size: 15px; font-size: 15px;
margin-bottom: 4px; justify-content: center;
align-items: center;
svg { svg {
height: 15px; height: 15px;
...@@ -103,17 +115,9 @@ ...@@ -103,17 +115,9 @@
fill: $gl-text-color; fill: $gl-text-color;
} }
.controllers-buttons,
.btn-scroll {
color: $gl-text-color;
height: 15px;
vertical-align: middle;
padding: 0;
width: 12px;
}
.controllers-buttons { .controllers-buttons {
margin: 1px 10px; color: $gl-text-color;
margin: 0 10px;
} }
.btn-scroll.animate { .btn-scroll.animate {
...@@ -143,15 +147,6 @@ ...@@ -143,15 +147,6 @@
} }
} }
.bash {
top: 35px;
left: 10px;
bottom: 0;
padding: 10px 20px 20px 5px;
white-space: pre-wrap;
overflow: auto;
}
.environment-information { .environment-information {
border: 1px solid $border-color; border: 1px solid $border-color;
padding: 8px $gl-padding 12px; padding: 8px $gl-padding 12px;
......
...@@ -54,13 +54,14 @@ ...@@ -54,13 +54,14 @@
- else - else
Job has been erased #{time_ago_with_tooltip(@build.erased_at)} Job has been erased #{time_ago_with_tooltip(@build.erased_at)}
.build-trace-container#build-trace .build-trace-container.prepend-top-default
.top-bar.sticky .top-bar.js-top-bar
.js-truncated-info.truncated-info.hidden< .js-truncated-info.truncated-info.hidden<
Showing last Showing last
%span.js-truncated-info-size.truncated-info-size>< %span.js-truncated-info-size.truncated-info-size><
KiB of log - KiB of log -
%a.js-raw-link.raw-link{ href: raw_namespace_project_job_path(@project.namespace, @project, @build) }>< Complete Raw %a.js-raw-link.raw-link{ href: raw_namespace_project_job_path(@project.namespace, @project, @build) }>< Complete Raw
.controllers .controllers
- if @build.has_trace? - if @build.has_trace?
= link_to raw_namespace_project_job_path(@project.namespace, @project, @build), = link_to raw_namespace_project_job_path(@project.namespace, @project, @build),
...@@ -82,10 +83,12 @@ ...@@ -82,10 +83,12 @@
.has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} } .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} }
%button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true }
= custom_icon('scroll_down') = custom_icon('scroll_down')
.bash.sticky.js-scroll-container
%code.js-build-output %pre.build-trace#build-trace
%code.bash.js-build-output
.build-loader-animation.js-build-refresh .build-loader-animation.js-build-refresh
= render "sidebar" = render "sidebar"
.js-build-options{ data: javascript_build_options } .js-build-options{ data: javascript_build_options }
......
---
title: Update jobs page output to have a scrollable page
merge_request: 12587
author:
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