Skip to content

Commit

Permalink
fix: restore scroll position when virtualizer used in dialog (#8642)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored and vaadin-bot committed Feb 11, 2025
1 parent 17738c2 commit 73a4be4
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 1 deletion.
14 changes: 14 additions & 0 deletions packages/component-base/src/virtualizer-iron-list-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ export class IronListAdapter {
this.__resizeObserver.observe(this.scrollTarget);
this.scrollTarget.addEventListener('scroll', () => this._scrollHandler());

const attachObserver = new ResizeObserver(([{ contentRect }]) => {
const isHidden = contentRect.width === 0 && contentRect.height === 0;
if (!isHidden && this.__scrollTargetHidden && this.scrollTarget.scrollTop !== this._scrollPosition) {
// When removing element from DOM, its scroll position is lost and
// virtualizer doesn't re-render when adding it to the DOM again.
// Restore scroll position when the scroll target becomes visible,
// which is the case e.g. when virtualizer is used inside a dialog.
this.scrollTarget.scrollTop = this._scrollPosition;
}

this.__scrollTargetHidden = isHidden;
});
attachObserver.observe(this.scrollTarget);

this._scrollLineHeight = this._getScrollLineHeight();
this.scrollTarget.addEventListener('wheel', (e) => this.__onWheel(e));

Expand Down
18 changes: 17 additions & 1 deletion packages/component-base/test/virtualizer.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from '@vaadin/chai-plugins';
import { aTimeout, fixtureSync, nextFrame, oneEvent } from '@vaadin/testing-helpers';
import { aTimeout, fixtureSync, nextFrame, nextResize, oneEvent } from '@vaadin/testing-helpers';
import sinon from 'sinon';
import { Virtualizer } from '../src/virtualizer.js';

Expand Down Expand Up @@ -350,6 +350,22 @@ describe('virtualizer', () => {
expect(initialCount).not.to.be.above(expectedCount);
});

it('should preserve scroll position when moving within DOM and changing visibility', async () => {
scrollTarget.scrollTop = 100;
await oneEvent(scrollTarget, 'scroll');

scrollTarget.hidden = true;
await nextResize(scrollTarget);

const wrapper = fixtureSync('<div></div>');
wrapper.appendChild(scrollTarget);

scrollTarget.hidden = false;
await nextResize(scrollTarget);

expect(scrollTarget.scrollTop).to.equal(100);
});

describe('lazy rendering', () => {
let render = false;

Expand Down

0 comments on commit 73a4be4

Please sign in to comment.