diff --git a/core/src/components/item-sliding/item-sliding.tsx b/core/src/components/item-sliding/item-sliding.tsx index bd20878f97..f6359404ac 100644 --- a/core/src/components/item-sliding/item-sliding.tsx +++ b/core/src/components/item-sliding/item-sliding.tsx @@ -3,6 +3,7 @@ import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from ' import { getIonMode } from '../../global/ionic-global'; import type { Gesture, GestureDetail, Side } from '../../interface'; +import { findClosestIonContent, disableContentScrollY, resetContentScrollY } from '../../utils/content'; import { isEndSide } from '../../utils/helpers'; const SWIPE_MARGIN = 30; @@ -43,7 +44,7 @@ export class ItemSliding implements ComponentInterface { private rightOptions?: HTMLIonItemOptionsElement; private optsDirty = true; private gesture?: Gesture; - private closestContent: HTMLIonContentElement | null = null; + private contentEl: HTMLElement | null = null; private initialContentScrollY = true; @Element() el!: HTMLIonItemSlidingElement; @@ -68,7 +69,7 @@ export class ItemSliding implements ComponentInterface { async connectedCallback() { this.item = this.el.querySelector('ion-item'); - this.closestContent = this.el.closest('ion-content'); + this.contentEl = findClosestIonContent(this.el); await this.updateOptions(); @@ -264,23 +265,6 @@ export class ItemSliding implements ComponentInterface { return !!(this.rightOptions || this.leftOptions); } - private disableContentScrollY() { - if (this.closestContent === null) { - return; - } - - this.initialContentScrollY = this.closestContent.scrollY; - this.closestContent.scrollY = false; - } - - private restoreContentScrollY() { - if (this.closestContent === null) { - return; - } - - this.closestContent.scrollY = this.initialContentScrollY; - } - private onStart() { /** * We need to query for the ion-item @@ -289,8 +273,10 @@ export class ItemSliding implements ComponentInterface { */ this.item = this.el.querySelector('ion-item'); - // Prevent scrolling during gesture - this.disableContentScrollY(); + const { contentEl } = this; + if (contentEl) { + this.initialContentScrollY = disableContentScrollY(contentEl); + } openSlidingItem = this.el; @@ -343,8 +329,10 @@ export class ItemSliding implements ComponentInterface { } private onEnd(gesture: GestureDetail) { - // Restore ion-content scrollY to initial value when gesture ends - this.restoreContentScrollY(); + const { contentEl, initialContentScrollY } = this; + if (contentEl) { + resetContentScrollY(contentEl, initialContentScrollY); + } const velocity = gesture.velocityX; diff --git a/core/src/components/item-sliding/test/basic/index.html b/core/src/components/item-sliding/test/basic/index.html index cebd73b958..a9bed0e0bd 100644 --- a/core/src/components/item-sliding/test/basic/index.html +++ b/core/src/components/item-sliding/test/basic/index.html @@ -75,7 +75,7 @@ - + Two options, one dynamic option and text @@ -89,7 +89,7 @@ - + diff --git a/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts new file mode 100644 index 0000000000..33c0511151 --- /dev/null +++ b/core/src/components/item-sliding/test/basic/item-sliding.e2e.ts @@ -0,0 +1,35 @@ +import { expect } from '@playwright/test'; +import { test } from '@utils/test/playwright'; + +test.describe('item-sliding: basic', () => { + test('should not scroll when the item-sliding is swiped', async ({ page, browserName }, testInfo) => { + test.skip(browserName === 'webkit', 'mouse.wheel is not available in WebKit'); + test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL-specific behaviors'); + + await page.goto(`/src/components/item-sliding/test/basic`); + + const itemSlidingEl = page.locator('#two-options'); + const scrollEl = page.locator('ion-content .inner-scroll'); + + expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0); + + const box = (await itemSlidingEl.boundingBox())!; + const centerX = box.x + box.width / 2; + const centerY = box.y + box.height / 2; + + await page.mouse.move(centerX, centerY); + await page.mouse.down(); + await page.mouse.move(centerX - 30, centerY); + + /** + * Do not use scrollToBottom() or other scrolling methods + * on ion-content as those will update the scroll position. + * Setting scrollTop still works even with overflow-y: hidden. + * However, simulating a user gesture should not scroll the content. + */ + await page.mouse.wheel(0, 100); + await page.waitForChanges(); + + expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0); + }); +}); diff --git a/core/src/components/item-sliding/test/scroll-target/index.html b/core/src/components/item-sliding/test/scroll-target/index.html new file mode 100644 index 0000000000..813e94363e --- /dev/null +++ b/core/src/components/item-sliding/test/scroll-target/index.html @@ -0,0 +1,89 @@ + + + + + Item Sliding - Scroll Target + + + + + + + + + + + + + Item Sliding - Scroll Target + + + + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur faucibus nulla a nunc tincidunt semper. + Nam nibh lorem, pharetra ac ex ac, tempus fringilla est. Aenean tincidunt ipsum pellentesque, consequat + libero id, feugiat leo. In vestibulum faucibus velit, non tincidunt erat tincidunt in. Donec a diam sed nisl + convallis maximus. Aenean cursus sagittis lorem vitae tristique. Pellentesque pellentesque, quam eget + lobortis finibus, lectus lorem maximus purus, quis sagittis tortor sem sed tellus. +

