mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
fix(modal): card modal no longer dismisses from content with refresher (#25227)
This commit is contained in:
@ -23,6 +23,7 @@ export const createSwipeToCloseGesture = (
|
||||
let isOpen = false;
|
||||
let canDismissBlocksGesture = false;
|
||||
const canDismissMaxStep = 0.2;
|
||||
const hasRefresherInContent = !!contentEl.querySelector('ion-refresher');
|
||||
const getScrollY = () => {
|
||||
if (isIonContent(contentEl)) {
|
||||
return (contentEl as HTMLIonContentElement).scrollY;
|
||||
@ -69,7 +70,16 @@ export const createSwipeToCloseGesture = (
|
||||
*/
|
||||
const content = target.closest('ion-content');
|
||||
if (content) {
|
||||
return scrollEl.scrollTop === 0;
|
||||
/**
|
||||
* The card should never swipe to close
|
||||
* on the content with a refresher.
|
||||
* Note: We cannot solve this by making the
|
||||
* swipeToClose gesture have a higher priority
|
||||
* than the refresher gesture as the iOS native
|
||||
* refresh gesture uses a scroll listener in
|
||||
* addition to a gesture.
|
||||
*/
|
||||
return !hasRefresherInContent && scrollEl.scrollTop === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
111
core/src/components/modal/test/card-refresher/index.html
Normal file
111
core/src/components/modal/test/card-refresher/index.html
Normal file
@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Modal - Card</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="viewport-fit=cover, 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 type="module" src="../../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<script type="module">
|
||||
import { modalController } from '../../../../../dist/ionic/index.esm.js';
|
||||
window.modalController = modalController;
|
||||
</script>
|
||||
<style>
|
||||
#content {
|
||||
position: relative;
|
||||
|
||||
display: block;
|
||||
flex: 1;
|
||||
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
contain: size style;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<div class="ion-page">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Card</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<ion-button expand="block" id="card" onclick="presentModal(document.querySelectorAll('.ion-page')[1])"
|
||||
>Card Modal</ion-button
|
||||
>
|
||||
</ion-content>
|
||||
</div>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
async function createModal(presentingEl, opts) {
|
||||
// create component to open
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = `
|
||||
<ion-header id="modal-header">
|
||||
<ion-toolbar>
|
||||
<ion-title>Contacts</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button class="add">
|
||||
<ion-icon name="add" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed">
|
||||
<ion-refresher-content></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
Hello World!
|
||||
<ion-button class="dismiss">Dismiss Modal</ion-button>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-title>Footer</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
`;
|
||||
|
||||
// listen for close event
|
||||
const button = element.querySelector('ion-button.dismiss');
|
||||
button.addEventListener('click', () => {
|
||||
modalController.dismiss();
|
||||
});
|
||||
|
||||
const create = element.querySelector('ion-button.add');
|
||||
create.addEventListener('click', async () => {
|
||||
const topModal = await modalController.getTop();
|
||||
|
||||
presentModal(topModal, opts);
|
||||
});
|
||||
|
||||
// present the modal
|
||||
const modalElement = await modalController.create({
|
||||
presentingElement: presentingEl,
|
||||
component: element,
|
||||
swipeToClose: true,
|
||||
...opts,
|
||||
});
|
||||
return modalElement;
|
||||
}
|
||||
|
||||
async function presentModal(presentingEl, opts) {
|
||||
const modal = await createModal(presentingEl, opts);
|
||||
await modal.present();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
25
core/src/components/modal/test/card-refresher/modal.e2e.ts
Normal file
25
core/src/components/modal/test/card-refresher/modal.e2e.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { dragElementBy, test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('card modal - with refresher', () => {
|
||||
test.beforeEach(async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS');
|
||||
|
||||
await page.goto('/src/components/modal/test/card-refresher');
|
||||
});
|
||||
test('it should not swipe to close on the content due to the presence of the refresher', async ({ page }) => {
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
|
||||
await page.click('#card');
|
||||
await ionModalDidPresent.next();
|
||||
|
||||
const modal = await page.locator('ion-modal');
|
||||
const content = (await page.$('ion-modal ion-content'))!;
|
||||
|
||||
await dragElementBy(content, page, 0, 500);
|
||||
|
||||
await content.waitForElementState('stable');
|
||||
|
||||
expect(modal).toBeVisible();
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user