feat(content, reorder-group, header, footer, infinite-scroll, refresher): add custom scroll target to improve compatibility with virtual scroll (#24883)

Resolves #23437
This commit is contained in:
Sean Perkins
2022-03-15 11:47:46 -04:00
committed by GitHub
parent 171020e9d2
commit 2a438da010
38 changed files with 1303 additions and 176 deletions

View File

@ -1,7 +1,7 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Method, Prop, State, Watch, h, readTask, writeTask } from '@stencil/core';
import { findClosestIonContent, getScrollElement, printIonContentErrorMsg } from '@utils/content';
import { getIonMode } from '../../global/ionic-global';
import { componentOnReady } from '../../utils/helpers';
@Component({
tag: 'ion-infinite-scroll',
@ -78,13 +78,12 @@ export class InfiniteScroll implements ComponentInterface {
@Event() ionInfinite!: EventEmitter<void>;
async connectedCallback() {
const contentEl = this.el.closest('ion-content');
const contentEl = findClosestIonContent(this.el);
if (!contentEl) {
console.error('<ion-infinite-scroll> must be used inside an <ion-content>');
printIonContentErrorMsg(this.el);
return;
}
await new Promise(resolve => componentOnReady(contentEl, resolve));
this.scrollEl = await contentEl.getScrollElement();
this.scrollEl = await getScrollElement(contentEl);
this.thresholdChanged();
this.disabledChanged();
if (this.position === 'top') {

View File

@ -12,6 +12,21 @@ The `ion-infinite-scroll` component has the infinite scroll logic. It requires a
Separating the `ion-infinite-scroll` and `ion-infinite-scroll-content` components allows developers to create their own content components, if desired. This content can contain anything, from an SVG element to elements with unique CSS animations.
## Usage with Virtual Scroll
Infinite scroll requires a scroll container to function. When using a virtual scrolling solution, you will need to disable scrolling on the `ion-content` and indicate which element container is responsible for the scroll container with the `.ion-content-scroll-host` class target.
```html
<ion-content scroll-y="false">
<virtual-scroll-element class="ion-content-scroll-host">
<!-- Your virtual scroll content -->
</virtual-scroll-element>
<ion-infinite-scroll>
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>
```
## Interfaces
### InfiniteScrollCustomEvent

View File

@ -16,7 +16,6 @@
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Infinite Scroll - Basic</ion-title>

View File

@ -0,0 +1,35 @@
import { newE2EPage } from '@stencil/core/testing';
import type { E2EPage } from '@stencil/core/testing';
import { scrollToBottom } from '@utils/test';
/**
* Scrolls an `ion-content` element to the bottom, triggering the `ionInfinite` event.
* Waits for the custom event to complete.
*/
async function scrollPage(page: E2EPage) {
await scrollToBottom(page, '#scroll-target');
await page.waitForChanges();
const ev = await page.spyOnEvent('ionInfiniteComplete', 'document');
await ev.next();
}
describe('infinite-scroll: custom scroll target', () => {
it('should load more items when scrolled to the bottom', async () => {
const page = await newE2EPage({
url: '/src/components/infinite-scroll/test/scroll-target?ionic:_testing=true'
});
const initialItems = await page.findAll('ion-item');
expect(initialItems.length).toBe(30);
await scrollPage(page);
const items = await page.findAll('ion-item');
expect(items.length).toBe(60);
});
});

View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Infinite Scroll - Custom Scroll Target</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
#scroll-target {
height: 100%;
overflow-y: auto;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Infinite Scroll - Custom Scroll Target</ion-title>
</ion-toolbar>
</ion-header>
<ion-content scroll-y="false">
<div id="scroll-target" class="ion-padding ion-content-scroll-host">
<ion-list></ion-list>
<ion-infinite-scroll threshold="100px">
<ion-infinite-scroll-content loading-spinner="crescent" loading-text="Loading more data...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
</div>
</ion-content>
</ion-app>
<script>
let count = 0;
const list = document.querySelector('ion-list');
const infiniteScroll = document.querySelector('ion-infinite-scroll');
infiniteScroll.addEventListener('ionInfinite', async function () {
await wait(500);
infiniteScroll.complete();
appendItems();
// Custom event consumed in the e2e tests
document.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
});
function appendItems() {
for (var i = 0; i < 30; i++) {
const el = document.createElement('ion-item');
el.textContent = `${++count}`;
list.appendChild(el);
}
}
function wait(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
}
appendItems();
</script>
</body>
</html>