mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 10:41:13 +08:00
fix(vue): improve swipe to go back reliability (#22288)
resolves #22237
This commit is contained in:
@ -10,14 +10,13 @@ import {
|
||||
InjectionKey
|
||||
} from 'vue';
|
||||
import { AnimationBuilder } from '@ionic/core';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { fireLifecycle, generateId, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '../utils';
|
||||
|
||||
let viewDepthKey: InjectionKey<0> = Symbol(0);
|
||||
export const IonRouterOutlet = defineComponent({
|
||||
name: 'IonRouterOutlet',
|
||||
setup(_, { attrs }) {
|
||||
const vueRouter = useRouter();
|
||||
const route = useRoute();
|
||||
const depth = inject(viewDepthKey, 0);
|
||||
|
||||
@ -57,13 +56,22 @@ export const IonRouterOutlet = defineComponent({
|
||||
|
||||
const canStart = () => {
|
||||
const stack = viewStacks.getViewStack(id);
|
||||
return stack && stack.length > 1;
|
||||
if (!stack || stack.length <= 1) return false;
|
||||
|
||||
/**
|
||||
* We only want to outlet of the entering view
|
||||
* to respond to this gesture, so check
|
||||
* to make sure the view is in the outlet we want.
|
||||
*/
|
||||
const routeInfo = ionRouter.getCurrentRouteInfo();
|
||||
const enteringViewItem = viewStacks.findViewItemByRouteInfo({ pathname: routeInfo.pushedByRoute }, id);
|
||||
|
||||
return !!enteringViewItem;
|
||||
}
|
||||
const onStart = async () => {
|
||||
const routeInfo = ionRouter.getCurrentRouteInfo();
|
||||
const { routerAnimation } = routeInfo;
|
||||
const items = viewStacks.getViewStack(id);
|
||||
const enteringViewItem = items[items.length - 2];
|
||||
const enteringViewItem = viewStacks.findViewItemByRouteInfo({ pathname: routeInfo.pushedByRoute }, id);
|
||||
const leavingViewItem = viewStacks.findViewItemByRouteInfo(routeInfo, id);
|
||||
|
||||
if (leavingViewItem) {
|
||||
@ -105,7 +113,23 @@ export const IonRouterOutlet = defineComponent({
|
||||
const onEnd = (shouldContinue: boolean) => {
|
||||
if (shouldContinue) {
|
||||
skipTransition = true;
|
||||
vueRouter.back();
|
||||
|
||||
/**
|
||||
* Use the same logic as clicking
|
||||
* ion-back-button to determine where
|
||||
* to go back to.
|
||||
*/
|
||||
ionRouter.handleNavigateBack();
|
||||
} else {
|
||||
/**
|
||||
* In the event that the swipe
|
||||
* gesture was aborted, we should
|
||||
* re-hide the page that was going to enter.
|
||||
*/
|
||||
const routeInfo = ionRouter.getCurrentRouteInfo();
|
||||
const enteringViewItem = viewStacks.findViewItemByRouteInfo({ pathname: routeInfo.pushedByRoute }, id);
|
||||
enteringViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
|
||||
enteringViewItem.ionPageElement.classList.add('ion-page-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
20
packages/vue/test-app/package-lock.json
generated
20
packages/vue/test-app/package-lock.json
generated
@ -1306,27 +1306,27 @@
|
||||
}
|
||||
},
|
||||
"@ionic/core": {
|
||||
"version": "5.4.0-dev.202010081632.d371af4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.4.0-dev.202010081632.d371af4.tgz",
|
||||
"integrity": "sha512-Umy3OIbolByO9a4ZpETLxQ904TmaxD8grUnF3j7sfRFji4jXnzHAaVz6FteogWeZHjUa4hxJwrD3oBMh3OPKJQ==",
|
||||
"version": "5.4.0-dev.202010091911.bfc0b25",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.4.0-dev.202010091911.bfc0b25.tgz",
|
||||
"integrity": "sha512-gDWwMcgV+kPeTfBToZ8oLoSI7FygkRYEgi/DEAIHye3W8T/Z/NmqrxaNqXrXU0J0RKvfzrIlrw6uB67Xz1xRcw==",
|
||||
"requires": {
|
||||
"ionicons": "^5.1.2",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@ionic/vue": {
|
||||
"version": "5.4.0-dev.202010081632.d371af4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-5.4.0-dev.202010081632.d371af4.tgz",
|
||||
"integrity": "sha512-fPvnbWLxrP4wCSFlQepH/J++IkOE0wDvZlPAjl+B/xXGW8jEp0cr8HVY+cXevgPGiUbE0eUexBlOtmYLZ2yIEQ==",
|
||||
"version": "5.4.0-dev.202010091911.bfc0b25",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-5.4.0-dev.202010091911.bfc0b25.tgz",
|
||||
"integrity": "sha512-zrF/u3sMq4k3mLMtlA/VcS+RFeWEXrhOnzJAo8uPuDGT4ZcttJv0sVIFJMqZF7zM+6+xxep5B5mMoB1cigwG3A==",
|
||||
"requires": {
|
||||
"@ionic/core": "5.4.0-dev.202010081632.d371af4",
|
||||
"@ionic/core": "5.4.0-dev.202010091911.bfc0b25",
|
||||
"ionicons": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@ionic/vue-router": {
|
||||
"version": "5.4.0-dev.202010081632.d371af4",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-5.4.0-dev.202010081632.d371af4.tgz",
|
||||
"integrity": "sha512-/ZrfEppL1UeA3Eb+QSa4D05AjPPwcloevpPOoXW8tkJ0q8A9yb7n/29IlsnPdp0m/zSJKyJAFlO2cxy5RVQSRw=="
|
||||
"version": "5.4.0-dev.202010091911.bfc0b25",
|
||||
"resolved": "https://registry.npmjs.org/@ionic/vue-router/-/vue-router-5.4.0-dev.202010091911.bfc0b25.tgz",
|
||||
"integrity": "sha512-moa388WPxJjHBcIVILoijS+xrTcS7jaK4T/rP3U9Lri1bSYrDpYNWmd/0sktGt80m3gaa8VXS5E995zDiyLAfQ=="
|
||||
},
|
||||
"@jest/console": {
|
||||
"version": "24.9.0",
|
||||
|
@ -11,8 +11,8 @@
|
||||
"cypress": "node_modules/.bin/cypress run --headless --browser chrome"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/vue": "5.4.0-dev.202010081632.d371af4",
|
||||
"@ionic/vue-router": "5.4.0-dev.202010081632.d371af4",
|
||||
"@ionic/vue": "5.4.0-dev.202010091911.bfc0b25",
|
||||
"@ionic/vue-router": "5.4.0-dev.202010091911.bfc0b25",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0-0",
|
||||
"vue-router": "^4.0.0-0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<ion-page data-pageid="tabs">
|
||||
<ion-content>
|
||||
<ion-tabs>
|
||||
<ion-tabs id="tabs">
|
||||
<ion-tab-bar slot="bottom">
|
||||
<ion-tab-button tab="tab1" href="/tabs/tab1">
|
||||
<ion-icon :icon="triangle" />
|
||||
|
@ -1,20 +1,4 @@
|
||||
describe('Navigation', () => {
|
||||
|
||||
/*
|
||||
// TODO move these to separate describe block
|
||||
it.skip('should swipe and abort', () => {
|
||||
cy.ionPageInvisible('home');
|
||||
cy.ionSwipeToGoBack();
|
||||
cy.ionPageInvisible('home');
|
||||
cy.ionPageVisible('navigation');
|
||||
});
|
||||
|
||||
it.skip('should swipe and complete', () => {
|
||||
cy.ionSwipeToGoBack(true);
|
||||
cy.ionPageVisible('home');
|
||||
});
|
||||
*/
|
||||
|
||||
it('should go to sibling page', () => {
|
||||
cy.visit('http://localhost:8080');
|
||||
cy.get('ion-item#navigation').click();
|
||||
@ -64,3 +48,27 @@ describe('Navigation', () => {
|
||||
cy.ionPageDoesNotExist('navigationchild')
|
||||
})
|
||||
});
|
||||
|
||||
describe('Navigation - Swipe to Go Back', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(320, 568);
|
||||
cy.visit('http://localhost:8080?ionic:mode=ios');
|
||||
cy.get('#navigation').click();
|
||||
cy.ionPageHidden('home');
|
||||
cy.ionPageVisible('navigation')
|
||||
});
|
||||
|
||||
it('should swipe and abort', () => {
|
||||
cy.ionSwipeToGoBack();
|
||||
cy.ionPageHidden('home');
|
||||
cy.ionPageVisible('navigation');
|
||||
});
|
||||
|
||||
it('should swipe and complete', () => {
|
||||
cy.ionSwipeToGoBack(true);
|
||||
cy.ionPageVisible('home');
|
||||
|
||||
// TODO: Vue router does not go back in cypress with router.back()
|
||||
//cy.ionPageDoesNotExist('navigation');
|
||||
});
|
||||
})
|
||||
|
@ -66,3 +66,71 @@ describe('Tabs', () => {
|
||||
cy.ionPageDoesNotExist('tabs');
|
||||
});
|
||||
})
|
||||
|
||||
describe('Tabs - Swipe to Go Back', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(320, 568);
|
||||
cy.visit('http://localhost:8080?ionic:mode=ios');
|
||||
cy.get('#tabs').click();
|
||||
cy.ionPageHidden('home');
|
||||
cy.ionPageVisible('tab1')
|
||||
});
|
||||
|
||||
it('should swipe and abort', () => {
|
||||
cy.ionSwipeToGoBack();
|
||||
cy.ionPageHidden('home');
|
||||
cy.ionPageVisible('tab1');
|
||||
});
|
||||
|
||||
it('should swipe and go back to home', () => {
|
||||
cy.ionSwipeToGoBack(true);
|
||||
cy.ionPageVisible('home');
|
||||
|
||||
// TODO: Vue router does not go back in cypress with router.back()
|
||||
//cy.ionPageDoesNotExist('tabs1');
|
||||
});
|
||||
|
||||
it('should swipe and abort within a tab', () => {
|
||||
cy.get('#child-one').click();
|
||||
cy.ionPageVisible('tab1childone');
|
||||
cy.ionPageHidden('tab1');
|
||||
|
||||
cy.ionSwipeToGoBack(false, 'ion-tabs#tabs ion-router-outlet');
|
||||
|
||||
cy.ionPageHidden('tab1');
|
||||
cy.ionPageVisible('tab1childone');
|
||||
});
|
||||
|
||||
it('should swipe and go back within a tab', () => {
|
||||
cy.get('#child-one').click();
|
||||
cy.ionPageVisible('tab1childone');
|
||||
cy.ionPageHidden('tab1');
|
||||
|
||||
cy.ionSwipeToGoBack(true, 'ion-tabs#tabs ion-router-outlet');
|
||||
|
||||
cy.ionPageVisible('tab1');
|
||||
cy.ionPageDoesNotExist('tab1childone');
|
||||
});
|
||||
|
||||
it('should swipe and go back to correct tab after switching tabs', () => {
|
||||
cy.get('#child-one').click();
|
||||
cy.ionPageVisible('tab1childone');
|
||||
cy.ionPageHidden('tab1');
|
||||
|
||||
cy.get('ion-tab-button#tab-button-tab2').click();
|
||||
cy.ionPageVisible('tab2');
|
||||
cy.ionPageHidden('tab1childone');
|
||||
|
||||
cy.get('ion-tab-button#tab-button-tab1').click();
|
||||
cy.ionPageVisible('tab1childone');
|
||||
cy.ionPageHidden('tab2');
|
||||
|
||||
cy.ionSwipeToGoBack(true, 'ion-tabs#tabs ion-router-outlet');
|
||||
|
||||
cy.ionPageVisible('tab1');
|
||||
cy.ionPageDoesNotExist('tab1childone');
|
||||
|
||||
cy.ionSwipeToGoBack(true, 'ion-tabs#tabs ion-router-outlet');
|
||||
cy.ionPageVisible('home');
|
||||
});
|
||||
})
|
||||
|
@ -24,11 +24,20 @@
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
Cypress.Commands.add('ionSwipeToGoBack', (complete = false) => {
|
||||
cy.get('ion-router-outlet')
|
||||
.trigger('mousedown', { position: "left" })
|
||||
.trigger('mousemove', { clientX: (complete) ? 100 : 300, clientY: 275 })
|
||||
.trigger('mouseup', { force: true })
|
||||
Cypress.Commands.add('ionSwipeToGoBack', (complete = false, selector = 'ion-router-outlet') => {
|
||||
const increment = (complete) ? 60 : 25;
|
||||
cy.get(selector)
|
||||
.first()
|
||||
.trigger('mousedown', 0, 275, { which: 1, force: true })
|
||||
.trigger('mousemove', increment * 1, 275, { which: 1, force: true })
|
||||
.wait(50)
|
||||
.trigger('mousemove', increment * 2, 275, { which: 1, force: true })
|
||||
.wait(50)
|
||||
.trigger('mousemove', increment * 3, 275, { which: 1, force: true })
|
||||
.wait(50)
|
||||
.trigger('mousemove', increment * 4, 275, { which: 1, force: true })
|
||||
.wait(50)
|
||||
.trigger('mouseup', increment * 4, 275, { which: 1, force: true })
|
||||
cy.wait(150);
|
||||
})
|
||||
|
||||
|
Reference in New Issue
Block a user