From 2c1f000e3e6c12b62c58067072dc8bc9bd361410 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 5 Jul 2017 11:32:22 +0000 Subject: [PATCH] Revert change to design. Go back to scrollable page --- app/assets/javascripts/build.js | 171 +++++++----------- .../javascripts/jobs/job_details_bundle.js | 8 - app/assets/stylesheets/pages/builds.scss | 109 ++++++----- app/views/projects/jobs/show.html.haml | 11 +- changelogs/unreleased/34531-remove-scroll.yml | 4 + 5 files changed, 133 insertions(+), 170 deletions(-) create mode 100644 changelogs/unreleased/34531-remove-scroll.yml diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 60103155ce0..1dfa064acfd 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -13,25 +13,21 @@ window.Build = (function () { this.options = options || $('.js-build-options').data(); this.pageUrl = this.options.pageUrl; - this.buildUrl = this.options.buildUrl; this.buildStatus = this.options.buildStatus; this.state = this.options.logState; this.buildStage = this.options.buildStage; this.$document = $(document); this.logBytes = 0; - this.scrollOffsetPadding = 30; this.hasBeenScrolled = false; this.updateDropdown = this.updateDropdown.bind(this); this.getBuildTrace = this.getBuildTrace.bind(this); - this.scrollToBottom = this.scrollToBottom.bind(this); - this.$body = $('body'); this.$buildTrace = $('#build-trace'); this.$buildRefreshAnimation = $('.js-build-refresh'); this.$truncatedInfo = $('.js-truncated-info'); this.$buildTraceOutput = $('.js-build-output'); - this.$scrollContainer = $('.js-scroll-container'); + this.$topBar = $('.js-top-bar'); // Scroll controllers this.$scrollTopBtn = $('.js-scroll-up'); @@ -63,13 +59,22 @@ window.Build = (function () { .off('click') .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') .on('scroll', () => { - this.hasBeenScrolled = true; - scrollThrottled(); + 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.toggleScrollAnimation(false); + } + + this.scrollThrottled(); }); $(window) @@ -77,59 +82,73 @@ window.Build = (function () { .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100)); this.updateArtifactRemoveDate(); + this.initAffixTopArea(); - // eslint-disable-next-line - this.getBuildTrace() - .then(() => this.toggleScroll()) - .then(() => { - if (!this.hasBeenScrolled) { - this.scrollToBottom(); - } - }) - .then(() => this.verifyTopPosition()); + this.getBuildTrace(); } + 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 () { - 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 () { - const currentPosition = this.$scrollContainer.scrollTop(); - const bottomScroll = currentPosition + this.$scrollContainer.innerHeight(); + const currentPosition = document.body.scrollTop; + const windowHeight = window.innerHeight; 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.$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.$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.$scrollContainer.scrollTop(0); this.toggleScroll(); }; - Build.prototype.scrollToBottom = function () { + Build.prototype.scrollToTop = function () { + document.body.scrollTop = 0; this.hasBeenScrolled = true; - this.$scrollContainer.scrollTop(this.$scrollContainer.prop('scrollHeight')); this.toggleScroll(); }; @@ -142,47 +161,6 @@ window.Build = (function () { 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 () { this.$sidebar = $('.js-build-sidebar'); this.$sidebar.niceScroll(); @@ -200,6 +178,8 @@ window.Build = (function () { this.state = log.state; } + this.windowSize = this.$buildTraceOutput.prop('scrollHeight'); + if (log.append) { this.$buildTraceOutput.append(log.html); this.logBytes += log.size; @@ -227,14 +207,7 @@ window.Build = (function () { } Build.timeout = setTimeout(() => { - //eslint-disable-next-line - this.getBuildTrace() - .then(() => { - if (!this.hasBeenScrolled) { - this.scrollToBottom(); - } - }) - .then(() => this.verifyTopPosition()); + this.getBuildTrace(); }, 4000); } else { this.$buildRefreshAnimation.remove(); @@ -247,7 +220,13 @@ window.Build = (function () { }) .fail(() => { this.$buildRefreshAnimation.remove(); - }); + }) + .then(() => { + if (!this.hasBeenScrolled) { + this.scrollDown(); + } + }) + .then(() => this.toggleScroll()); }; Build.prototype.shouldHideSidebarForViewport = function () { @@ -259,14 +238,11 @@ window.Build = (function () { const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; const $toggleButton = $('.js-sidebar-build-toggle-header'); - this.$buildTrace - .toggleClass('sidebar-expanded', shouldShow) - .toggleClass('sidebar-collapsed', shouldHide); this.$sidebar .toggleClass('right-sidebar-expanded', shouldShow) .toggleClass('right-sidebar-collapsed', shouldHide); - $('.js-build-page') + this.$topBar .toggleClass('sidebar-expanded', shouldShow) .toggleClass('sidebar-collapsed', shouldHide); @@ -279,17 +255,10 @@ window.Build = (function () { Build.prototype.sidebarOnResize = function () { this.toggleSidebar(this.shouldHideSidebarForViewport()); - - this.verifyTopPosition(); - - if (this.canScroll()) { - this.toggleScroll(); - } }; Build.prototype.sidebarOnClick = function () { if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); - this.verifyTopPosition(); }; Build.prototype.updateArtifactRemoveDate = function () { diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js index 939d17129de..f92e669414a 100644 --- a/app/assets/javascripts/jobs/job_details_bundle.js +++ b/app/assets/javascripts/jobs/job_details_bundle.js @@ -26,14 +26,6 @@ document.addEventListener('DOMContentLoaded', () => { mounted() { this.mediator.initBuildClass(); }, - updated() { - // Wait for flash message to be appended - Vue.nextTick(() => { - if (this.mediator.build) { - this.mediator.build.verifyTopPosition(); - } - }); - }, render(createElement) { return createElement('job-header', { props: { diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 9cff99b839c..23c06eca3c3 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -37,65 +37,77 @@ } .build-page { - .sticky { - position: absolute; - left: 0; - right: 0; + .build-trace-container { + position: relative; } - .build-trace-container { - position: absolute; - top: 225px; - left: 15px; - bottom: 10px; + .build-trace { background: $black; color: $gray-darkest; - font-family: $monospace_font; + white-space: pre; + overflow-x: auto; font-size: 12px; + border-radius: 0; + border: none; - &.sidebar-expanded { - right: 305px; + .bash { + display: block; } + } - &.sidebar-collapsed { - right: 16px; + .top-bar { + height: 35px; + display: flex; + justify-content: flex-end; + background: $gray-light; + border: 1px solid $border-color; + color: $gl-text-color; + position: sticky; + position: -webkit-sticky; + top: 50px; + + &.affix { + top: 50px; } - code { - background: $black; - color: $gray-darkest; + // with sidebar + &.affix.sidebar-expanded { + right: 306px; + left: 16px; } - .top-bar { - top: 0; - height: 35px; - display: flex; - justify-content: flex-end; - background: $gray-light; - border: 1px solid $border-color; - color: $gl-text-color; + // without sidebar + &.affix.sidebar-collapsed { + right: 16px; + left: 16px; + } - .truncated-info { - margin: 0 auto; - align-self: center; + &.affix-top { + position: absolute; + right: 0; + left: 0; + } - .truncated-info-size { - margin: 0 5px; - } + .truncated-info { + margin: 0 auto; + align-self: center; - .raw-link { - color: $gl-text-color; - margin-left: 5px; - text-decoration: underline; - } + .truncated-info-size { + margin: 0 5px; + } + + .raw-link { + color: $gl-text-color; + margin-left: 5px; + text-decoration: underline; } } .controllers { display: flex; - align-self: center; font-size: 15px; - margin-bottom: 4px; + justify-content: center; + align-items: center; svg { height: 15px; @@ -103,17 +115,9 @@ fill: $gl-text-color; } - .controllers-buttons, - .btn-scroll { - color: $gl-text-color; - height: 15px; - vertical-align: middle; - padding: 0; - width: 12px; - } - .controllers-buttons { - margin: 1px 10px; + color: $gl-text-color; + margin: 0 10px; } .btn-scroll.animate { @@ -143,15 +147,6 @@ } } - .bash { - top: 35px; - left: 10px; - bottom: 0; - padding: 10px 20px 20px 5px; - white-space: pre-wrap; - overflow: auto; - } - .environment-information { border: 1px solid $border-color; padding: 8px $gl-padding 12px; diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index c73bae0a2c9..e9bc1068417 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -54,13 +54,14 @@ - else Job has been erased #{time_ago_with_tooltip(@build.erased_at)} - .build-trace-container#build-trace - .top-bar.sticky + .build-trace-container.prepend-top-default + .top-bar.js-top-bar .js-truncated-info.truncated-info.hidden< Showing last %span.js-truncated-info-size.truncated-info-size>< KiB of log - %a.js-raw-link.raw-link{ href: raw_namespace_project_job_path(@project.namespace, @project, @build) }>< Complete Raw + .controllers - if @build.has_trace? = link_to raw_namespace_project_job_path(@project.namespace, @project, @build), @@ -82,10 +83,12 @@ .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 } = 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 + = render "sidebar" .js-build-options{ data: javascript_build_options } diff --git a/changelogs/unreleased/34531-remove-scroll.yml b/changelogs/unreleased/34531-remove-scroll.yml new file mode 100644 index 00000000000..c3c5289f66f --- /dev/null +++ b/changelogs/unreleased/34531-remove-scroll.yml @@ -0,0 +1,4 @@ +--- +title: Update jobs page output to have a scrollable page +merge_request: 12587 +author: -- GitLab