diff --git a/packages/component-base/src/virtualizer-iron-list-adapter.js b/packages/component-base/src/virtualizer-iron-list-adapter.js index 0dea4a2471..7a505769cf 100644 --- a/packages/component-base/src/virtualizer-iron-list-adapter.js +++ b/packages/component-base/src/virtualizer-iron-list-adapter.js @@ -5,7 +5,7 @@ */ /* eslint-disable @typescript-eslint/member-ordering */ // https://github.com/vaadin/eslint-config-vaadin/issues/33 -import { animationFrame, timeOut } from './async.js'; +import { animationFrame, microTask, timeOut } from './async.js'; import { isSafari } from './browser-utils.js'; import { Debouncer, flush } from './debounce.js'; import { ironList } from './iron-list-core.js'; @@ -353,6 +353,9 @@ export class IronListAdapter { // Schedule and flush a resize handler this._resizeHandler(); flush(); + // Schedule an update to ensure item positions are correct after subsequent size changes + // Fix for https://github.com/vaadin/flow-components/issues/6269 + this._debounce('_update', this._update, microTask); } /** @private */ diff --git a/packages/component-base/test/virtualizer-variable-row-height.test.js b/packages/component-base/test/virtualizer-variable-row-height.test.js index 6d6146e41f..5a477b3e85 100644 --- a/packages/component-base/test/virtualizer-variable-row-height.test.js +++ b/packages/component-base/test/virtualizer-variable-row-height.test.js @@ -1,5 +1,5 @@ import { expect } from '@esm-bundle/chai'; -import { fixtureSync, nextFrame } from '@vaadin/testing-helpers'; +import { aTimeout, fixtureSync, nextFrame } from '@vaadin/testing-helpers'; import sinon from 'sinon'; import { Virtualizer } from '../src/virtualizer.js'; @@ -233,3 +233,52 @@ describe('virtualizer - variable row height - large variance', () => { expect(virtualizer.__adapter.__fixInvalidItemPositioning.callCount).to.equal(0); }); }); + +describe('virtualizer - variable row height - size changes', () => { + let virtualizer; + + beforeEach(async () => { + const reverseItemHeights = [50, 30]; + + const scrollTarget = fixtureSync(` +
+
+
+ `); + const scrollContainer = scrollTarget.firstElementChild; + + virtualizer = new Virtualizer({ + createElements: (count) => Array.from(Array(count)).map(() => document.createElement('div')), + updateElement: (el, index) => { + el.style.height = `${reverseItemHeights[virtualizer.size - index - 1]}px`; + el.style.outline = '1px solid black'; + el.style.outlineOffset = '-1px'; + el.style.width = '100%'; + el.textContent = `Item ${index}`; + el.id = `item-${index}`; + }, + scrollTarget, + scrollContainer, + }); + + virtualizer.size = 2; + // Wait for a possible resize observer flush + await aTimeout(100); + }); + + it('should position the items correctly after subsequent size changes', async () => { + virtualizer.size = 1; + virtualizer.update(); + + virtualizer.size = 2; + virtualizer.update(); + + await nextFrame(); + + const item0 = document.getElementById('item-0'); + const item1 = document.getElementById('item-1'); + expect(item0.clientHeight).to.equal(30); + expect(item1.clientHeight).to.equal(50); + expect(item1.getBoundingClientRect().top).to.equal(item0.getBoundingClientRect().bottom); + }); +});