diff --git a/src/vaadin-grid-scroller.html b/src/vaadin-grid-scroller.html index d07b413a3..1973672f1 100644 --- a/src/vaadin-grid-scroller.html +++ b/src/vaadin-grid-scroller.html @@ -280,6 +280,27 @@ _scrollHandler() { const delta = this.$.table.scrollTop - this._scrollPosition; this._accessIronListAPI(super._scrollHandler); + + if (this._physicalCount !== 0) { + const isScrollingDown = delta >= 0; + const reusables = this._getReusables(!isScrollingDown); + + if (reusables.indexes.length) { + // After running super._scrollHandler, fix internal properties to workaround an iron-list issue. + // See https://github.com/vaadin/web-components/issues/1691 + this._physicalTop = reusables.physicalTop; + + if (isScrollingDown) { + this._virtualStart -= reusables.indexes.length; + this._physicalStart -= reusables.indexes.length; + } else { + this._virtualStart += reusables.indexes.length; + this._physicalStart += reusables.indexes.length; + } + this._update(reusables.indexes, !isScrollingDown); + } + } + const oldOffset = this._vidxOffset; if (this._accessIronListAPI(() => this._maxScrollTop) && this._virtualCount < this._effectiveSize) { this._adjustVirtualIndexOffset(delta); diff --git a/test/scrolling-mode.html b/test/scrolling-mode.html index dfa41f14f..f1b3bbac2 100644 --- a/test/scrolling-mode.html +++ b/test/scrolling-mode.html @@ -188,6 +188,59 @@ grid.$.table.dispatchEvent(new CustomEvent('scroll')); }); + it('should have an item at the bottom of the viewport after scrolling up', async() => { + const scrollTarget = grid.$.table; + grid.size = 100000; + await nextFrame(); + + // Scroll to end + grid.scrollToIndex(100000); + await nextFrame(); + + // Scroll upwards in 1000px steps + for (let i = 0; i < 20; i++) { + await nextFrame(); + scrollTarget.scrollTop -= 1000; + + // Sanity check for iron-list internal properties + const firstItem = grid._physicalItems[grid._physicalStart]; + expect(firstItem.index).to.equal(grid._virtualStart + grid._vidxOffset); + } + + // There should be an item at the bottom of the viewport + await nextFrame(); + const gridRect = scrollTarget.getBoundingClientRect(); + const lastVisibleElement = grid._cellFromPoint(gridRect.left, gridRect.bottom - 30); + expect(lastVisibleElement.localName).to.equal('td'); + }); + + it('should have an item at the top of the viewport after scrolling down', async() => { + const scrollTarget = grid.$.table; + grid.size = 100000; + await nextFrame(); + + // Scroll to start + grid.scrollToIndex(0); + await nextFrame(); + + // Scroll downwards in 1000px steps + for (let i = 0; i < 20; i++) { + await nextFrame(); + scrollTarget.scrollTop += 1000; + + // Sanity check for iron-list internal properties + const firstItem = grid._physicalItems[grid._physicalStart]; + expect(firstItem.index).to.equal(grid._virtualStart + grid._vidxOffset); + } + + // There should be an item at the top of the viewport + flushGrid(grid); + await nextFrame(); + const gridRect = scrollTarget.getBoundingClientRect(); + const firstVisibleElement = grid._cellFromPoint(gridRect.left, gridRect.top + 30); + expect(firstVisibleElement.localName).to.equal('td'); + }); + describe('overflow attribute', () => { it('bottom right', () => {