Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(nav): swipe to go back works inside card modal #25333

Merged
merged 12 commits into from
May 23, 2022
2 changes: 1 addition & 1 deletion core/src/components/modal/gestures/swipe-to-close.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ export const createSwipeToCloseGesture = (el: HTMLIonModalElement, animation: An
const gesture = createGesture({
el,
gestureName: 'modalSwipeToClose',
gesturePriority: 40,
gesturePriority: 39,
direction: 'y',
threshold: 10,
canStart,
Expand Down
88 changes: 88 additions & 0 deletions core/src/components/modal/test/card-nav/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Modal - Card + Nav</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>
</head>

<body>
<script>
class AppNav extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-content>
<ion-nav root="page-one"></ion-nav>
</ion-content>
`;
}
}
class PageOne extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Page One</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<h1>Page One</h1>
<ion-nav-link router-direction="forward" component="page-two">
<ion-button id="go-page-two">Go to Page Two</ion-button>
</ion-nav-link>
</ion-content>
`;
}
}
class PageTwo extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Page Two</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding page-two-content"></ion-content>
`;
}
}
customElements.define('page-one', PageOne);
customElements.define('page-two', PageTwo);
customElements.define('app-nav', AppNav);
</script>
<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 id="open-modal">Open Modal</ion-button>

<ion-modal trigger="open-modal" component="app-nav"></ion-modal>
</ion-content>
</div>
</ion-app>

<script>
const modal = document.querySelector('ion-modal');
const nav = document.querySelector('ion-nav');
modal.canDismiss = true;
modal.presentingElement = document.querySelector('.ion-page');
</script>
</body>
</html>
50 changes: 50 additions & 0 deletions core/src/components/modal/test/card-nav/modal.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect } from '@playwright/test';
import { test, dragElementBy } from '@utils/test/playwright';

import { CardModalPage } from '../fixtures';

test.describe('card modal - nav', () => {
let cardModalPage: CardModalPage;
test.beforeEach(async ({ page, browserName }, testInfo) => {
test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS');
test.skip(
testInfo.project.metadata.rtl === true,
'This test only verifies that the gesture activates inside of a modal.'
);
test.skip(browserName !== 'chromium', 'dragElementBy is flaky outside of Chrome browsers.');

cardModalPage = new CardModalPage(page);
await cardModalPage.navigate('/src/components/modal/test/card-nav?ionic:_testing=false');
});
test('it should swipe to go back', async ({ page }) => {
await cardModalPage.openModalByTrigger('#open-modal');

const nav = page.locator('ion-nav') as any;
const ionNavDidChange = await nav.spyOnEvent('ionNavDidChange');

await page.click('#go-page-two');

await ionNavDidChange.next();

const pageOne = page.locator('page-one');
expect(pageOne).toHaveClass(/ion-page-hidden/);

const content = page.locator('.page-two-content');

await dragElementBy(content, page, 1000, 0, 10);

await ionNavDidChange.next();
});
test('should swipe to close', async ({ page }) => {
await cardModalPage.openModalByTrigger('#open-modal');

const nav = page.locator('ion-nav') as any;
const ionNavDidChange = await nav.spyOnEvent('ionNavDidChange');

await page.click('#go-page-two');

await ionNavDidChange.next();

await cardModalPage.swipeToCloseModal('ion-modal ion-content.page-two-content');
});
});
39 changes: 4 additions & 35 deletions core/src/components/modal/test/card/modal.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,15 @@
import { expect } from '@playwright/test';
import { dragElementBy, test, Viewports } from '@utils/test/playwright';
import type { E2EPage, EventSpy } from '@utils/test/playwright';

class CardModalPage {
private ionModalDidPresent!: EventSpy;
private ionModalDidDismiss!: EventSpy;
private page: E2EPage;

constructor(page: E2EPage) {
this.page = page;
}
async navigate() {
const { page } = this;
await page.goto('/src/components/modal/test/card');
this.ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
this.ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
}
async openModalByTrigger(selector: string) {
await this.page.click(selector);
await this.ionModalDidPresent.next();

return this.page.locator('ion-modal');
}

async swipeToCloseModal(selector: string, waitForDismiss = true, swipeY = 500) {
const { page } = this;
const elementRef = await page.locator(selector);
await dragElementBy(elementRef, page, 0, swipeY);

if (waitForDismiss) {
await this.ionModalDidDismiss.next();
}
}
}
import { test, Viewports } from '@utils/test/playwright';

import { CardModalPage } from '../fixtures';

test.describe('card modal', () => {
let cardModalPage: CardModalPage;
test.beforeEach(async ({ page }, testInfo) => {
test.skip(testInfo.project.metadata.mode !== 'ios', 'Card style modal is only available on iOS');

cardModalPage = new CardModalPage(page);
await cardModalPage.navigate();
await cardModalPage.navigate('/src/components/modal/test/card');
});
test.describe('card modal: rendering', () => {
test('should not have visual regressions', async ({ page }) => {
Expand Down
34 changes: 34 additions & 0 deletions core/src/components/modal/test/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { dragElementBy } from '@utils/test/playwright';
import type { E2EPage, EventSpy } from '@utils/test/playwright';

export class CardModalPage {
private ionModalDidPresent!: EventSpy;
private ionModalDidDismiss!: EventSpy;
private page: E2EPage;

constructor(page: E2EPage) {
this.page = page;
}
async navigate(url: string) {
const { page } = this;
await page.goto(url);
this.ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
this.ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
}
async openModalByTrigger(selector: string) {
await this.page.click(selector);
await this.ionModalDidPresent.next();

return this.page.locator('ion-modal');
}

async swipeToCloseModal(selector: string, waitForDismiss = true, swipeY = 500) {
const { page } = this;
const elementRef = await page.locator(selector);
await dragElementBy(elementRef, page, 0, swipeY);

if (waitForDismiss) {
await this.ionModalDidDismiss.next();
}
}
}
8 changes: 5 additions & 3 deletions core/src/utils/test/playwright/drag-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export const dragElementBy = async (
el: Locator | ElementHandle<SVGElement | HTMLElement>,
page: E2EPage,
dragByX = 0,
dragByY = 0
dragByY = 0,
startXCoord?: number,
startYCoord?: number
) => {
const boundingBox = await el.boundingBox();

Expand All @@ -16,8 +18,8 @@ export const dragElementBy = async (
);
}

const startX = boundingBox.x + boundingBox.width / 2;
const startY = boundingBox.y + boundingBox.height / 2;
const startX = startXCoord === undefined ? boundingBox.x + boundingBox.width / 2 : startXCoord;
const startY = startYCoord === undefined ? boundingBox.y + boundingBox.height / 2 : startYCoord;

const midX = startX + dragByX / 2;
const midY = startY + dragByY / 2;
Expand Down