+ + + + Item Sliding + + + + + + + + + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur faucibus nulla a nunc tincidunt semper. + Nam nibh lorem, pharetra ac ex ac, tempus fringilla est. Aenean tincidunt ipsum pellentesque, consequat + libero id, feugiat leo. In vestibulum faucibus velit, non tincidunt erat tincidunt in. Donec a diam sed nisl + convallis maximus. Aenean cursus sagittis lorem vitae tristique. Pellentesque pellentesque, quam eget + lobortis finibus, lectus lorem maximus purus, quis sagittis tortor sem sed tellus. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur faucibus nulla a nunc tincidunt semper. + Nam nibh lorem, pharetra ac ex ac, tempus fringilla est. Aenean tincidunt ipsum pellentesque, consequat + libero id, feugiat leo. In vestibulum faucibus velit, non tincidunt erat tincidunt in. Donec a diam sed nisl + convallis maximus. Aenean cursus sagittis lorem vitae tristique. Pellentesque pellentesque, quam eget + lobortis finibus, lectus lorem maximus purus, quis sagittis tortor sem sed tellus. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur faucibus nulla a nunc tincidunt semper. + Nam nibh lorem, pharetra ac ex ac, tempus fringilla est. Aenean tincidunt ipsum pellentesque, consequat + libero id, feugiat leo. In vestibulum faucibus velit, non tincidunt erat tincidunt in. Donec a diam sed nisl + convallis maximus. Aenean cursus sagittis lorem vitae tristique. Pellentesque pellentesque, quam eget + lobortis finibus, lectus lorem maximus purus, quis sagittis tortor sem sed tellus. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur faucibus nulla a nunc tincidunt semper. + Nam nibh lorem, pharetra ac ex ac, tempus fringilla est. Aenean tincidunt ipsum pellentesque, consequat + libero id, feugiat leo. In vestibulum faucibus velit, non tincidunt erat tincidunt in. Donec a diam sed nisl + convallis maximus. Aenean cursus sagittis lorem vitae tristique. Pellentesque pellentesque, quam eget + lobortis finibus, lectus lorem maximus purus, quis sagittis tortor sem sed tellus. +

+
+
+
+ + diff --git a/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts b/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts new file mode 100644 index 0000000000..162ed9105a --- /dev/null +++ b/core/src/components/item-sliding/test/scroll-target/item-sliding.e2e.ts @@ -0,0 +1,38 @@ +import { expect } from '@playwright/test'; +import { test } from '@utils/test/playwright'; + +test.describe('item-sliding: scroll-target', () => { + test('should not scroll when the item-sliding is swiped in custom scroll target', async ({ + page, + browserName, + }, testInfo) => { + test.skip(browserName === 'webkit', 'mouse.wheel is not available in WebKit'); + test.skip(testInfo.project.metadata.rtl === true, 'This feature does not have RTL-specific behaviors'); + + await page.goto(`/src/components/item-sliding/test/scroll-target`); + + const itemSlidingEl = page.locator('ion-item-sliding'); + const scrollEl = page.locator('.ion-content-scroll-host'); + + expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0); + + const box = (await itemSlidingEl.boundingBox())!; + const centerX = box.x + box.width / 2; + const centerY = box.y + box.height / 2; + + await page.mouse.move(centerX, centerY); + await page.mouse.down(); + await page.mouse.move(centerX - 30, centerY); + + /** + * Do not use scrollToBottom() or other scrolling methods + * on ion-content as those will update the scroll position. + * Setting scrollTop still works even with overflow-y: hidden. + * However, simulating a user gesture should not scroll the content. + */ + await page.mouse.wheel(0, 100); + await page.waitForChanges(); + + expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0); + }); +});