Commit 35bf473f authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'jivl-backport-scroll-utilities' into 'master'

Backport scroll utilities for the job log component

See merge request gitlab-org/gitlab-ce!19221
parents 38f95d6c 7644edd8
...@@ -6,9 +6,12 @@ import { visitUrl } from './lib/utils/url_utility'; ...@@ -6,9 +6,12 @@ import { visitUrl } from './lib/utils/url_utility';
import bp from './breakpoints'; import bp from './breakpoints';
import { numberToHumanSize } from './lib/utils/number_utils'; import { numberToHumanSize } from './lib/utils/number_utils';
import { setCiStatusFavicon } from './lib/utils/common_utils'; import { setCiStatusFavicon } from './lib/utils/common_utils';
import { isScrolledToBottom, scrollDown } from './lib/utils/scroll_utils';
import LogOutputBehaviours from './lib/utils/logoutput_behaviours';
export default class Job { export default class Job extends LogOutputBehaviours {
constructor(options) { constructor(options) {
super();
this.timeout = null; this.timeout = null;
this.state = null; this.state = null;
this.fetchingStatusFavicon = false; this.fetchingStatusFavicon = false;
...@@ -29,10 +32,6 @@ export default class Job { ...@@ -29,10 +32,6 @@ export default class Job {
this.$buildTraceOutput = $('.js-build-output'); this.$buildTraceOutput = $('.js-build-output');
this.$topBar = $('.js-top-bar'); this.$topBar = $('.js-top-bar');
// Scroll controllers
this.$scrollTopBtn = $('.js-scroll-up');
this.$scrollBottomBtn = $('.js-scroll-down');
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.initSidebar(); this.initSidebar();
...@@ -48,23 +47,14 @@ export default class Job { ...@@ -48,23 +47,14 @@ export default class Job {
.off('click', '.stage-item') .off('click', '.stage-item')
.on('click', '.stage-item', this.updateDropdown); .on('click', '.stage-item', this.updateDropdown);
// add event listeners to the scroll buttons
this.$scrollTopBtn
.off('click')
.on('click', this.scrollToTop.bind(this));
this.$scrollBottomBtn
.off('click')
.on('click', this.scrollToBottom.bind(this));
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
this.$window this.$window
.off('scroll') .off('scroll')
.on('scroll', () => { .on('scroll', () => {
if (!this.isScrolledToBottom()) { if (!isScrolledToBottom()) {
this.toggleScrollAnimation(false); this.toggleScrollAnimation(false);
} else if (this.isScrolledToBottom() && !this.isLogComplete) { } else if (isScrolledToBottom() && !this.isLogComplete) {
this.toggleScrollAnimation(true); this.toggleScrollAnimation(true);
} }
this.scrollThrottled(); this.scrollThrottled();
...@@ -90,60 +80,8 @@ export default class Job { ...@@ -90,60 +80,8 @@ export default class Job {
StickyFill.add(this.$topBar); StickyFill.add(this.$topBar);
} }
// eslint-disable-next-line class-methods-use-this
canScroll() {
return $(document).height() > $(window).height();
}
toggleScroll() {
const $document = $(document);
const currentPosition = $document.scrollTop();
const scrollHeight = $document.height();
const windowHeight = $(window).height();
if (this.canScroll()) {
if (currentPosition > 0 &&
(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 Log
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, false);
} else if (this.isScrolledToBottom()) {
// User is at the bottom of the build log.
this.toggleDisableButton(this.$scrollTopBtn, false);
this.toggleDisableButton(this.$scrollBottomBtn, true);
}
} else {
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, true);
}
}
// eslint-disable-next-line class-methods-use-this
isScrolledToBottom() {
const $document = $(document);
const currentPosition = $document.scrollTop();
const scrollHeight = $document.height();
const windowHeight = $(window).height();
return scrollHeight - currentPosition === windowHeight;
}
// eslint-disable-next-line class-methods-use-this
scrollDown() {
const $document = $(document);
$document.scrollTop($document.height());
}
scrollToBottom() { scrollToBottom() {
this.scrollDown(); scrollDown();
this.hasBeenScrolled = true; this.hasBeenScrolled = true;
this.toggleScroll(); this.toggleScroll();
} }
...@@ -154,12 +92,6 @@ export default class Job { ...@@ -154,12 +92,6 @@ export default class Job {
this.toggleScroll(); this.toggleScroll();
} }
// eslint-disable-next-line class-methods-use-this
toggleDisableButton($button, disable) {
if (disable && $button.prop('disabled')) return;
$button.prop('disabled', disable);
}
toggleScrollAnimation(toggle) { toggleScrollAnimation(toggle) {
this.$scrollBottomBtn.toggleClass('animate', toggle); this.$scrollBottomBtn.toggleClass('animate', toggle);
} }
...@@ -191,7 +123,7 @@ export default class Job { ...@@ -191,7 +123,7 @@ export default class Job {
this.state = log.state; this.state = log.state;
} }
this.isScrollInBottom = this.isScrolledToBottom(); this.isScrollInBottom = isScrolledToBottom();
if (log.append) { if (log.append) {
this.$buildTraceOutput.append(log.html); this.$buildTraceOutput.append(log.html);
...@@ -231,7 +163,7 @@ export default class Job { ...@@ -231,7 +163,7 @@ export default class Job {
}) })
.then(() => { .then(() => {
if (this.isScrollInBottom) { if (this.isScrollInBottom) {
this.scrollDown(); scrollDown();
} }
}) })
.then(() => this.toggleScroll()); .then(() => this.toggleScroll());
......
import $ from 'jquery';
import { canScroll, isScrolledToBottom, toggleDisableButton } from './scroll_utils';
export default class LogOutputBehaviours {
constructor() {
// Scroll buttons
this.$scrollTopBtn = $('.js-scroll-up');
this.$scrollBottomBtn = $('.js-scroll-down');
this.$scrollTopBtn.off('click').on('click', this.scrollToTop.bind(this));
this.$scrollBottomBtn.off('click').on('click', this.scrollToBottom.bind(this));
}
toggleScroll() {
const $document = $(document);
const currentPosition = $document.scrollTop();
const scrollHeight = $document.height();
const windowHeight = $(window).height();
if (canScroll()) {
if (currentPosition > 0 && scrollHeight - currentPosition !== windowHeight) {
// User is in the middle of the log
toggleDisableButton(this.$scrollTopBtn, false);
toggleDisableButton(this.$scrollBottomBtn, false);
} else if (currentPosition === 0) {
// User is at Top of Log
toggleDisableButton(this.$scrollTopBtn, true);
toggleDisableButton(this.$scrollBottomBtn, false);
} else if (isScrolledToBottom()) {
// User is at the bottom of the build log.
toggleDisableButton(this.$scrollTopBtn, false);
toggleDisableButton(this.$scrollBottomBtn, true);
}
} else {
toggleDisableButton(this.$scrollTopBtn, true);
toggleDisableButton(this.$scrollBottomBtn, true);
}
}
toggleScrollAnimation(toggle) {
this.$scrollBottomBtn.toggleClass('animate', toggle);
}
}
import $ from 'jquery';
export const canScroll = () => $(document).height() > $(window).height();
/**
* Checks if the entire page is scrolled down all the way to the bottom
*/
export const isScrolledToBottom = () => {
const $document = $(document);
const currentPosition = $document.scrollTop();
const scrollHeight = $document.height();
const windowHeight = $(window).height();
return scrollHeight - currentPosition === windowHeight;
};
export const scrollDown = () => {
const $document = $(document);
$document.scrollTop($document.height());
};
export const toggleDisableButton = ($button, disable) => {
if (disable && $button.prop('disabled')) return;
$button.prop('disabled', disable);
};
export default {};
...@@ -86,9 +86,7 @@ ...@@ -86,9 +86,7 @@
%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')
%pre.build-trace#build-trace = render 'shared/builds/build_output'
%code.bash.js-build-output
.build-loader-animation.js-build-refresh
- else - else
= render "empty_states" = render "empty_states"
......
%pre.build-trace#build-trace
%code.bash.js-build-output
.build-loader-animation.js-build-refresh
...@@ -4,7 +4,7 @@ module QA::Page ...@@ -4,7 +4,7 @@ module QA::Page
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
PASSED_STATUS = 'passed'.freeze PASSED_STATUS = 'passed'.freeze
view 'app/views/projects/jobs/show.html.haml' do view 'app/views/shared/builds/_build_output.html.haml' do
element :build_output, '.js-build-output' element :build_output, '.js-build-output'
end end
......
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