Skip to content

Commit

Permalink
Fix million item scrolling and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tomivirkki committed Aug 24, 2017
1 parent de481ed commit 35b7324
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 45 deletions.
66 changes: 32 additions & 34 deletions test/million-dollar-scrolling.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,30 @@
<script>
function simulateScrollToStart(grid, done) {
// make sure not over scroll more than the delta threshold limit of 10k.
var table = grid.$.scroller.$.table;
table.addEventListener('scroll', function() {
if (grid.$.scroller.$.table.scrollTop > 0) {
var table = grid.$.table;
var listener = function() {
if (table.scrollTop > 0) {
table.scrollTop -= 2000;
grid._scrollHandler();
} else {
Polymer.Base.async(done, 100);
table.removeEventListener('scroll', listener);
setTimeout(done, 100);
}
});
};
table.addEventListener('scroll', listener);
table.scrollTop -= 2500;
}

function simulateScrollToEnd(grid, done) {
// make sure not over scroll more than the delta threshold limit of 10k.
var table = grid.$.scroller.$.table;
var table = grid.$.table;
var listener = function() {
if (table.scrollTop < table.scrollHeight - table.clientHeight - 1) {
table.scrollTop += 2500;
grid._scrollHandler();
} else {
table.removeEventListener('scroll', listener);
Polymer.Base.async(done, 100);
setTimeout(done, 100);
}
};
table.addEventListener('scroll', listener);
Expand All @@ -80,65 +84,59 @@
describe('scrolling', function() {
var container, grid;

beforeEach(function(done) {
Polymer.Base.async(done, 1);
});

[100, 10000, 100000, 1000000].forEach(function(size) {
describe(size + ' items', function() {
beforeEach(function(done) {
// The before each block times out in CI with Firefox on Polymer 2
this.timeout(30000);

container = fixture('table');
grid = container.$.grid;
grid.dataProvider = infiniteDataProvider;
grid.size = size;
flush(function() {
grid.$.scroller.$.table.scrollTop = grid.$.scroller.$.table.scrollHeight;

setTimeout(function() {
grid.$.scroller.$.table.scrollTop = 0;
done();
}, 50);
});
flushGrid(grid);
grid.$.table.scrollTop = grid.$.table.scrollHeight;
grid._scrollHandler();
setTimeout(function() {
grid.$.table.scrollTop = 0;
grid._scrollHandler();
done();
}, 50);
});

it('should be able to scroll to half-way', function() {
var viewportHeight = 300;
var itemHeight = 30;
grid.$.scroller.$.table.scrollTop =
Math.floor(grid.$.scroller.$.table.scrollHeight / 2) + 1.5 * viewportHeight - 0.5 * itemHeight;
grid._scrollHandler();
var viewportHeight = grid.$.table.clientHeight - grid.$.header.clientHeight - grid.$.footer.clientHeight;
var itemHeight = getFirstCell(grid).clientHeight;
grid.$.table.scrollTop =
Math.floor(grid.$.table.scrollHeight / 2) + 1.5 * viewportHeight - 0.5 * itemHeight;

grid.$.scroller._scrollHandler();
grid.$.scroller.flushDebouncer('vaadin-grid-scrolling');
grid._scrollHandler();
flushGrid(grid);

expect(getFirstCell(grid).index).to.be.closeTo(grid.size / 2, 20);
expect(getFirstCell(grid)._instance.index).to.be.closeTo(grid.size / 2, 20);
});

it('should be able to scroll to end', function(done) {
scrollToEnd(grid, function() {
expect(getCellContent(getLastVisibleItem(grid.$.scroller)).textContent).to.contain('item' + (grid.size - 1));
expect(getCellContent(getLastVisibleItem(grid)).textContent).to.contain('item' + (grid.size - 1));
done();
});
});

it('should be able to manually scroll to start', function(done) {
grid.$.scroller.$.table.scrollTop = 20000;
grid.$.table.scrollTop = 20000;
this.timeout(1000000);

simulateScrollToStart(grid, function() {
expect(getCellContent(getFirstVisibleItem(grid.$.scroller)).textContent).to.contain('item0');
expect(getCellContent(getFirstVisibleItem(grid)).textContent).to.contain('item0');
done();
});
});

it('should be able to manually scroll to end', function(done) {
grid.$.scroller.$.table.scrollTop = grid.$.scroller.$.table.scrollHeight - 20000;
grid.$.table.scrollTop = grid.$.table.scrollHeight - 20000;
this.timeout(1000000);

simulateScrollToEnd(grid, function() {
expect(getCellContent(getLastVisibleItem(grid.$.scroller)).textContent).to.contain('item' + (grid.size - 1));
expect(getCellContent(getLastVisibleItem(grid)).textContent).to.contain('item' + (grid.size - 1));
done();
});
});
Expand Down
4 changes: 0 additions & 4 deletions vaadin-grid-scroll-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@

static get properties() {
return {
_vidxOffset: {
type: Number,
value: 0
},

ios: {
type: Boolean,
Expand Down
70 changes: 63 additions & 7 deletions vaadin-grid-scroller.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,22 @@
* @private
*/
scrollToIndex(index) {
// TODO: Implement
super.scrollToIndex(index);

// this._pendingScrollToScaledIndex = null;
// if (!this.$.items.style.borderTopWidth) {
// // Schedule another scroll to be invoked once init is complete
// this._pendingScrollToScaledIndex = idx;
// }
index = Math.min(Math.max(index, 0), this.size - 1);
this.$.table.scrollTop = index / this.size * this.$.table.scrollHeight;
this._scrollHandler();
super.scrollToIndex(index - this._vidxOffset);
// _scrollTop is not up-to-date at this point, update and run scrollhandler
super._resetScrollPosition(this._scrollPosition);
this._scrollHandler();

// TODO: This is a hack to get around offset issues when scrolling to bottom.
// Revisit iron-list-behavior for cleaner fix.
// if (this._vidxOffset + this.lastVisibleIndex === this.size - 1) {
if (this.lastVisibleIndex === this.size - 1) {
if (this._vidxOffset + this.lastVisibleIndex === this.size - 1) {
this.$.table.scrollTop = this.$.table.scrollHeight - this.$.table.offsetHeight;
this._scrollHandler();
}
Expand All @@ -65,13 +72,13 @@
// Size was reduced, scroll to 0 first
this._scrollTop = 0;
}
this.items = Array.isArray(this.items) ? this.items : {length: size};
this.items = Array.isArray(this.items) ? this.items : {length: Math.min(size, 100000)};

// Set scrollOffset to avoid scroll position resetting to 0 on _itemsChanged
this.scrollOffset = this._scrollTop;
super._itemsChanged({path:"items"});

this._virtualCount = size || 0;
this._virtualCount = this.items.length || 0;

if (this._scrollTop === 0) {
this.scrollToIndex(Math.min(size - 1, fvi));
Expand Down Expand Up @@ -126,7 +133,15 @@
}

_scrollHandler() {
var delta = this.$.table.scrollTop - this._scrollPosition;
super._scrollHandler();
const oldOffset = this._vidxOffset;
if (this._maxScrollTop && this._virtualCount < this.size) {
this._adjustVirtualIndexOffset(delta);
}
if (this._vidxOffset !== oldOffset) {
this._update();
}
this._afterScroll();
}

Expand All @@ -146,6 +161,47 @@
// Ignore
}

_adjustVirtualIndexOffset(delta) {
if (Math.abs(delta) > 10000) {
if (this._noScale) {
this._noScale = false;
return;
}
var scale = Math.round(this._scrollPosition / this._scrollHeight * 1000) / 1000;
var offset = scale * this.size;
this._vidxOffset = Math.round(offset - scale * this._virtualCount);
} else {
// Make sure user can always swipe/wheel scroll to the start and end
// TODO: causes a small jump in the scroll handle
var oldOffset = this._vidxOffset || 0;
var threshold = 1000;
var maxShift = 100;
// At start
if (this._scrollTop === 0) {
this._vidxOffset = 0;
if (oldOffset !== this._vidxOffset) {
super.scrollToIndex(0);
}
} else if (this.firstVisibleIndex < threshold && this._vidxOffset > 0) {
this._vidxOffset -= Math.min(this._vidxOffset, maxShift);
super.scrollToIndex(this.firstVisibleIndex + (oldOffset - this._vidxOffset) + 1);
this._noScale = true;
}
// At end
var maxOffset = this.size - this._virtualCount;
if (this._scrollTop >= this._maxScrollTop) {
this._vidxOffset = maxOffset;
if (oldOffset !== this._vidxOffset) {
super.scrollToIndex(this._virtualCount);
}
} else if (this.firstVisibleIndex > this._virtualCount - threshold && this._vidxOffset < maxOffset) {
this._vidxOffset += Math.min(maxOffset - this._vidxOffset, maxShift);
super.scrollToIndex(this.firstVisibleIndex - (this._vidxOffset - oldOffset));
this._noScale = true;
}
}
}

}

customElements.define(VaadinGridScroller.is, VaadinGridScroller);
Expand Down

0 comments on commit 35b7324

Please sign in to comment.