Skip to content

Commit eb305b7

Browse files
committed
Cleanup the fix, improve tests
1 parent f46f510 commit eb305b7

File tree

4 files changed

+46
-23
lines changed

4 files changed

+46
-23
lines changed

packages/component-base/src/virtualizer-iron-list-adapter.js

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,6 @@ export class IronListAdapter {
180180
this._resizeHandler();
181181
flush();
182182
this._scrollHandler();
183-
if (this.__physicalSizesChangedDebouncer) {
184-
this.__physicalSizesChangedDebouncer.flush();
185-
}
186183
if (this.__fixInvalidItemPositioningDebouncer) {
187184
this.__fixInvalidItemPositioningDebouncer.flush();
188185
}
@@ -231,16 +228,15 @@ export class IronListAdapter {
231228
const prevAvgCount = this._physicalAverageCount;
232229
const prevPhysicalAvg = this._physicalAverage;
233230

234-
let physicalSizesChanged = false;
235231
// eslint-disable-next-line @typescript-eslint/no-unused-vars
236232
this._iterateItems((pidx, vidx) => {
237233
oldPhysicalSize += this._physicalSizes[pidx];
238-
const elementOldPhysicalSize = this._physicalSizes[pidx] || 0;
234+
const elementOldPhysicalSize = this._physicalSizes[pidx];
239235
this._physicalSizes[pidx] = Math.ceil(this.__getBorderBoxHeight(this._physicalItems[pidx]));
240236

241237
if (this._physicalSizes[pidx] !== elementOldPhysicalSize) {
242238
// Physical size changed, but resize observer may not catch it if the original size is restored quickly.
243-
physicalSizesChanged = true;
239+
this.__schedulePhysicalSizesChangedDebouncer();
244240
}
245241

246242
newPhysicalSize += this._physicalSizes[pidx];
@@ -255,13 +251,27 @@ export class IronListAdapter {
255251
(prevPhysicalAvg * prevAvgCount + newPhysicalSize) / this._physicalAverageCount,
256252
);
257253
}
254+
}
258255

259-
if (physicalSizesChanged) {
260-
// There were changes in physical sizes, schedule a resize handler call
261-
this.__physicalSizesChangedDebouncer = Debouncer.debounce(this.__physicalSizesChangedDebouncer, microTask, () => {
262-
this._resizeHandler();
263-
});
264-
}
256+
__schedulePhysicalSizesChangedDebouncer() {
257+
this.__physicalSizesChangedDebouncer = Debouncer.debounce(
258+
this.__physicalSizesChangedDebouncer,
259+
animationFrame,
260+
() => {
261+
if (this.__hasSizeChanges()) {
262+
this._updateMetrics();
263+
this._positionItems();
264+
this._updateScrollerSize();
265+
}
266+
},
267+
);
268+
}
269+
270+
__hasSizeChanges() {
271+
return this._physicalItems?.some((item, pidx) => {
272+
const currentSize = Math.ceil(this.__getBorderBoxHeight(item));
273+
return currentSize !== this._physicalSizes[pidx];
274+
});
265275
}
266276

267277
__getBorderBoxHeight(el) {
@@ -646,11 +656,6 @@ export class IronListAdapter {
646656
_resizeHandler() {
647657
super._resizeHandler();
648658

649-
if (this.__physicalSizesChangedDebouncer && this.__physicalSizesChangedDebouncer.isActive()) {
650-
// Cancel any pending debounced calls to avoid unnecessary extra work
651-
this.__physicalSizesChangedDebouncer.cancel();
652-
}
653-
654659
// Fixes an issue where the new items are not created on scroll target resize when the scroll position is around the end.
655660
// See https://github.com/vaadin/flow-components/issues/7307
656661
const lastIndexVisible = this.adjustedLastVisibleIndex === this.size - 1;

packages/component-base/test/virtualizer-item-height.test.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from '@vaadin/chai-plugins';
2-
import { aTimeout, fixtureSync, nextFrame, nextResize } from '@vaadin/testing-helpers';
2+
import { aTimeout, fixtureSync, nextFrame, nextResize, oneEvent } from '@vaadin/testing-helpers';
33
import sinon from 'sinon';
44
import { Virtualizer } from '../src/virtualizer.js';
55

@@ -523,8 +523,10 @@ describe('virtualizer - item height - self-resizing items', () => {
523523
it('should not overlap items after scrolling', async () => {
524524
await contentUpdate();
525525
// Scroll manually to the end
526-
scrollTarget.scrollTop = scrollTarget.scrollHeight;
527-
await contentUpdate();
526+
while (Math.ceil(scrollTarget.scrollTop) < scrollTarget.scrollHeight - scrollTarget.clientHeight) {
527+
scrollTarget.scrollTop += 100;
528+
await oneEvent(scrollTarget, 'scroll');
529+
}
528530

529531
// Ensure that the first two visible items do not overlap
530532
const firstVisibleItem = scrollTarget.querySelector(`#item-${virtualizer.firstVisibleIndex}`);

packages/component-base/test/virtualizer-variable-row-height.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ describe('virtualizer - variable row height - large variance', () => {
6464
// Scroll to last item
6565
virtualizer.scrollToIndex(virtualizer.size - 1);
6666
await nextFrame();
67-
virtualizer.scrollToIndex(virtualizer.size - 1);
6867
await nextFrame();
6968
// Manually scroll to end
7069
scrollTarget.scrollTop = scrollTarget.scrollHeight - scrollTarget.offsetHeight;

test/integration/virtual-list-card.test.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from '@vaadin/chai-plugins';
2-
import { aTimeout, fixtureSync } from '@vaadin/testing-helpers';
2+
import { aTimeout, fixtureSync, nextFrame, oneEvent } from '@vaadin/testing-helpers';
33
import '@vaadin/virtual-list';
44
import '@vaadin/card';
55

@@ -28,10 +28,27 @@ describe('virtual-list with card items', () => {
2828
};
2929

3030
virtualList.items = Array.from({ length: 100 }, (_, i) => ({ index: i }));
31-
await contentUpdate();
31+
await nextFrame();
3232
});
3333

3434
it('should not overlap items after scrolling', async () => {
35+
// Scroll manually to the end
36+
while (Math.ceil(virtualList.scrollTop) < virtualList.scrollHeight - virtualList.clientHeight) {
37+
virtualList.scrollTop += 100;
38+
await oneEvent(virtualList, 'scroll');
39+
}
40+
41+
await contentUpdate();
42+
43+
// Ensure that the first two visible items do not overlap
44+
const firstVisibleItem = virtualList.querySelector(`#card-${virtualList.firstVisibleIndex}`);
45+
const secondVisibleItem = virtualList.querySelector(`#card-${virtualList.firstVisibleIndex + 1}`);
46+
expect(firstVisibleItem.getBoundingClientRect().bottom).to.be.at.most(
47+
secondVisibleItem.getBoundingClientRect().top,
48+
);
49+
});
50+
51+
it('should not overlap items after changing scroll position', async () => {
3552
virtualList.scrollTop = virtualList.scrollHeight;
3653

3754
await contentUpdate();

0 commit comments

Comments
 (0)