fix(vue): custom animation plays when replacing (#25863)

resolves #25831
This commit is contained in:
Liam DeBeasi
2022-09-06 08:07:33 -05:00
committed by GitHub
parent 629f1ed33f
commit 2d3661ae38
4 changed files with 172 additions and 8 deletions

View File

@ -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,

View File

@ -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);
}) })

View 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
})
)
});
});

View File

@ -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: () => {}
}
})
} }