From 7cb390450e53eb5d9e4e4de6551c84b0b0798e10 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:50:59 -0400 Subject: [PATCH 01/14] feat(reorder-group): add new reorder start, move, end events --- core/api.txt | 3 ++ core/src/components.d.ts | 24 +++++++-- .../reorder-group/reorder-group-interface.ts | 23 +++++++++ .../reorder-group/reorder-group.tsx | 50 +++++++++++++++++-- packages/angular/src/directives/proxies.ts | 27 ++++++++-- packages/angular/src/index.ts | 5 ++ .../standalone/src/directives/proxies.ts | 27 ++++++++-- packages/angular/standalone/src/index.ts | 5 ++ packages/react/src/components/index.ts | 5 ++ packages/vue/src/index.ts | 5 ++ packages/vue/src/proxies.ts | 10 +++- 11 files changed, 168 insertions(+), 16 deletions(-) diff --git a/core/api.txt b/core/api.txt index c792eb050a1..745d82786af 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1508,6 +1508,9 @@ ion-reorder-group,none ion-reorder-group,prop,disabled,boolean,true,false,false ion-reorder-group,method,complete,complete(listOrReorder?: boolean | any[]) => Promise ion-reorder-group,event,ionItemReorder,ItemReorderEventDetail,true +ion-reorder-group,event,ionReorderEnd,ReorderEndEventDetail,true +ion-reorder-group,event,ionReorderMove,ReorderMoveEventDetail,true +ion-reorder-group,event,ionReorderStart,void,true ion-ripple-effect,shadow ion-ripple-effect,prop,type,"bounded" | "unbounded",'bounded',false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 3fc70d62b02..eb1ac61f170 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -30,7 +30,7 @@ import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAct import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; import { RefresherEventDetail } from "./components/refresher/refresher-interface"; -import { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface"; +import { ItemReorderEventDetail, ReorderEndEventDetail, ReorderMoveEventDetail } from "./components/reorder-group/reorder-group-interface"; import { NavigationHookCallback } from "./components/route/route-interface"; import { SearchbarChangeEventDetail, SearchbarInputEventDetail } from "./components/searchbar/searchbar-interface"; import { SegmentChangeEventDetail, SegmentValue } from "./components/segment/segment-interface"; @@ -68,7 +68,7 @@ export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAct export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface"; export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface"; export { RefresherEventDetail } from "./components/refresher/refresher-interface"; -export { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface"; +export { ItemReorderEventDetail, ReorderEndEventDetail, ReorderMoveEventDetail } from "./components/reorder-group/reorder-group-interface"; export { NavigationHookCallback } from "./components/route/route-interface"; export { SearchbarChangeEventDetail, SearchbarInputEventDetail } from "./components/searchbar/searchbar-interface"; export { SegmentChangeEventDetail, SegmentValue } from "./components/segment/segment-interface"; @@ -2770,7 +2770,7 @@ export namespace Components { } interface IonReorderGroup { /** - * Completes the reorder operation. Must be called by the `ionItemReorder` event. If a list of items is passed, the list will be reordered and returned in the proper order. If no parameters are passed or if `true` is passed in, the reorder will complete and the item will remain in the position it was dragged to. If `false` is passed, the reorder will complete and the item will bounce back to its original position. + * Completes the reorder operation. Must be called by the `ionReorderEnd` event. If a list of items is passed, the list will be reordered and returned in the proper order. If no parameters are passed or if `true` is passed in, the reorder will complete and the item will remain in the position it was dragged to. If `false` is passed, the reorder will complete and the item will bounce back to its original position. * @param listOrReorder A list of items to be sorted and returned in the new order or a boolean of whether or not the reorder should reposition the item. */ "complete": (listOrReorder?: boolean | any[]) => Promise; @@ -4755,6 +4755,9 @@ declare global { }; interface HTMLIonReorderGroupElementEventMap { "ionItemReorder": ItemReorderEventDetail; + "ionReorderStart": void; + "ionReorderMove": ReorderMoveEventDetail; + "ionReorderEnd": ReorderEndEventDetail; } interface HTMLIonReorderGroupElement extends Components.IonReorderGroup, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLIonReorderGroupElement, ev: IonReorderGroupCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -8039,9 +8042,22 @@ declare namespace LocalJSX { */ "disabled"?: boolean; /** - * Event that needs to be listened to in order to complete the reorder action. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. + * TODO(FW-6590): Remove this in a major release. + * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to`, you should now add `undefined` checks as they can be `undefined` in `ionReorderEnd`. */ "onIonItemReorder"?: (event: IonReorderGroupCustomEvent) => void; + /** + * Event that is emitted when the reorder gesture ends. The from and to properties are only available if the reorder gesture moved the item. If the item did not move, the from and to properties will be undefined. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. + */ + "onIonReorderEnd"?: (event: IonReorderGroupCustomEvent) => void; + /** + * Event that is emitted as the reorder gesture moves. + */ + "onIonReorderMove"?: (event: IonReorderGroupCustomEvent) => void; + /** + * Event that is emitted when the reorder gesture starts. + */ + "onIonReorderStart"?: (event: IonReorderGroupCustomEvent) => void; } interface IonRippleEffect { /** diff --git a/core/src/components/reorder-group/reorder-group-interface.ts b/core/src/components/reorder-group/reorder-group-interface.ts index d76af54d6bd..08b66ed8ed2 100644 --- a/core/src/components/reorder-group/reorder-group-interface.ts +++ b/core/src/components/reorder-group/reorder-group-interface.ts @@ -1,10 +1,33 @@ +// TODO(FW-6590): Remove this once the deprecated event is removed export interface ItemReorderEventDetail { from: number; to: number; complete: (data?: boolean | any[]) => any; } +// TODO(FW-6590): Remove this once the deprecated event is removed export interface ItemReorderCustomEvent extends CustomEvent { detail: ItemReorderEventDetail; target: HTMLIonReorderGroupElement; } + +export interface ReorderMoveEventDetail { + from: number; + to: number; +} + +export interface ReorderEndEventDetail { + from?: number; + to?: number; + complete: (data?: boolean | any[]) => any; +} + +export interface ReorderMoveCustomEvent extends CustomEvent { + detail: ReorderMoveEventDetail; + target: HTMLIonReorderGroupElement; +} + +export interface ReorderEndCustomEvent extends CustomEvent { + detail: ReorderEndEventDetail; + target: HTMLIonReorderGroupElement; +} diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index 3dba0535b89..3de70726cb4 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -7,7 +7,7 @@ import { hapticSelectionChanged, hapticSelectionEnd, hapticSelectionStart } from import { getIonMode } from '../../global/ionic-global'; import type { Gesture, GestureDetail } from '../../interface'; -import type { ItemReorderEventDetail } from './reorder-group-interface'; +import type { ItemReorderEventDetail, ReorderMoveEventDetail, ReorderEndEventDetail } from './reorder-group-interface'; // TODO(FW-2832): types @@ -52,11 +52,34 @@ export class ReorderGroup implements ComponentInterface { } /** - * Event that needs to be listened to in order to complete the reorder action. + * TODO(FW-6590): Remove this in a major release. + * @deprecated Use `ionReorderEnd` instead. The new event is emitted + * at the end of every reorder gesture, even if the positions do not + * change. If you were accessing `event.detail.from` or `event.detail.to`, + * you should now add `undefined` checks as they can be `undefined` in + * `ionReorderEnd`. + */ + @Event() ionItemReorder!: EventEmitter; + + /** + * Event that is emitted when the reorder gesture starts. + */ + @Event() ionReorderStart!: EventEmitter; + + /** + * Event that is emitted as the reorder gesture moves. + */ + @Event() ionReorderMove!: EventEmitter; + + /** + * Event that is emitted when the reorder gesture ends. + * The from and to properties are only available if the reorder gesture + * moved the item. If the item did not move, the from and to properties + * will be undefined. * Once the event has been emitted, the `complete()` method then needs * to be called in order to finalize the reorder action. */ - @Event() ionItemReorder!: EventEmitter; + @Event() ionReorderEnd!: EventEmitter; async connectedCallback() { const contentEl = findClosestIonContent(this.el); @@ -88,7 +111,7 @@ export class ReorderGroup implements ComponentInterface { } /** - * Completes the reorder operation. Must be called by the `ionItemReorder` event. + * Completes the reorder operation. Must be called by the `ionReorderEnd` event. * * If a list of items is passed, the list will be reordered and returned in the * proper order. @@ -164,6 +187,8 @@ export class ReorderGroup implements ComponentInterface { item.classList.add(ITEM_REORDER_SELECTED); hapticSelectionStart(); + + this.ionReorderStart.emit(); } private onMove(ev: GestureDetail) { @@ -180,6 +205,7 @@ export class ReorderGroup implements ComponentInterface { const currentY = Math.max(top, Math.min(ev.currentY, bottom)); const deltaY = scroll + currentY - ev.startY; const normalizedY = currentY - top; + const fromIndex = this.lastToIndex; const toIndex = this.itemIndexForTop(normalizedY); if (toIndex !== this.lastToIndex) { const fromIndex = indexForItem(selectedItem); @@ -191,6 +217,11 @@ export class ReorderGroup implements ComponentInterface { // Update selected item position selectedItem.style.transform = `translateY(${deltaY}px)`; + + this.ionReorderMove.emit({ + from: fromIndex, + to: toIndex, + }); } private onEnd() { @@ -205,13 +236,24 @@ export class ReorderGroup implements ComponentInterface { const fromIndex = indexForItem(selectedItemEl); if (toIndex === fromIndex) { + this.ionReorderEnd.emit({ + complete: this.completeReorder.bind(this), + }); + this.completeReorder(); } else { + // TODO(FW-6590): Remove this once the deprecated event is removed this.ionItemReorder.emit({ from: fromIndex, to: toIndex, complete: this.completeReorder.bind(this), }); + + this.ionReorderEnd.emit({ + from: fromIndex, + to: toIndex, + complete: this.completeReorder.bind(this), + }); } hapticSelectionEnd(); diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 12b3b7ba3b2..eec38efe49d 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1895,20 +1895,41 @@ export class IonReorderGroup { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; - proxyOutputs(this, this.el, ['ionItemReorder']); + proxyOutputs(this, this.el, ['ionItemReorder', 'ionReorderStart', 'ionReorderMove', 'ionReorderEnd']); } } import type { ItemReorderEventDetail as IIonReorderGroupItemReorderEventDetail } from '@ionic/core'; +import type { ReorderMoveEventDetail as IIonReorderGroupReorderMoveEventDetail } from '@ionic/core'; +import type { ReorderEndEventDetail as IIonReorderGroupReorderEndEventDetail } from '@ionic/core'; export declare interface IonReorderGroup extends Components.IonReorderGroup { /** - * Event that needs to be listened to in order to complete the reorder action. + * TODO(FW-6590): Remove this in a major release. @deprecated Use `ionReorderEnd` instead. The new event is emitted +at the end of every reorder gesture, even if the positions do not +change. If you were accessing `event.detail.from` or `event.detail.to`, +you should now add `undefined` checks as they can be `undefined` in +`ionReorderEnd`. + */ + ionItemReorder: EventEmitter>; + /** + * Event that is emitted when the reorder gesture starts. + */ + ionReorderStart: EventEmitter>; + /** + * Event that is emitted as the reorder gesture moves. + */ + ionReorderMove: EventEmitter>; + /** + * Event that is emitted when the reorder gesture ends. +The from and to properties are only available if the reorder gesture +moved the item. If the item did not move, the from and to properties +will be undefined. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. */ - ionItemReorder: EventEmitter>; + ionReorderEnd: EventEmitter>; } diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index cd2d82ea4b0..3ee4a74ee1e 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -90,6 +90,7 @@ export { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, + // TODO(FW-6590): Remove the next two lines once the deprecated event is removed ItemReorderEventDetail, ItemReorderCustomEvent, ItemSlidingCustomEvent, @@ -112,6 +113,10 @@ export { RangeKnobMoveEndEventDetail, RefresherCustomEvent, RefresherEventDetail, + ReorderMoveCustomEvent, + ReorderMoveEventDetail, + ReorderEndCustomEvent, + ReorderEndEventDetail, RouterEventDetail, RouterCustomEvent, ScrollBaseCustomEvent, diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index c7d111f7bef..1038861bcde 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1755,20 +1755,41 @@ export class IonReorderGroup { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; - proxyOutputs(this, this.el, ['ionItemReorder']); + proxyOutputs(this, this.el, ['ionItemReorder', 'ionReorderStart', 'ionReorderMove', 'ionReorderEnd']); } } import type { ItemReorderEventDetail as IIonReorderGroupItemReorderEventDetail } from '@ionic/core/components'; +import type { ReorderMoveEventDetail as IIonReorderGroupReorderMoveEventDetail } from '@ionic/core/components'; +import type { ReorderEndEventDetail as IIonReorderGroupReorderEndEventDetail } from '@ionic/core/components'; export declare interface IonReorderGroup extends Components.IonReorderGroup { /** - * Event that needs to be listened to in order to complete the reorder action. + * TODO(FW-6590): Remove this in a major release. @deprecated Use `ionReorderEnd` instead. The new event is emitted +at the end of every reorder gesture, even if the positions do not +change. If you were accessing `event.detail.from` or `event.detail.to`, +you should now add `undefined` checks as they can be `undefined` in +`ionReorderEnd`. + */ + ionItemReorder: EventEmitter>; + /** + * Event that is emitted when the reorder gesture starts. + */ + ionReorderStart: EventEmitter>; + /** + * Event that is emitted as the reorder gesture moves. + */ + ionReorderMove: EventEmitter>; + /** + * Event that is emitted when the reorder gesture ends. +The from and to properties are only available if the reorder gesture +moved the item. If the item did not move, the from and to properties +will be undefined. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. */ - ionItemReorder: EventEmitter>; + ionReorderEnd: EventEmitter>; } diff --git a/packages/angular/standalone/src/index.ts b/packages/angular/standalone/src/index.ts index 23debccc1cd..db9a8a57da7 100644 --- a/packages/angular/standalone/src/index.ts +++ b/packages/angular/standalone/src/index.ts @@ -88,6 +88,7 @@ export { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, + // TODO(FW-6590): Remove the next two lines once the deprecated event is removed ItemReorderEventDetail, ItemReorderCustomEvent, ItemSlidingCustomEvent, @@ -110,6 +111,10 @@ export { RangeKnobMoveEndEventDetail, RefresherCustomEvent, RefresherEventDetail, + ReorderMoveCustomEvent, + ReorderMoveEventDetail, + ReorderEndCustomEvent, + ReorderEndEventDetail, RouterEventDetail, RouterCustomEvent, ScrollBaseCustomEvent, diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 417b826866a..5355401a797 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -47,6 +47,7 @@ export { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, + // TODO(FW-6590): Remove the next two lines once the deprecated event is removed ItemReorderEventDetail, ItemReorderCustomEvent, ItemSlidingCustomEvent, @@ -68,6 +69,10 @@ export { RangeKnobMoveEndEventDetail, RefresherCustomEvent, RefresherEventDetail, + ReorderMoveCustomEvent, + ReorderMoveEventDetail, + ReorderEndCustomEvent, + ReorderEndEventDetail, RouterEventDetail, RouterCustomEvent, ScrollBaseCustomEvent, diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 0a6aac15970..b189d4d9370 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -84,6 +84,7 @@ export { InputOtpChangeEventDetail, InputOtpCompleteEventDetail, InputOtpInputEventDetail, + // TODO(FW-6590): Remove the next two lines once the deprecated event is removed ItemReorderEventDetail, ItemReorderCustomEvent, ItemSlidingCustomEvent, @@ -107,6 +108,10 @@ export { RangeKnobMoveEndEventDetail, RefresherCustomEvent, RefresherEventDetail, + ReorderMoveCustomEvent, + ReorderMoveEventDetail, + ReorderEndCustomEvent, + ReorderEndEventDetail, RouterEventDetail, RouterCustomEvent, ScrollBaseCustomEvent, diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 0db034f746d..a0c23232908 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -804,9 +804,15 @@ export const IonReorder: StencilVueComponent = /*@__PURE__*/ def export const IonReorderGroup: StencilVueComponent = /*@__PURE__*/ defineContainer('ion-reorder-group', defineIonReorderGroup, [ 'disabled', - 'ionItemReorder' + 'ionItemReorder', + 'ionReorderStart', + 'ionReorderMove', + 'ionReorderEnd' ], [ - 'ionItemReorder' + 'ionItemReorder', + 'ionReorderStart', + 'ionReorderMove', + 'ionReorderEnd' ]); From 1499525cd629156c843b480fb91985c4020d7820 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:52:17 -0400 Subject: [PATCH 02/14] test(reorder-group): change all usage of ionItemReorder to ionReorderEnd --- .../components/item/test/reorder/index.html | 2 +- .../reorder-group/test/basic/index.html | 19 ++++++++++++++++++- .../reorder-group/test/data/index.html | 2 +- .../reorder-group/test/interactive/index.html | 4 ++-- .../test/interactive/reorder-group.e2e.ts | 8 ++++---- .../reorder-group/test/nested/index.html | 4 ++-- .../test/nested/reorder-group.e2e.ts | 8 ++++---- .../test/scroll-target/index.html | 4 ++-- .../test/scroll-target/reorder-group.e2e.ts | 8 ++++---- 9 files changed, 38 insertions(+), 21 deletions(-) diff --git a/core/src/components/item/test/reorder/index.html b/core/src/components/item/test/reorder/index.html index 22228cfd10a..64a9bf06042 100644 --- a/core/src/components/item/test/reorder/index.html +++ b/core/src/components/item/test/reorder/index.html @@ -84,7 +84,7 @@ } function initGroup(group) { var groupEl = document.getElementById(group.id); - groupEl.addEventListener('ionItemReorder', function (ev) { + groupEl.addEventListener('ionReorderEnd', function (ev) { ev.detail.complete(); }); var groupItems = []; diff --git a/core/src/components/reorder-group/test/basic/index.html b/core/src/components/reorder-group/test/basic/index.html index 55e034c82ab..a916567dfc1 100644 --- a/core/src/components/reorder-group/test/basic/index.html +++ b/core/src/components/reorder-group/test/basic/index.html @@ -122,8 +122,25 @@ const reorderGroup = document.getElementById('reorder'); reorderGroup.disabled = !reorderGroup.disabled; + // TODO(FW-6590): Remove this once the deprecated event is removed reorderGroup.addEventListener('ionItemReorder', ({ detail }) => { - console.log('Dragged from index', detail.from, 'to', detail.to); + console.log('ionItemReorder: Dragged from index', detail.from, 'to', detail.to); + }); + + reorderGroup.addEventListener('ionReorderStart', () => { + console.log('ionReorderStart'); + }); + + reorderGroup.addEventListener('ionReorderMove', ({ detail }) => { + console.log('ionReorderMove: Dragged from index', detail.from, 'to', detail.to); + }); + + reorderGroup.addEventListener('ionReorderEnd', ({ detail }) => { + if (detail.from !== undefined && detail.to !== undefined) { + console.log('ionReorderEnd: Dragged from index', detail.from, 'to', detail.to); + } else { + console.log('ionReorderEnd: No position change occurred'); + } detail.complete(); }); diff --git a/core/src/components/reorder-group/test/data/index.html b/core/src/components/reorder-group/test/data/index.html index e30aa583ae6..bffa2c5fe82 100644 --- a/core/src/components/reorder-group/test/data/index.html +++ b/core/src/components/reorder-group/test/data/index.html @@ -50,7 +50,7 @@ reorderGroup.innerHTML = html; } - reorderGroup.addEventListener('ionItemReorder', ({ detail }) => { + reorderGroup.addEventListener('ionReorderEnd', ({ detail }) => { console.log('Dragged from index', detail.from, 'to', detail.to); console.log('Before complete', items); diff --git a/core/src/components/reorder-group/test/interactive/index.html b/core/src/components/reorder-group/test/interactive/index.html index 79150979ff3..b213b0a1b49 100644 --- a/core/src/components/reorder-group/test/interactive/index.html +++ b/core/src/components/reorder-group/test/interactive/index.html @@ -37,9 +37,9 @@ diff --git a/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts b/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts index c443275e43b..c303c6c169e 100644 --- a/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts +++ b/core/src/components/reorder-group/test/interactive/reorder-group.e2e.ts @@ -11,24 +11,24 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { }); test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => { const items = page.locator('ion-item'); - const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete'); + const ionReorderComplete = await page.spyOnEvent('ionReorderComplete'); await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']); await dragElementBy(items.nth(1), page, 0, 300); - await ionItemReorderComplete.next(); + await ionReorderComplete.next(); await expect(items).toContainText(['Item 1', 'Item 3', 'Item 4', 'Item 2']); }); test('should drag and drop when ion-item wraps ion-reorder', async ({ page }) => { const reorderHandle = page.locator('ion-reorder'); const items = page.locator('ion-item'); - const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete'); + const ionReorderComplete = await page.spyOnEvent('ionReorderComplete'); await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']); await dragElementBy(reorderHandle.nth(0), page, 0, 300); - await ionItemReorderComplete.next(); + await ionReorderComplete.next(); await expect(items).toContainText(['Item 2', 'Item 3', 'Item 4', 'Item 1']); }); diff --git a/core/src/components/reorder-group/test/nested/index.html b/core/src/components/reorder-group/test/nested/index.html index 0f425dc8812..7679dc38276 100644 --- a/core/src/components/reorder-group/test/nested/index.html +++ b/core/src/components/reorder-group/test/nested/index.html @@ -68,9 +68,9 @@ customElements.define('app-reorder', AppReorder); const group = document.querySelector('ion-reorder-group'); - group.addEventListener('ionItemReorder', (ev) => { + group.addEventListener('ionReorderEnd', (ev) => { ev.detail.complete(); - window.dispatchEvent(new CustomEvent('ionItemReorderComplete')); + window.dispatchEvent(new CustomEvent('ionReorderComplete')); }); diff --git a/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts b/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts index a86de620cee..8c7377bf4d7 100644 --- a/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts +++ b/core/src/components/reorder-group/test/nested/reorder-group.e2e.ts @@ -11,24 +11,24 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { }); test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => { const items = page.locator('app-reorder'); - const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete'); + const ionReorderComplete = await page.spyOnEvent('ionReorderComplete'); await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']); await dragElementBy(items.nth(1), page, 0, 300); - await ionItemReorderComplete.next(); + await ionReorderComplete.next(); await expect(items).toContainText(['Item 1', 'Item 3', 'Item 4', 'Item 2']); }); test('should drag and drop when ion-item wraps ion-reorder', async ({ page }) => { const reorderHandle = page.locator('app-reorder ion-reorder'); const items = page.locator('app-reorder'); - const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete'); + const ionReorderComplete = await page.spyOnEvent('ionReorderComplete'); await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']); await dragElementBy(reorderHandle.nth(0), page, 0, 300); - await ionItemReorderComplete.next(); + await ionReorderComplete.next(); await expect(items).toContainText(['Item 2', 'Item 3', 'Item 4', 'Item 1']); }); diff --git a/core/src/components/reorder-group/test/scroll-target/index.html b/core/src/components/reorder-group/test/scroll-target/index.html index e286dba3bec..eb147b0053e 100644 --- a/core/src/components/reorder-group/test/scroll-target/index.html +++ b/core/src/components/reorder-group/test/scroll-target/index.html @@ -57,9 +57,9 @@ diff --git a/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts b/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts index 12a76299b53..0711c6022a9 100644 --- a/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts +++ b/core/src/components/reorder-group/test/scroll-target/reorder-group.e2e.ts @@ -11,24 +11,24 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { }); test('should drag and drop when ion-reorder wraps ion-item', async ({ page }) => { const items = page.locator('ion-item'); - const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete'); + const ionReorderComplete = await page.spyOnEvent('ionReorderComplete'); await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']); await dragElementBy(items.nth(1), page, 0, 300); - await ionItemReorderComplete.next(); + await ionReorderComplete.next(); await expect(items).toContainText(['Item 1', 'Item 3', 'Item 4', 'Item 2']); }); test('should drag and drop when ion-item wraps ion-reorder', async ({ page }) => { const reorderHandle = page.locator('ion-reorder'); const items = page.locator('ion-item'); - const ionItemReorderComplete = await page.spyOnEvent('ionItemReorderComplete'); + const ionReorderComplete = await page.spyOnEvent('ionReorderComplete'); await expect(items).toContainText(['Item 1', 'Item 2', 'Item 3', 'Item 4']); await dragElementBy(reorderHandle.nth(0), page, 0, 300); - await ionItemReorderComplete.next(); + await ionReorderComplete.next(); await expect(items).toContainText(['Item 2', 'Item 3', 'Item 4', 'Item 1']); }); From 6d887b9e875ab25688727867f0360cb16c8820eb Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:53:18 -0400 Subject: [PATCH 03/14] test(reorder-group): add an e2e test that covers all reorder events --- .../test/reorder-group-events.e2e.ts | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 core/src/components/reorder-group/test/reorder-group-events.e2e.ts diff --git a/core/src/components/reorder-group/test/reorder-group-events.e2e.ts b/core/src/components/reorder-group/test/reorder-group-events.e2e.ts new file mode 100644 index 00000000000..294aa0dd411 --- /dev/null +++ b/core/src/components/reorder-group/test/reorder-group-events.e2e.ts @@ -0,0 +1,289 @@ +import { expect } from '@playwright/test'; +import { configs, dragElementBy, test } from '@utils/test/playwright'; + +/** + * This behavior does not vary across modes/directions. + */ +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('reorder-group: events:'), () => { + test.describe('ionReorderStart', () => { + test('should emit when the reorder operation starts', async ({ page }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionReorderStart = await page.spyOnEvent('ionReorderStart'); + + await expect(ionReorderStart).toHaveReceivedEventTimes(0); + + // Start the drag to verify it emits the event without having to + // actually move the item. Do not release the drag here. + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 0, undefined, undefined, false); + + await page.waitForChanges(); + + await expect(ionReorderStart).toHaveReceivedEventTimes(1); + + // Drag the reorder item further to verify it does + // not emit the event again + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 300); + + await page.waitForChanges(); + + await expect(ionReorderStart).toHaveReceivedEventTimes(1); + }); + }); + + test.describe('ionReorderMove', () => { + test('should emit when the reorder operation does not move the item position', async ({ page }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionReorderMove = await page.spyOnEvent('ionReorderMove'); + + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 0); + + await page.waitForChanges(); + + expect(ionReorderMove.events.length).toBeGreaterThan(0); + + // Grab the last event to verify that it is emitting + // the correct from and to positions + const lastEvent = ionReorderMove.events[ionReorderMove.events.length - 1]; + expect(lastEvent?.detail.from).toBe(0); + expect(lastEvent?.detail.to).toBe(0); + }); + + test('should emit when the reorder operation moves the item by multiple positions', async ({ page }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionReorderMove = await page.spyOnEvent('ionReorderMove'); + + // Drag the reorder item by a lot to verify it emits the event + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 300); + + await page.waitForChanges(); + + expect(ionReorderMove.events.length).toBeGreaterThan(0); + + // Grab the last event where the from and to are different to + // verify that it is not using the gesture start position as the from + const lastDifferentEvent = ionReorderMove.events + .reverse() + .find((event) => event.detail.from !== event.detail.to); + expect(lastDifferentEvent?.detail.from).toBe(1); + expect(lastDifferentEvent?.detail.to).toBe(2); + }); + }); + + test.describe('ionReorderEnd', () => { + test('should emit without details when the reorder operation ends without moving the item position', async ({ + page, + }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionReorderEnd = await page.spyOnEvent('ionReorderEnd'); + + // Drag the reorder item a little bit but not enough to + // make it switch to a different position + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 20); + + await page.waitForChanges(); + + await expect(ionReorderEnd).toHaveReceivedEventTimes(1); + await expect(ionReorderEnd).toHaveReceivedEventDetail({ complete: undefined }); + }); + + test('should emit with details when the reorder operation ends and the item has moved', async ({ page }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionReorderEnd = await page.spyOnEvent('ionReorderEnd'); + + // Start the drag to verify it does not emit the event at the start + // of the drag or during the drag. Do not release the drag here. + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 100, undefined, undefined, false); + + await page.waitForChanges(); + + await expect(ionReorderEnd).toHaveReceivedEventTimes(0); + + // Drag the reorder item further and release the drag to verify it emits the event + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 300); + + await page.waitForChanges(); + + await expect(ionReorderEnd).toHaveReceivedEventTimes(1); + await expect(ionReorderEnd).toHaveReceivedEventDetail({ from: 0, to: 2, complete: undefined }); + }); + }); + + // TODO(FW-6590): Remove this once the deprecated event is removed + test.describe('ionItemReorder', () => { + test('should not emit when the reorder operation ends without moving the item position', async ({ page }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionItemReorder = await page.spyOnEvent('ionItemReorder'); + + // Drag the reorder item a little bit but not enough to + // make it switch to a different position + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 20); + + await page.waitForChanges(); + + await expect(ionItemReorder).toHaveReceivedEventTimes(0); + }); + + test('should emit when the reorder operation ends and the item has moved', async ({ page }) => { + await page.setContent( + ` + + + Item 1 + + + + Item 2 + + + + Item 3 + + + + `, + config + ); + + const reorderGroup = page.locator('ion-reorder-group'); + const ionItemReorder = await page.spyOnEvent('ionItemReorder'); + + // Start the drag to verify it does not emit the event at the start + // of the drag or during the drag. Do not release the drag here. + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 100, undefined, undefined, false); + + await page.waitForChanges(); + + await expect(ionItemReorder).toHaveReceivedEventTimes(0); + + // Drag the reorder item further and release the drag to verify it emits the event + await dragElementBy(reorderGroup.locator('ion-reorder').first(), page, 0, 300); + + await page.waitForChanges(); + + await expect(ionItemReorder).toHaveReceivedEventTimes(1); + await expect(ionItemReorder).toHaveReceivedEventDetail({ from: 0, to: 2, complete: undefined }); + }); + }); + }); +}); From 861124ead46df5f1f584fe1464cfa932ff119aab Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 10 Jun 2025 17:36:41 -0400 Subject: [PATCH 04/14] chore: export interfaces --- core/src/interface.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/interface.d.ts b/core/src/interface.d.ts index 3dcfb6aec2f..3f30243a093 100644 --- a/core/src/interface.d.ts +++ b/core/src/interface.d.ts @@ -25,7 +25,7 @@ export { RadioGroupCustomEvent } from './components/radio-group/radio-group-inte export { RangeCustomEvent, PinFormatter } from './components/range/range-interface'; export { HTMLStencilElement, RouterCustomEvent } from './components/router/utils/interface'; export { RefresherCustomEvent } from './components/refresher/refresher-interface'; -export { ItemReorderCustomEvent } from './components/reorder-group/reorder-group-interface'; +export { ItemReorderCustomEvent, ReorderEndCustomEvent, ReorderMoveCustomEvent } from './components/reorder-group/reorder-group-interface'; export { SearchbarCustomEvent } from './components/searchbar/searchbar-interface'; export { SegmentCustomEvent } from './components/segment/segment-interface'; export { SelectCustomEvent, SelectCompareFn } from './components/select/select-interface'; From 69ce85392243e153e7a416ac56858ccb9543f1df Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:18:57 -0400 Subject: [PATCH 05/14] style: lint --- core/src/interface.d.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/interface.d.ts b/core/src/interface.d.ts index 3f30243a093..878d6c08467 100644 --- a/core/src/interface.d.ts +++ b/core/src/interface.d.ts @@ -25,7 +25,11 @@ export { RadioGroupCustomEvent } from './components/radio-group/radio-group-inte export { RangeCustomEvent, PinFormatter } from './components/range/range-interface'; export { HTMLStencilElement, RouterCustomEvent } from './components/router/utils/interface'; export { RefresherCustomEvent } from './components/refresher/refresher-interface'; -export { ItemReorderCustomEvent, ReorderEndCustomEvent, ReorderMoveCustomEvent } from './components/reorder-group/reorder-group-interface'; +export { + ItemReorderCustomEvent, + ReorderEndCustomEvent, + ReorderMoveCustomEvent, +} from './components/reorder-group/reorder-group-interface'; export { SearchbarCustomEvent } from './components/searchbar/searchbar-interface'; export { SegmentCustomEvent } from './components/segment/segment-interface'; export { SelectCustomEvent, SelectCompareFn } from './components/select/select-interface'; From 7bd60879163592f70fe818586f71c8a7fcf4692d Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:18:00 -0400 Subject: [PATCH 06/14] fix(reorder-group): always emit from and to in ionReorderEnd --- core/core | 0 core/src/components.d.ts | 4 +-- .../reorder-group/reorder-group-interface.ts | 4 +-- .../reorder-group/reorder-group.tsx | 28 ++++++++----------- .../test/reorder-group-events.e2e.ts | 2 +- packages/angular/src/directives/proxies.ts | 12 ++++---- .../standalone/src/directives/proxies.ts | 12 ++++---- 7 files changed, 29 insertions(+), 33 deletions(-) create mode 100644 core/core diff --git a/core/core b/core/core new file mode 100644 index 00000000000..e69de29bb2d diff --git a/core/src/components.d.ts b/core/src/components.d.ts index eb1ac61f170..8c755886d37 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -8043,11 +8043,11 @@ declare namespace LocalJSX { "disabled"?: boolean; /** * TODO(FW-6590): Remove this in a major release. - * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to`, you should now add `undefined` checks as they can be `undefined` in `ionReorderEnd`. + * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to` before and relying on them being different you should now add checks as they are always emitted in `ionReorderEnd`, even when they are the same. */ "onIonItemReorder"?: (event: IonReorderGroupCustomEvent) => void; /** - * Event that is emitted when the reorder gesture ends. The from and to properties are only available if the reorder gesture moved the item. If the item did not move, the from and to properties will be undefined. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. + * Event that is emitted when the reorder gesture ends. The from and to properties are always available, regardless of if the reorder gesture moved the item. If the item did not change from its start position, the from and to properties will be the same. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. */ "onIonReorderEnd"?: (event: IonReorderGroupCustomEvent) => void; /** diff --git a/core/src/components/reorder-group/reorder-group-interface.ts b/core/src/components/reorder-group/reorder-group-interface.ts index 08b66ed8ed2..b400413d15e 100644 --- a/core/src/components/reorder-group/reorder-group-interface.ts +++ b/core/src/components/reorder-group/reorder-group-interface.ts @@ -17,8 +17,8 @@ export interface ReorderMoveEventDetail { } export interface ReorderEndEventDetail { - from?: number; - to?: number; + from: number; + to: number; complete: (data?: boolean | any[]) => any; } diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index 3de70726cb4..f649b477c6d 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -55,9 +55,9 @@ export class ReorderGroup implements ComponentInterface { * TODO(FW-6590): Remove this in a major release. * @deprecated Use `ionReorderEnd` instead. The new event is emitted * at the end of every reorder gesture, even if the positions do not - * change. If you were accessing `event.detail.from` or `event.detail.to`, - * you should now add `undefined` checks as they can be `undefined` in - * `ionReorderEnd`. + * change. If you were accessing `event.detail.from` or `event.detail.to` + * before and relying on them being different you should now add checks as + * they are always emitted in `ionReorderEnd`, even when they are the same. */ @Event() ionItemReorder!: EventEmitter; @@ -73,9 +73,9 @@ export class ReorderGroup implements ComponentInterface { /** * Event that is emitted when the reorder gesture ends. - * The from and to properties are only available if the reorder gesture - * moved the item. If the item did not move, the from and to properties - * will be undefined. + * The from and to properties are always available, regardless of + * if the reorder gesture moved the item. If the item did not change + * from its start position, the from and to properties will be the same. * Once the event has been emitted, the `complete()` method then needs * to be called in order to finalize the reorder action. */ @@ -236,10 +236,6 @@ export class ReorderGroup implements ComponentInterface { const fromIndex = indexForItem(selectedItemEl); if (toIndex === fromIndex) { - this.ionReorderEnd.emit({ - complete: this.completeReorder.bind(this), - }); - this.completeReorder(); } else { // TODO(FW-6590): Remove this once the deprecated event is removed @@ -248,14 +244,14 @@ export class ReorderGroup implements ComponentInterface { to: toIndex, complete: this.completeReorder.bind(this), }); - - this.ionReorderEnd.emit({ - from: fromIndex, - to: toIndex, - complete: this.completeReorder.bind(this), - }); } + this.ionReorderEnd.emit({ + from: fromIndex, + to: toIndex, + complete: this.completeReorder.bind(this), + }); + hapticSelectionEnd(); } diff --git a/core/src/components/reorder-group/test/reorder-group-events.e2e.ts b/core/src/components/reorder-group/test/reorder-group-events.e2e.ts index 294aa0dd411..d3324e7dbdf 100644 --- a/core/src/components/reorder-group/test/reorder-group-events.e2e.ts +++ b/core/src/components/reorder-group/test/reorder-group-events.e2e.ts @@ -164,7 +164,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await page.waitForChanges(); await expect(ionReorderEnd).toHaveReceivedEventTimes(1); - await expect(ionReorderEnd).toHaveReceivedEventDetail({ complete: undefined }); + await expect(ionReorderEnd).toHaveReceivedEventDetail({ from: 0, to: 0, complete: undefined }); }); test('should emit with details when the reorder operation ends and the item has moved', async ({ page }) => { diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index eec38efe49d..3e5d7019f9c 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1908,9 +1908,9 @@ export declare interface IonReorderGroup extends Components.IonReorderGroup { /** * TODO(FW-6590): Remove this in a major release. @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not -change. If you were accessing `event.detail.from` or `event.detail.to`, -you should now add `undefined` checks as they can be `undefined` in -`ionReorderEnd`. +change. If you were accessing `event.detail.from` or `event.detail.to` +before and relying on them being different you should now add checks as +they are always emitted in `ionReorderEnd`, even when they are the same. */ ionItemReorder: EventEmitter>; /** @@ -1923,9 +1923,9 @@ you should now add `undefined` checks as they can be `undefined` in ionReorderMove: EventEmitter>; /** * Event that is emitted when the reorder gesture ends. -The from and to properties are only available if the reorder gesture -moved the item. If the item did not move, the from and to properties -will be undefined. +The from and to properties are always available, regardless of +if the reorder gesture moved the item. If the item did not change +from its start position, the from and to properties will be the same. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. */ diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 1038861bcde..780f99d7824 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1768,9 +1768,9 @@ export declare interface IonReorderGroup extends Components.IonReorderGroup { /** * TODO(FW-6590): Remove this in a major release. @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not -change. If you were accessing `event.detail.from` or `event.detail.to`, -you should now add `undefined` checks as they can be `undefined` in -`ionReorderEnd`. +change. If you were accessing `event.detail.from` or `event.detail.to` +before and relying on them being different you should now add checks as +they are always emitted in `ionReorderEnd`, even when they are the same. */ ionItemReorder: EventEmitter>; /** @@ -1783,9 +1783,9 @@ you should now add `undefined` checks as they can be `undefined` in ionReorderMove: EventEmitter>; /** * Event that is emitted when the reorder gesture ends. -The from and to properties are only available if the reorder gesture -moved the item. If the item did not move, the from and to properties -will be undefined. +The from and to properties are always available, regardless of +if the reorder gesture moved the item. If the item did not change +from its start position, the from and to properties will be the same. Once the event has been emitted, the `complete()` method then needs to be called in order to finalize the reorder action. */ From e76f8bd8b338672423834886bd7a1e08c2d92434 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:34:31 -0400 Subject: [PATCH 07/14] style(): update test logs and add TODO --- core/src/components/reorder-group/reorder-group.tsx | 4 ++++ core/src/components/reorder-group/test/basic/index.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index f649b477c6d..f2c03e87033 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -236,6 +236,10 @@ export class ReorderGroup implements ComponentInterface { const fromIndex = indexForItem(selectedItemEl); if (toIndex === fromIndex) { + // TODO(FW-6590): Remove this once the deprecated event is removed + // Since the ionReorderEnd event is emitted at the end of every reorder + // gesture, even if the item did not move, the user can always call + // complete() to reset the state of the reorder group. this.completeReorder(); } else { // TODO(FW-6590): Remove this once the deprecated event is removed diff --git a/core/src/components/reorder-group/test/basic/index.html b/core/src/components/reorder-group/test/basic/index.html index a916567dfc1..84af0ed5120 100644 --- a/core/src/components/reorder-group/test/basic/index.html +++ b/core/src/components/reorder-group/test/basic/index.html @@ -136,7 +136,7 @@ }); reorderGroup.addEventListener('ionReorderEnd', ({ detail }) => { - if (detail.from !== undefined && detail.to !== undefined) { + if (detail.from !== detail.to) { console.log('ionReorderEnd: Dragged from index', detail.from, 'to', detail.to); } else { console.log('ionReorderEnd: No position change occurred'); From 570b40e98e7cd524fb82beff2ec68de02e694b9d Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:45:18 -0400 Subject: [PATCH 08/14] style: remove the TODO from the docs description --- core/src/components.d.ts | 1 - core/src/components/reorder-group/reorder-group.tsx | 2 +- packages/angular/src/directives/proxies.ts | 2 +- packages/angular/standalone/src/directives/proxies.ts | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 8c755886d37..87fa54a9404 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -8042,7 +8042,6 @@ declare namespace LocalJSX { */ "disabled"?: boolean; /** - * TODO(FW-6590): Remove this in a major release. * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to` before and relying on them being different you should now add checks as they are always emitted in `ionReorderEnd`, even when they are the same. */ "onIonItemReorder"?: (event: IonReorderGroupCustomEvent) => void; diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index f2c03e87033..efed191c7ef 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -51,8 +51,8 @@ export class ReorderGroup implements ComponentInterface { } } + // TODO(FW-6590): Remove this in a major release. /** - * TODO(FW-6590): Remove this in a major release. * @deprecated Use `ionReorderEnd` instead. The new event is emitted * at the end of every reorder gesture, even if the positions do not * change. If you were accessing `event.detail.from` or `event.detail.to` diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 3e5d7019f9c..b1ee6771c6c 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1906,7 +1906,7 @@ import type { ReorderEndEventDetail as IIonReorderGroupReorderEndEventDetail } f export declare interface IonReorderGroup extends Components.IonReorderGroup { /** - * TODO(FW-6590): Remove this in a major release. @deprecated Use `ionReorderEnd` instead. The new event is emitted + * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to` before and relying on them being different you should now add checks as diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 780f99d7824..77e2b22b4d9 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1766,7 +1766,7 @@ import type { ReorderEndEventDetail as IIonReorderGroupReorderEndEventDetail } f export declare interface IonReorderGroup extends Components.IonReorderGroup { /** - * TODO(FW-6590): Remove this in a major release. @deprecated Use `ionReorderEnd` instead. The new event is emitted + * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to` before and relying on them being different you should now add checks as From abe95a4c98d8abf13eda72197cba8c6fc63e35fc Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:59:55 -0400 Subject: [PATCH 09/14] rm empty file --- core/core | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/core diff --git a/core/core b/core/core deleted file mode 100644 index e69de29bb2d..00000000000 From 7948ef4e8c452ff19f9755c5ff000934e8a8769d Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 14:03:37 -0400 Subject: [PATCH 10/14] refactor: haptic end first --- core/src/components/reorder-group/reorder-group.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index efed191c7ef..2b883218689 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -250,13 +250,14 @@ export class ReorderGroup implements ComponentInterface { }); } + hapticSelectionEnd(); + this.ionReorderEnd.emit({ from: fromIndex, to: toIndex, complete: this.completeReorder.bind(this), }); - hapticSelectionEnd(); } private completeReorder(listOrReorder?: boolean | any[]): any { From b9f8b3ad486a58361b8654afced644a09e2508b1 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 14:56:38 -0400 Subject: [PATCH 11/14] test(reorder-group): update data test to reorder the DOM --- .../reorder-group/test/data/index.html | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/core/src/components/reorder-group/test/data/index.html b/core/src/components/reorder-group/test/data/index.html index bffa2c5fe82..56cf7b67da1 100644 --- a/core/src/components/reorder-group/test/data/index.html +++ b/core/src/components/reorder-group/test/data/index.html @@ -14,7 +14,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -36,27 +36,44 @@ for (var i = 0; i < 30; i++) { items.push(i + 1); } - const reorderGroup = document.getElementById('reorderGroup'); - - function render() { - let html = ''; - for (let item of items) { - html += ` - - ${item} - - `; - } - reorderGroup.innerHTML = html; - } + const reorderGroup = document.querySelector('ion-reorder-group'); + reorderItems(items); reorderGroup.addEventListener('ionReorderEnd', ({ detail }) => { - console.log('Dragged from index', detail.from, 'to', detail.to); - + // Before complete is called with the items they will remain in the + // order before the drag console.log('Before complete', items); + + // Finish the reorder and position the item in the DOM based on + // where the gesture ended. Update the items variable to the + // new order of items items = detail.complete(items); + + // Reorder the items in the DOM + reorderItems(items); + + // After complete is called the items will be in the new order console.log('After complete', items); }); + + function reorderItems(items) { + reorderGroup.replaceChildren(); + + let reordered = ''; + + for (let i = 0; i < items.length; i++) { + reordered += ` + + + Item ${items[i]} + + + + `; + } + + reorderGroup.innerHTML = reordered; + } From 7f7675e996ddae985b04e77ad848d0314e80943d Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 11 Jun 2025 15:04:06 -0400 Subject: [PATCH 12/14] style: lint --- core/src/components/reorder-group/reorder-group.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index 2b883218689..22bc96760b1 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -257,7 +257,6 @@ export class ReorderGroup implements ComponentInterface { to: toIndex, complete: this.completeReorder.bind(this), }); - } private completeReorder(listOrReorder?: boolean | any[]): any { From 3fb22747dc40dd121818e5db2b2cc6913c33742b Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 12 Jun 2025 13:49:04 -0400 Subject: [PATCH 13/14] refactor(reorder-group): remove TODO to remove completeReorder --- core/src/components/reorder-group/reorder-group.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index 22bc96760b1..a6f7a442ef4 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -236,10 +236,6 @@ export class ReorderGroup implements ComponentInterface { const fromIndex = indexForItem(selectedItemEl); if (toIndex === fromIndex) { - // TODO(FW-6590): Remove this once the deprecated event is removed - // Since the ionReorderEnd event is emitted at the end of every reorder - // gesture, even if the item did not move, the user can always call - // complete() to reset the state of the reorder group. this.completeReorder(); } else { // TODO(FW-6590): Remove this once the deprecated event is removed From 98287b7edd9902fd14b46258b72c9b356cd7f492 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Thu, 12 Jun 2025 14:02:13 -0400 Subject: [PATCH 14/14] docs(reorder-group): add description of deprecated event back --- core/src/components.d.ts | 3 ++- core/src/components/reorder-group/reorder-group.tsx | 10 +++++----- packages/angular/src/directives/proxies.ts | 9 ++++----- packages/angular/standalone/src/directives/proxies.ts | 9 ++++----- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 87fa54a9404..66ab40e747a 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -8042,7 +8042,8 @@ declare namespace LocalJSX { */ "disabled"?: boolean; /** - * @deprecated Use `ionReorderEnd` instead. The new event is emitted at the end of every reorder gesture, even if the positions do not change. If you were accessing `event.detail.from` or `event.detail.to` before and relying on them being different you should now add checks as they are always emitted in `ionReorderEnd`, even when they are the same. + * Event that needs to be listened to in order to complete the reorder action. + * @deprecated Use `ionReorderEnd` instead. If you are accessing `event.detail.from` or `event.detail.to` and relying on them being different you should now add checks as they are always emitted in `ionReorderEnd`, even when they are the same. */ "onIonItemReorder"?: (event: IonReorderGroupCustomEvent) => void; /** diff --git a/core/src/components/reorder-group/reorder-group.tsx b/core/src/components/reorder-group/reorder-group.tsx index a6f7a442ef4..b13c12cc931 100644 --- a/core/src/components/reorder-group/reorder-group.tsx +++ b/core/src/components/reorder-group/reorder-group.tsx @@ -53,11 +53,11 @@ export class ReorderGroup implements ComponentInterface { // TODO(FW-6590): Remove this in a major release. /** - * @deprecated Use `ionReorderEnd` instead. The new event is emitted - * at the end of every reorder gesture, even if the positions do not - * change. If you were accessing `event.detail.from` or `event.detail.to` - * before and relying on them being different you should now add checks as - * they are always emitted in `ionReorderEnd`, even when they are the same. + * Event that needs to be listened to in order to complete the reorder action. + * @deprecated Use `ionReorderEnd` instead. If you are accessing + * `event.detail.from` or `event.detail.to` and relying on them + * being different you should now add checks as they are always emitted + * in `ionReorderEnd`, even when they are the same. */ @Event() ionItemReorder!: EventEmitter; diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index b1ee6771c6c..c81ac8f8571 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1906,11 +1906,10 @@ import type { ReorderEndEventDetail as IIonReorderGroupReorderEndEventDetail } f export declare interface IonReorderGroup extends Components.IonReorderGroup { /** - * @deprecated Use `ionReorderEnd` instead. The new event is emitted -at the end of every reorder gesture, even if the positions do not -change. If you were accessing `event.detail.from` or `event.detail.to` -before and relying on them being different you should now add checks as -they are always emitted in `ionReorderEnd`, even when they are the same. + * Event that needs to be listened to in order to complete the reorder action. @deprecated Use `ionReorderEnd` instead. If you are accessing +`event.detail.from` or `event.detail.to` and relying on them +being different you should now add checks as they are always emitted +in `ionReorderEnd`, even when they are the same. */ ionItemReorder: EventEmitter>; /** diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 77e2b22b4d9..93f9bf10c86 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1766,11 +1766,10 @@ import type { ReorderEndEventDetail as IIonReorderGroupReorderEndEventDetail } f export declare interface IonReorderGroup extends Components.IonReorderGroup { /** - * @deprecated Use `ionReorderEnd` instead. The new event is emitted -at the end of every reorder gesture, even if the positions do not -change. If you were accessing `event.detail.from` or `event.detail.to` -before and relying on them being different you should now add checks as -they are always emitted in `ionReorderEnd`, even when they are the same. + * Event that needs to be listened to in order to complete the reorder action. @deprecated Use `ionReorderEnd` instead. If you are accessing +`event.detail.from` or `event.detail.to` and relying on them +being different you should now add checks as they are always emitted +in `ionReorderEnd`, even when they are the same. */ ionItemReorder: EventEmitter>; /**