mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
fix(vue): custom animation plays when replacing (#25863)
resolves #25831
This commit is contained in:
@ -217,9 +217,18 @@ export const IonRouterOutlet = /*@__PURE__*/ defineComponent({
|
|||||||
requestAnimationFrame(async () => {
|
requestAnimationFrame(async () => {
|
||||||
enteringEl.classList.add('ion-page-invisible');
|
enteringEl.classList.add('ion-page-invisible');
|
||||||
|
|
||||||
|
const hasRootDirection = direction === undefined || direction === 'root' || direction === 'none';
|
||||||
const result = await ionRouterOutlet.value.commit(enteringEl, leavingEl, {
|
const result = await ionRouterOutlet.value.commit(enteringEl, leavingEl, {
|
||||||
deepWait: true,
|
deepWait: true,
|
||||||
duration: direction === undefined || direction === 'root' || direction === 'none' ? 0 : undefined,
|
/**
|
||||||
|
* replace operations result in a direction of none.
|
||||||
|
* These typically do not have need animations, so we set
|
||||||
|
* the duration to 0. However, if a developer explicitly
|
||||||
|
* passes an animationBuilder, we should assume that
|
||||||
|
* they want an animation to be played even
|
||||||
|
* though it is a replace operation.
|
||||||
|
*/
|
||||||
|
duration: hasRootDirection && animationBuilder === undefined ? 0 : undefined,
|
||||||
direction,
|
direction,
|
||||||
showGoBack,
|
showGoBack,
|
||||||
progressAnimation,
|
progressAnimation,
|
||||||
|
@ -203,7 +203,9 @@ describe('useIonRouter', () => {
|
|||||||
await waitForRouter();
|
await waitForRouter();
|
||||||
|
|
||||||
expect(router.currentRoute.value.path).toEqual('/page2');
|
expect(router.currentRoute.value.path).toEqual('/page2');
|
||||||
expect(animFn).not.toHaveBeenCalled();
|
|
||||||
|
// Animation should still be called even though this is a replace operation
|
||||||
|
expect(animFn).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(vm.ionRouter.canGoBack()).toEqual(false);
|
expect(vm.ionRouter.canGoBack()).toEqual(false);
|
||||||
})
|
})
|
||||||
|
157
packages/vue/test/base/tests/unit/router-outlet.spec.ts
Normal file
157
packages/vue/test/base/tests/unit/router-outlet.spec.ts
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import { enableAutoUnmount, mount } from '@vue/test-utils';
|
||||||
|
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||||
|
import {
|
||||||
|
IonicVue,
|
||||||
|
IonApp,
|
||||||
|
IonRouterOutlet,
|
||||||
|
IonPage,
|
||||||
|
useIonRouter,
|
||||||
|
createAnimation
|
||||||
|
} from '@ionic/vue';
|
||||||
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
|
import { mockAnimation, waitForRouter } from './utils';
|
||||||
|
|
||||||
|
enableAutoUnmount(afterEach);
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
components: { IonApp, IonRouterOutlet },
|
||||||
|
template: '<ion-app><ion-router-outlet /></ion-app>',
|
||||||
|
}
|
||||||
|
|
||||||
|
const BasePage = {
|
||||||
|
template: '<ion-page :data-pageid="name"></ion-page>',
|
||||||
|
components: { IonPage },
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* While these tests use useIonRouter,
|
||||||
|
* they are different from the tests in hook.spec.ts
|
||||||
|
* in that they are testing that the correct parameters
|
||||||
|
* are passed to IonRouterOutlet as opposed to hook.spec.ts
|
||||||
|
* which makes sure that the animation function is called when
|
||||||
|
* specifically using useIonRouter.
|
||||||
|
*/
|
||||||
|
describe('Routing', () => {
|
||||||
|
it('should have an animation duration of 0 if replacing without an explicit animation', async () => {
|
||||||
|
const Page1 = {
|
||||||
|
...BasePage,
|
||||||
|
setup() {
|
||||||
|
const ionRouter = useIonRouter();
|
||||||
|
const redirect = () => {
|
||||||
|
ionRouter.replace('/page2')
|
||||||
|
}
|
||||||
|
|
||||||
|
return { redirect }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Page2 = {
|
||||||
|
...BasePage
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', component: Page1 },
|
||||||
|
{ path: '/page2', component: Page2 }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push('/');
|
||||||
|
await router.isReady();
|
||||||
|
const wrapper = mount(App, {
|
||||||
|
global: {
|
||||||
|
plugins: [router, IonicVue]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock the commit function on IonRouterOutlet
|
||||||
|
*/
|
||||||
|
const commitFn = jest.fn();
|
||||||
|
const routerOutlet = wrapper.findComponent(IonRouterOutlet);
|
||||||
|
routerOutlet.vm.$el.commit = commitFn;
|
||||||
|
|
||||||
|
// call redirect method on Page1
|
||||||
|
const cmp = wrapper.findComponent(Page1);
|
||||||
|
cmp.vm.redirect();
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(commitFn).toBeCalledWith(
|
||||||
|
/**
|
||||||
|
* We are not checking the first 2
|
||||||
|
* params in this test,
|
||||||
|
* so we can use expect.anything().
|
||||||
|
*/
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
expect.objectContaining({
|
||||||
|
direction: "none",
|
||||||
|
duration: 0,
|
||||||
|
animationBuilder: undefined
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have an animation duration of null if replacing with an explicit animation', async () => {
|
||||||
|
const animation = mockAnimation();
|
||||||
|
const Page1 = {
|
||||||
|
...BasePage,
|
||||||
|
setup() {
|
||||||
|
const ionRouter = useIonRouter();
|
||||||
|
const redirect = () => {
|
||||||
|
ionRouter.replace('/page2', animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { redirect }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Page2 = {
|
||||||
|
...BasePage
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', component: Page1 },
|
||||||
|
{ path: '/page2', component: Page2 }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push('/');
|
||||||
|
await router.isReady();
|
||||||
|
const wrapper = mount(App, {
|
||||||
|
global: {
|
||||||
|
plugins: [router, IonicVue]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock the commit function on IonRouterOutlet
|
||||||
|
*/
|
||||||
|
const commitFn = jest.fn();
|
||||||
|
const routerOutlet = wrapper.findComponent(IonRouterOutlet);
|
||||||
|
routerOutlet.vm.$el.commit = commitFn;
|
||||||
|
|
||||||
|
// call redirect method on Page1
|
||||||
|
const cmp = wrapper.findComponent(Page1);
|
||||||
|
cmp.vm.redirect();
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(commitFn).toBeCalledWith(
|
||||||
|
/**
|
||||||
|
* We are not checking the first 2
|
||||||
|
* params in this test,
|
||||||
|
* so we can use expect.anything().
|
||||||
|
*/
|
||||||
|
expect.anything(),
|
||||||
|
expect.anything(),
|
||||||
|
expect.objectContaining({
|
||||||
|
direction: "none",
|
||||||
|
duration: undefined,
|
||||||
|
animationBuilder: animation
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,5 @@
|
|||||||
import { flushPromises } from '@vue/test-utils';
|
import { flushPromises } from '@vue/test-utils';
|
||||||
|
import { createAnimation } from '@ionic/vue';
|
||||||
|
|
||||||
export const waitForRouter = async () => {
|
export const waitForRouter = async () => {
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
@ -6,10 +7,5 @@ export const waitForRouter = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const mockAnimation = () => {
|
export const mockAnimation = () => {
|
||||||
return jest.fn(() => {
|
return jest.fn(() => createAnimation());
|
||||||
return {
|
|
||||||
onFinish: () => {},
|
|
||||||
play: () => {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user