Skip to content

Commit f98e634

Browse files
committed
test(infinite-scroll): adding tests for preserve rerender scroll position
1 parent 0e4473f commit f98e634

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

core/src/components/infinite-scroll/infinite-scroll.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -157,28 +157,35 @@ export class InfiniteScroll implements ComponentInterface {
157157
return 4;
158158
};
159159

160+
/**
161+
* Loop through our sibling elements and lock or unlock their min height.
162+
* This keeps our siblings, for example `ion-list`, the same height as their
163+
* content currently is, so when it loads new data and the DOM removes the old
164+
* data, the height of the container doesn't change and we don't lose our scroll position.
165+
*
166+
* We preserve existing min-height values, if they're set, so we don't erase what
167+
* has been previously set by the user when we restore after complete is called.
168+
*/
160169
private lockSiblingMinHeight(lock: boolean) {
161170
if (!this.preserveRerenderScrollPosition) {
162171
return;
163172
}
164173

165174
// Loop through all the siblings of the infinite scroll, but ignore the infinite scroll itself
166-
const siblings = this.el.parentElement?.children;
167-
if (siblings) {
168-
for (const sibling of siblings) {
169-
if (sibling !== this.el && sibling instanceof HTMLElement) {
170-
if (lock) {
171-
const elementHeight = sibling.getBoundingClientRect().height;
172-
const previousMinHeight = sibling.style.minHeight;
173-
if (previousMinHeight) {
174-
sibling.style.setProperty('--ion-previous-min-height', previousMinHeight);
175-
}
176-
sibling.style.minHeight = `${elementHeight}px`;
177-
} else {
178-
const previousMinHeight = sibling.style.getPropertyValue('--ion-previous-min-height');
179-
sibling.style.minHeight = previousMinHeight || 'auto';
180-
sibling.style.removeProperty('--ion-previous-min-height');
175+
const siblings = this.el.parentElement?.children || [];
176+
for (const sibling of siblings) {
177+
if (sibling !== this.el && sibling instanceof HTMLElement) {
178+
if (lock) {
179+
const elementHeight = sibling.getBoundingClientRect().height;
180+
const previousMinHeight = sibling.style.minHeight;
181+
if (previousMinHeight) {
182+
sibling.style.setProperty('--ion-previous-min-height', previousMinHeight);
181183
}
184+
sibling.style.minHeight = `${elementHeight}px`;
185+
} else {
186+
const previousMinHeight = sibling.style.getPropertyValue('--ion-previous-min-height');
187+
sibling.style.minHeight = previousMinHeight || 'auto';
188+
sibling.style.removeProperty('--ion-previous-min-height');
182189
}
183190
}
184191
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
5+
test.describe(title('infinite-scroll: preserve rerender scroll position'), () => {
6+
test('should load more items when scrolled to the bottom', async ({ page }) => {
7+
await page.goto('/src/components/infinite-scroll/test/preserve-rerender-scroll-position', config);
8+
9+
const ionInfiniteComplete = await page.spyOnEvent('ionInfiniteComplete');
10+
const content = page.locator('ion-content');
11+
const items = page.locator('ion-item');
12+
const innerScroll = page.locator('.inner-scroll');
13+
expect(await items.count()).toBe(30);
14+
15+
let previousScrollTop = 0;
16+
for (let i = 0; i < 10; i++) {
17+
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
18+
const currentScrollTop = await innerScroll.evaluate((el: HTMLIonContentElement) => el.scrollTop);
19+
expect(currentScrollTop).toBeGreaterThan(previousScrollTop);
20+
await ionInfiniteComplete.next();
21+
const newScrollTop = await innerScroll.evaluate((el: HTMLIonContentElement) => el.scrollTop);
22+
console.log(`Scroll position should be preserved after ${i + 1} iterations`, newScrollTop, previousScrollTop);
23+
expect(newScrollTop, `Scroll position should be preserved after ${i + 1} iterations`).toBeGreaterThanOrEqual(previousScrollTop);
24+
previousScrollTop = currentScrollTop;
25+
26+
// Timeout to allow the browser to catch up.
27+
// For some reason, without this, the scroll top gets reset to 0. Adding this
28+
// prevents that, which implies it's an issue with Playwright, not the feature.
29+
await new Promise(resolve => setTimeout(resolve, 100));
30+
}
31+
});
32+
});
33+
});

0 commit comments

Comments
 (0)