fix(angular): export RefresherPullEnd types (#30991)

Issue number: internal

---------

## What is the current behavior?
Currently, the 8.8 refresher events are not exported to Angular

## What is the new behavior?
With this change, we export the new events and also add Angular tests to
validate that they work and will continue working in the future.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No
This commit is contained in:
Shane
2026-03-05 15:56:32 -08:00
committed by GitHub
parent 0e76a69370
commit 72abccaad8
10 changed files with 133 additions and 6 deletions

View File

@@ -8046,7 +8046,7 @@ declare namespace LocalJSX {
*/
"onIonRefresh"?: (event: IonRefresherCustomEvent<RefresherEventDetail>) => void;
/**
* Emitted when the user begins to start pulling down. TODO(FW-7044): Remove this in a major release
* Emitted when the user begins to start pulling down.
* @deprecated Use `ionPullStart` instead.
*/
"onIonStart"?: (event: IonRefresherCustomEvent<void>) => void;

View File

@@ -141,9 +141,9 @@ export class Refresher implements ComponentInterface {
*/
@Event() ionPull!: EventEmitter<void>;
// TODO(FW-7044): Remove this in a major release
/**
* Emitted when the user begins to start pulling down.
* TODO(FW-7044): Remove this in a major release
*
* @deprecated Use `ionPullStart` instead.
*/

View File

@@ -1831,8 +1831,7 @@ called when the async operation has completed.
*/
ionPull: EventEmitter<CustomEvent<void>>;
/**
* Emitted when the user begins to start pulling down.
TODO(FW-7044): Remove this in a major release @deprecated Use `ionPullStart` instead.
* Emitted when the user begins to start pulling down. @deprecated Use `ionPullStart` instead.
*/
ionStart: EventEmitter<CustomEvent<void>>;
/**

View File

@@ -114,6 +114,8 @@ export {
RangeKnobMoveEndEventDetail,
RefresherCustomEvent,
RefresherEventDetail,
RefresherPullEndCustomEvent,
RefresherPullEndEventDetail,
ReorderMoveCustomEvent,
ReorderMoveEventDetail,
ReorderEndCustomEvent,

View File

@@ -1685,8 +1685,7 @@ called when the async operation has completed.
*/
ionPull: EventEmitter<CustomEvent<void>>;
/**
* Emitted when the user begins to start pulling down.
TODO(FW-7044): Remove this in a major release @deprecated Use `ionPullStart` instead.
* Emitted when the user begins to start pulling down. @deprecated Use `ionPullStart` instead.
*/
ionStart: EventEmitter<CustomEvent<void>>;
/**

View File

@@ -112,6 +112,8 @@ export {
RangeKnobMoveEndEventDetail,
RefresherCustomEvent,
RefresherEventDetail,
RefresherPullEndCustomEvent,
RefresherPullEndEventDetail,
ReorderMoveCustomEvent,
ReorderMoveEventDetail,
ReorderEndCustomEvent,

View File

@@ -0,0 +1,50 @@
import { expect, test } from '@playwright/test';
/**
* Simulates a pull-to-refresh gesture by dragging from the top of
* the content element downward with intermediate steps so the
* gesture recognizer detects the movement.
*/
const pullDown = async (page: import('@playwright/test').Page, distance: number) => {
const content = page.locator('ion-content');
const box = await content.boundingBox();
if (!box) throw new Error('ion-content not visible');
const startX = box.x + box.width / 2;
const startY = box.y + 30;
await page.mouse.move(startX, startY);
await page.mouse.down();
await page.mouse.move(startX, startY + distance, { steps: 20 });
await page.mouse.up();
};
test.describe('refresher: angular standalone', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/standalone/refresher');
});
test('should emit ionPullStart and ionPullEnd with cancel reason', async ({ page }) => {
// Small drag that doesn't reach the refresh threshold
await pullDown(page, 60);
await page.waitForTimeout(500);
await expect(page.locator('#pull-start-count')).toHaveText('1');
await expect(page.locator('#refresh-count')).toHaveText('0');
await expect(page.locator('#pull-end-count')).toHaveText('1');
await expect(page.locator('#pull-end-reason')).toHaveText('cancel');
});
test('should emit ionPullStart, ionRefresh, and ionPullEnd with complete reason', async ({ page }) => {
// Large drag past the refresh threshold
await pullDown(page, 300);
await page.waitForTimeout(1000);
await expect(page.locator('#pull-start-count')).toHaveText('1');
await expect(page.locator('#refresh-count')).toHaveText('1');
await expect(page.locator('#pull-end-count')).toHaveText('1');
await expect(page.locator('#pull-end-reason')).toHaveText('complete');
});
});

