diff --git a/packages/vue/src/components/IonApp.ts b/packages/vue/src/components/IonApp.ts index 4d1755ae54..7a3e184c7b 100644 --- a/packages/vue/src/components/IonApp.ts +++ b/packages/vue/src/components/IonApp.ts @@ -31,14 +31,7 @@ export const addTeleportedUserComponent = (component: VNode) => { }; export const removeTeleportedUserComponent = (component: VNode) => { - /** - * Finds the index of the component in the array and removes it. - * Previously we were using a filter to remove the component from the array, - * but this was causing a bug where dismissing an overlay and then presenting - * a new overlay, would cause the new overlay to be removed. - */ - const index = userComponents.value.findIndex((cmp) => cmp === component); - if (index !== -1) { - userComponents.value.splice(index, 1); - } + userComponents.value = userComponents.value.filter( + (cmp) => cmp !== component + ); }; diff --git a/packages/vue/src/framework-delegate.ts b/packages/vue/src/framework-delegate.ts index 03a022859a..2908cff61a 100644 --- a/packages/vue/src/framework-delegate.ts +++ b/packages/vue/src/framework-delegate.ts @@ -11,7 +11,9 @@ export const VueDelegate = ( addFn = addTeleportedUserComponent, removeFn = removeTeleportedUserComponent ): FrameworkDelegate => { - let Component: VNode | undefined; + // `h` doesn't provide a type for the component argument + const refMap = new WeakMap(); + // TODO(FW-2969): types const attachViewToDom = ( parentElement: HTMLElement, @@ -32,15 +34,23 @@ export const VueDelegate = ( classes && div.classList.add(...classes); parentElement.appendChild(div); - Component = h(Teleport, { to: div }, h(component, { ...componentProps })); + const hostComponent = h( + Teleport, + { to: div }, + h(component, { ...componentProps }) + ); - addFn(Component); + refMap.set(component, hostComponent); + + addFn(hostComponent); return Promise.resolve(div); }; - const removeViewFromDom = () => { - Component && removeFn(Component); + const removeViewFromDom = (_container: any, component: any) => { + const hostComponent = refMap.get(component); + hostComponent && removeFn(hostComponent); + return Promise.resolve(); }; diff --git a/packages/vue/test/base/src/components/ModalContent.vue b/packages/vue/test/base/src/components/ModalContent.vue index e572706d20..48586c826b 100644 --- a/packages/vue/test/base/src/components/ModalContent.vue +++ b/packages/vue/test/base/src/components/ModalContent.vue @@ -9,7 +9,8 @@ - {{ title }} + {{ title }} + @@ -23,6 +24,7 @@ import { IonHeader, IonTitle, IonToolbar, + IonInput, modalController } from '@ionic/vue'; import { defineComponent } from 'vue'; @@ -38,7 +40,8 @@ export default defineComponent({ IonContent, IonHeader, IonTitle, - IonToolbar + IonToolbar, + IonInput }, setup() { const dismiss = async () => { diff --git a/packages/vue/test/base/tests/e2e/specs/overlays.cy.js b/packages/vue/test/base/tests/e2e/specs/overlays.cy.js index fcb0360d3b..14d836acdf 100644 --- a/packages/vue/test/base/tests/e2e/specs/overlays.cy.js +++ b/packages/vue/test/base/tests/e2e/specs/overlays.cy.js @@ -114,7 +114,7 @@ describe('Overlays', () => { cy.get('ion-button#present-overlay').click(); cy.get('ion-modal').should('exist'); - cy.get('ion-modal ion-content').should('have.text', 'Custom Title'); + cy.get('ion-modal ion-content #title').should('have.text', 'Custom Title'); }); it('should pass props to modal via component', () => { @@ -124,7 +124,7 @@ describe('Overlays', () => { cy.get('ion-button#present-overlay').click(); cy.get('ion-modal').should('exist'); - cy.get('ion-modal ion-content').should('have.text', 'Custom Title'); + cy.get('ion-modal ion-content #title').should('have.text', 'Custom Title'); }); it('should pass props to popover via controller', () => { @@ -195,6 +195,44 @@ describe('Overlays', () => { didDismiss: 2 }); }); + + it('should unmount modal via component', () => { + cy.get('ion-radio#ion-modal').click(); + cy.get('ion-radio#component').click(); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + cy.get('ion-modal ion-input').should('have.value', ''); + cy.get('ion-modal ion-input').type('1'); + + cy.get('ion-modal #dismiss').click(); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + cy.get('ion-modal ion-input').should('have.value', ''); + }); + + + it('should unmount modal via controller', () => { + cy.get('ion-radio#ion-modal').click(); + cy.get('ion-radio#controller').click(); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + cy.get('ion-modal ion-input').should('have.value', ''); + cy.get('ion-modal ion-input').type('1'); + + cy.get('ion-modal #dismiss').click(); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + cy.get('ion-modal ion-input').should('have.value', ''); + }); + }) const testLifecycle = (selector, expected = {}) => {