Commit 1d5669fd authored by Sean Roberts's avatar Sean Roberts

Fix mobile scrolling

parent 4b3c1325
...@@ -19,6 +19,7 @@ describe('VirtualThreadList', function () { ...@@ -19,6 +19,7 @@ describe('VirtualThreadList', function () {
}; };
var fakeScope; var fakeScope;
var fakeScrollRoot;
var fakeWindow; var fakeWindow;
function idRange(start, end) { function idRange(start, end) {
...@@ -45,6 +46,26 @@ describe('VirtualThreadList', function () { ...@@ -45,6 +46,26 @@ describe('VirtualThreadList', function () {
beforeEach(function () { beforeEach(function () {
fakeScope = {$digest: sinon.stub()}; fakeScope = {$digest: sinon.stub()};
fakeScrollRoot = {
className: 'app-content-wrapper',
scrollTop: 0,
listeners: {},
addEventListener: function (event, listener) {
this.listeners[event] = this.listeners[event] || [];
this.listeners[event].push(listener);
},
removeEventListener: function (event, listener) {
this.listeners[event] = this.listeners[event].filter(function (fn) {
return fn !== listener;
});
},
trigger: function (event) {
(this.listeners[event] || []).forEach(function (cb) {
cb();
});
},
};
fakeWindow = { fakeWindow = {
listeners: {}, listeners: {},
addEventListener: function (event, listener) { addEventListener: function (event, listener) {
...@@ -57,12 +78,19 @@ describe('VirtualThreadList', function () { ...@@ -57,12 +78,19 @@ describe('VirtualThreadList', function () {
}); });
}, },
trigger: function (event) { trigger: function (event) {
this.listeners[event].forEach(function (cb) { (this.listeners[event] || []).forEach(function (cb) {
cb(); cb();
}); });
}, },
innerHeight: 100, innerHeight: 100,
pageYOffset: 0, document: {
querySelector: function(selector){
if(selector === '.' + fakeScrollRoot.className){
return fakeScrollRoot;
}
return null;
},
},
}; };
threadOptions.invisibleThreadFilter = sinon.stub().returns(false); threadOptions.invisibleThreadFilter = sinon.stub().returns(false);
...@@ -77,7 +105,7 @@ describe('VirtualThreadList', function () { ...@@ -77,7 +105,7 @@ describe('VirtualThreadList', function () {
unroll('generates expected state when #when', function (testCase) { unroll('generates expected state when #when', function (testCase) {
var thread = generateRootThread(testCase.threads); var thread = generateRootThread(testCase.threads);
fakeWindow.pageYOffset = testCase.scrollOffset; fakeScrollRoot.scrollTop = testCase.scrollOffset;
fakeWindow.innerHeight = testCase.windowHeight; fakeWindow.innerHeight = testCase.windowHeight;
// make sure for everything that is not being presented in the // make sure for everything that is not being presented in the
...@@ -93,7 +121,7 @@ describe('VirtualThreadList', function () { ...@@ -93,7 +121,7 @@ describe('VirtualThreadList', function () {
assert.equal(lastState.offscreenUpperHeight, testCase.expectedHeightAbove); assert.equal(lastState.offscreenUpperHeight, testCase.expectedHeightAbove);
assert.equal(lastState.offscreenLowerHeight, testCase.expectedHeightBelow); assert.equal(lastState.offscreenLowerHeight, testCase.expectedHeightBelow);
},[{ },[{
when: 'window is scrolled to top of list', when: 'scrollRoot is scrolled to top of list',
threads: 100, threads: 100,
scrollOffset: 0, scrollOffset: 0,
windowHeight: 300, windowHeight: 300,
...@@ -101,7 +129,7 @@ describe('VirtualThreadList', function () { ...@@ -101,7 +129,7 @@ describe('VirtualThreadList', function () {
expectedHeightAbove: 0, expectedHeightAbove: 0,
expectedHeightBelow: 18800, expectedHeightBelow: 18800,
},{ },{
when: 'window is scrolled to middle of list', when: 'scrollRoot is scrolled to middle of list',
threads: 100, threads: 100,
scrollOffset: 2000, scrollOffset: 2000,
windowHeight: 300, windowHeight: 300,
...@@ -109,7 +137,7 @@ describe('VirtualThreadList', function () { ...@@ -109,7 +137,7 @@ describe('VirtualThreadList', function () {
expectedHeightAbove: 1000, expectedHeightAbove: 1000,
expectedHeightBelow: 16800, expectedHeightBelow: 16800,
},{ },{
when: 'window is scrolled to bottom of list', when: 'scrollRoot is scrolled to bottom of list',
threads: 100, threads: 100,
scrollOffset: 18800, scrollOffset: 18800,
windowHeight: 300, windowHeight: 300,
...@@ -118,15 +146,17 @@ describe('VirtualThreadList', function () { ...@@ -118,15 +146,17 @@ describe('VirtualThreadList', function () {
expectedHeightBelow: 0, expectedHeightBelow: 0,
}]); }]);
unroll('recalculates when a window.#event occurs', function (testCase) { it('recalculates when a window.resize occurs', function () {
lastState = null; lastState = null;
fakeWindow.trigger(testCase.event); fakeWindow.trigger('resize');
assert.ok(lastState); assert.ok(lastState);
},[{ });
event: 'resize',
},{ it('recalculates when a scrollRoot.scroll occurs', function () {
event: 'scroll', lastState = null;
}]); fakeScrollRoot.trigger('scroll');
assert.ok(lastState);
});
it('recalculates when root thread changes', function () { it('recalculates when root thread changes', function () {
threadList.setRootThread({annotation: undefined, children: []}); threadList.setRootThread({annotation: undefined, children: []});
...@@ -137,7 +167,7 @@ describe('VirtualThreadList', function () { ...@@ -137,7 +167,7 @@ describe('VirtualThreadList', function () {
unroll('affects visible threads', function (testCase) { unroll('affects visible threads', function (testCase) {
var thread = generateRootThread(10); var thread = generateRootThread(10);
fakeWindow.innerHeight = 500; fakeWindow.innerHeight = 500;
fakeWindow.pageYOffset = 0; fakeScrollRoot.scrollTop = 0;
idRange(0,10).forEach(function (id) { idRange(0,10).forEach(function (id) {
threadList.setThreadHeight(id, testCase.threadHeight); threadList.setThreadHeight(id, testCase.threadHeight);
}); });
...@@ -154,16 +184,18 @@ describe('VirtualThreadList', function () { ...@@ -154,16 +184,18 @@ describe('VirtualThreadList', function () {
}); });
describe('#detach', function () { describe('#detach', function () {
unroll('stops listening to window.#event events', function (testCase) { it('stops listening to window.resize events', function () {
threadList.detach(); threadList.detach();
lastState = null; lastState = null;
fakeWindow.trigger(testCase.event); fakeWindow.trigger('resize');
assert.isNull(lastState); assert.isNull(lastState);
},[{ });
event: 'resize', it('stops listening to scrollRoot.scroll events', function () {
},{ threadList.detach();
event: 'scroll', lastState = null;
}]); fakeScrollRoot.trigger('scroll');
assert.isNull(lastState);
});
}); });
describe('#yOffsetOf', function () { describe('#yOffsetOf', function () {
......
...@@ -36,16 +36,17 @@ function VirtualThreadList($scope, window_, rootThread, options) { ...@@ -36,16 +36,17 @@ function VirtualThreadList($scope, window_, rootThread, options) {
this._heights = {}; this._heights = {};
this.window = window_; this.window = window_;
this.scrollRoot = this.window.document.querySelector('.app-content-wrapper');
var debouncedUpdate = debounce(function () { var debouncedUpdate = debounce(function () {
self._updateVisibleThreads(); self._updateVisibleThreads();
$scope.$digest(); $scope.$digest();
}, 20); }, 20);
this.window.addEventListener('scroll', debouncedUpdate); this.scrollRoot.addEventListener('scroll', debouncedUpdate);
this.window.addEventListener('resize', debouncedUpdate); this.window.addEventListener('resize', debouncedUpdate);
this._detach = function () { this._detach = function () {
this.window.removeEventListener('scroll', debouncedUpdate); this.scrollRoot.removeEventListener('scroll', debouncedUpdate);
this.window.removeEventListener('resize', debouncedUpdate); this.window.removeEventListener('resize', debouncedUpdate);
}; };
} }
...@@ -153,17 +154,20 @@ VirtualThreadList.prototype._updateVisibleThreads = function () { ...@@ -153,17 +154,20 @@ VirtualThreadList.prototype._updateVisibleThreads = function () {
for (var i = 0; i < allThreads.length; i++) { for (var i = 0; i < allThreads.length; i++) {
thread = allThreads[i]; thread = allThreads[i];
var threadHeight = this._height(thread.id); var threadHeight = this._height(thread.id);
var added = false; var added = false;
if (usedHeight + threadHeight < this.window.pageYOffset - MARGIN_ABOVE) { if (usedHeight + threadHeight < this.scrollRoot.scrollTop - MARGIN_ABOVE) {
// Thread is above viewport // Thread is above viewport
offscreenUpperHeight += threadHeight; offscreenUpperHeight += threadHeight;
} else if (usedHeight < } else if (usedHeight <
this.window.pageYOffset + visibleHeight + MARGIN_BELOW) { this.scrollRoot.scrollTop + visibleHeight + MARGIN_BELOW) {
// Thread is either in or close to the viewport // Thread is either in or close to the viewport
visibleThreads.push(thread); visibleThreads.push(thread);
added = true; added = true;
} else { } else {
// Thread is below viewport // Thread is below viewport
offscreenLowerHeight += threadHeight; offscreenLowerHeight += threadHeight;
} }
......
...@@ -57,6 +57,9 @@ hypothesis-app { ...@@ -57,6 +57,9 @@ hypothesis-app {
@include grey-background; @include grey-background;
min-height: 100%; min-height: 100%;
height: 100%;
overflow: scroll;
-webkit-overflow-scrolling: touch;
padding: $sidebar-h-padding; padding: $sidebar-h-padding;
padding-top: $sidebar-h-padding + $top-bar-height; padding-top: $sidebar-h-padding + $top-bar-height;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
border-bottom: solid 1px $gray-lighter; border-bottom: solid 1px $gray-lighter;
height: $top-bar-height; height: $top-bar-height;
font-size: 15px; font-size: 15px;
position: fixed; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
......
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