View File

@@ -32,6 +32,7 @@ export const routes: Routes = [
{ path: 'providers', loadComponent: () => import('../providers/providers.component').then(c => c.ProvidersComponent) },
{ path: 'overlay-controllers', loadComponent: () => import('../overlay-controllers/overlay-controllers.component').then(c => c.OverlayControllersComponent) },
{ path: 'button', loadComponent: () => import('../button/button.component').then(c => c.ButtonComponent) },
{ path: 'refresher', loadComponent: () => import('../refresher/refresher.component').then(c => c.RefresherComponent) },
{ path: 'reorder-group', loadComponent: () => import('../reorder-group/reorder-group.component').then(c => c.ReorderGroupComponent) },
{ path: 'icon', loadComponent: () => import('../icon/icon.component').then(c => c.IconComponent) },
{ path: 'split-pane', redirectTo: '/standalone/split-pane/inbox', pathMatch: 'full' },

View File

@@ -33,6 +33,11 @@
Inputs Test
</ion-label>
</ion-item>
<ion-item routerLink="/standalone/refresher">
<ion-label>
Refresher Test
</ion-label>
</ion-item>
<ion-item routerLink="/standalone/reorder-group">
<ion-label>
Reorder Group Test

View File

@@ -0,0 +1,69 @@
import { Component } from "@angular/core";
import { IonContent, IonHeader, IonItem, IonLabel, IonList, IonRefresher, IonRefresherContent, IonTitle, IonToolbar } from '@ionic/angular/standalone';
import { RefresherCustomEvent, RefresherPullEndCustomEvent } from "@ionic/angular";
@Component({
selector: 'app-refresher',
template: `
<ion-header>
<ion-toolbar>
<ion-title>Refresher Test</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-refresher
slot="fixed"
(ionPullStart)="onPullStart()"
(ionRefresh)="onRefresh($event)"
(ionPullEnd)="onPullEnd($event)"
>
<ion-refresher-content></ion-refresher-content>
</ion-refresher>
<ion-list>
<ion-item>
<ion-label>
ionPullStart count: <span id="pull-start-count">{{ pullStartCount }}</span>
</ion-label>
</ion-item>
<ion-item>
<ion-label>
ionRefresh count: <span id="refresh-count">{{ refreshCount }}</span>
</ion-label>
</ion-item>
<ion-item>
<ion-label>
ionPullEnd count: <span id="pull-end-count">{{ pullEndCount }}</span>
</ion-label>
</ion-item>
<ion-item>
<ion-label>
ionPullEnd reason: <span id="pull-end-reason">{{ pullEndReason }}</span>
</ion-label>
</ion-item>
</ion-list>
</ion-content>
`,
standalone: true,
imports: [IonContent, IonHeader, IonItem, IonLabel, IonList, IonRefresher, IonRefresherContent, IonTitle, IonToolbar]
})
export class RefresherComponent {
pullStartCount = 0;
refreshCount = 0;
pullEndCount = 0;
pullEndReason = '';
onPullStart() {
this.pullStartCount++;
}
onRefresh(event: RefresherCustomEvent) {
this.refreshCount++;
event.target.complete();
}
onPullEnd(event: RefresherPullEndCustomEvent) {
this.pullEndCount++;
this.pullEndReason = event.detail.reason;
}
}