diff --git a/core/src/components/item-sliding/item-sliding.tsx b/core/src/components/item-sliding/item-sliding.tsx index 3f671c0b1d..74c854e185 100644 --- a/core/src/components/item-sliding/item-sliding.tsx +++ b/core/src/components/item-sliding/item-sliding.tsx @@ -1,5 +1,6 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; +import { watchForOptions } from '@utils/watch-options'; import { getIonMode } from '../../global/ionic-global'; import type { Gesture, GestureDetail } from '../../interface'; @@ -47,6 +48,7 @@ export class ItemSliding implements ComponentInterface { private gesture?: Gesture; private contentEl: HTMLElement | null = null; private initialContentScrollY = true; + private mutationObserver?: MutationObserver; @Element() el!: HTMLIonItemSlidingElement; @@ -69,13 +71,19 @@ export class ItemSliding implements ComponentInterface { @Event() ionDrag!: EventEmitter; async connectedCallback() { - this.item = this.el.querySelector('ion-item'); - this.contentEl = findClosestIonContent(this.el); + const { el } = this; + + this.item = el.querySelector('ion-item'); + this.contentEl = findClosestIonContent(el); await this.updateOptions(); + this.mutationObserver = watchForOptions(el, 'ion-item-option', async () => { + await this.updateOptions(); + }); + this.gesture = (await import('../../utils/gesture')).createGesture({ - el: this.el, + el, gestureName: 'item-swipe', gesturePriority: 100, threshold: 5, @@ -99,6 +107,11 @@ export class ItemSliding implements ComponentInterface { if (openSlidingItem === this.el) { openSlidingItem = undefined; } + + if (this.mutationObserver) { + this.mutationObserver.disconnect(); + this.mutationObserver = undefined; + } } /** diff --git a/core/src/components/item-sliding/test/async/index.html b/core/src/components/item-sliding/test/async/index.html index 401e37dc96..032d0ef3a9 100644 --- a/core/src/components/item-sliding/test/async/index.html +++ b/core/src/components/item-sliding/test/async/index.html @@ -12,6 +12,12 @@ + + @@ -23,8 +29,18 @@ + Make async changes - + + + Option + + + + ion-item-options added async + + + ion-item-options removed async Option @@ -32,16 +48,40 @@ diff --git a/core/src/components/item-sliding/test/async/item-sliding.e2e.ts b/core/src/components/item-sliding/test/async/item-sliding.e2e.ts index d91375403e..aa44b11292 100644 --- a/core/src/components/item-sliding/test/async/item-sliding.e2e.ts +++ b/core/src/components/item-sliding/test/async/item-sliding.e2e.ts @@ -3,14 +3,17 @@ import { configs, test } from '@utils/test/playwright'; configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('item-sliding: async'), () => { - test('should open even when item is added async', async ({ page }) => { + test.beforeEach(async ({ page }) => { await page.goto(`/src/components/item-sliding/test/async`, config); + const toggleButton = page.locator('#toggle-button'); - const itemEl = page.locator('ion-item'); - const itemSlidingEl = page.locator('ion-item-sliding'); + await toggleButton.click(); + await expect(toggleButton).toHaveClass(/hidden/); // class is added when everything is ready + }); - // Wait for item to be added to DOM - await page.waitForSelector('ion-item'); + test('should open even when ion-item is added async', async ({ page }) => { + const itemSlidingEl = page.locator('#async-item'); + const itemEl = itemSlidingEl.locator('ion-item'); // Click item to open ion-item-sliding await itemEl.click(); @@ -18,5 +21,19 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => // This class is added when the item sliding component is fully open await expect(itemSlidingEl).toHaveClass(/item-sliding-active-slide/); }); + + test('should open when ion-item-options are added async', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/25578', + }); + + const itemSlidingEl = page.locator('#async-options-added'); + const itemEl = itemSlidingEl.locator('ion-item'); + + await itemEl.click(); + + await expect(itemSlidingEl).toHaveClass(/item-sliding-active-slide/); + }); }); });