diff --git a/packages/vue/src/utils/overlays.ts b/packages/vue/src/utils/overlays.ts index 91133e3490a..b917e750541 100644 --- a/packages/vue/src/utils/overlays.ts +++ b/packages/vue/src/utils/overlays.ts @@ -1,5 +1,5 @@ -import type { VNode, ComponentOptions } from "vue"; -import { defineComponent, h, ref, onMounted } from "vue"; +import type { ComponentOptions, VNode } from "vue"; +import { defineComponent, h, onMounted, ref } from "vue"; // TODO(FW-2969): types @@ -147,23 +147,32 @@ export const defineOverlayContainer = ( const elementRef = ref(); onMounted(() => { + // Convert name from kebab-case to camelCase + const componentName = name.replace(/-([a-z])/g, (_, letter) => + letter.toUpperCase() + ); elementRef.value.addEventListener("ionMount", (ev: Event) => { emit("ionMount", ev); + emit(componentName + "IonMount", ev); isOpen.value = true; }); elementRef.value.addEventListener("willPresent", (ev: Event) => { emit("willPresent", ev); + emit(componentName + "WillPresent", ev); isOpen.value = true; }); elementRef.value.addEventListener("didDismiss", (ev: Event) => { emit("didDismiss", ev); + emit(componentName + "DidDismiss", ev); isOpen.value = false; }); elementRef.value.addEventListener("willDismiss", (ev: Event) => { emit("willDismiss", ev); + emit(componentName + "WillDismiss", ev); }); elementRef.value.addEventListener("didPresent", (ev: Event) => { emit("didPresent", ev); + emit(componentName + "DidPresent", ev); }); }); diff --git a/packages/vue/test/base/src/views/Overlays.vue b/packages/vue/test/base/src/views/Overlays.vue index 49b956e0ddd..0b382b94b47 100644 --- a/packages/vue/test/base/src/views/Overlays.vue +++ b/packages/vue/test/base/src/views/Overlays.vue @@ -64,6 +64,10 @@ Modal onDidPresent:
{{ didPresent }}

Modal onWillDismiss:
{{ willDismiss }}

Modal onDidDismiss:
{{ didDismiss }}

+ Modal ionModalWillPresent:
{{ ionModalWillPresent }}

+ Modal ionModalDidPresent:
{{ ionModalDidPresent }}

+ Modal ionModalWillDismiss:
{{ ionModalWillDismiss }}

+ Modal ionModalDidDismiss:
{{ ionModalDidDismiss }}

@@ -266,6 +274,19 @@ export default defineComponent({ const openModal = async () => { const modal = await modalController.create({ cssClass: "ion-modal-controller", component: ModalContent, componentProps: overlayProps }); + + // Attach lifecycle listeners for controller-created modal + modal.addEventListener('willPresent', () => { willPresent.value += 1; }); + modal.addEventListener('didPresent', () => { didPresent.value += 1; }); + modal.addEventListener('willDismiss', () => { willDismiss.value += 1; }); + modal.addEventListener('didDismiss', () => { didDismiss.value += 1; }); + + // Long-form event names + modal.addEventListener('ionModalWillPresent', () => { ionModalWillPresent.value += 1; }); + modal.addEventListener('ionModalDidPresent', () => { ionModalDidPresent.value += 1; }); + modal.addEventListener('ionModalWillDismiss', () => { ionModalWillDismiss.value += 1; }); + modal.addEventListener('ionModalDidDismiss', () => { ionModalDidDismiss.value += 1; }); + await modal.present(); } @@ -335,21 +356,37 @@ export default defineComponent({ const didPresent = ref(0); const willDismiss = ref(0); const didDismiss = ref(0); + const ionModalWillPresent = ref(0); + const ionModalDidPresent = ref(0); + const ionModalWillDismiss = ref(0); + const ionModalDidDismiss = ref(0); const onModalWillPresent = () => willPresent.value += 1; const onModalDidPresent = () => { didPresent.value += 1; setModalRef(true); } const onModalWillDismiss = () => willDismiss.value += 1; const onModalDidDismiss = () => { didDismiss.value += 1; setModalRef(false); } + const onIonModalWillPresent = () => ionModalWillPresent.value += 1; + const onIonModalDidPresent = () => ionModalDidPresent.value += 1; + const onIonModalWillDismiss = () => ionModalWillDismiss.value += 1; + const onIonModalDidDismiss = () => ionModalDidDismiss.value += 1; return { onModalWillPresent, onModalDidPresent, onModalWillDismiss, onModalDidDismiss, + onIonModalWillPresent, + onIonModalDidPresent, + onIonModalWillDismiss, + onIonModalDidDismiss, willPresent, didPresent, willDismiss, didDismiss, + ionModalWillPresent, + ionModalDidPresent, + ionModalWillDismiss, + ionModalDidDismiss, changeLoadingProps, overlayProps, present, 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 79f4ff4efb1..8d34036799b 100644 --- a/packages/vue/test/base/tests/e2e/specs/overlays.cy.js +++ b/packages/vue/test/base/tests/e2e/specs/overlays.cy.js @@ -1,7 +1,11 @@ const testController = (overlay, shadow = false) => { const selector = `.${overlay}-controller`; - cy.get(`ion-radio#${overlay}`).click(); - cy.get('ion-radio#controller').click(); + cy.get(`ion-radio#${overlay}`) + .scrollIntoView({ offset: { top: -100, left: 0 } }) + .click({ force: true }); + cy.get('ion-radio#controller') + .scrollIntoView({ offset: { top: -100, left: 0 } }) + .click({ force: true }); cy.get('ion-button#present-overlay').click(); cy.get(selector).should('exist').should('be.visible'); @@ -16,8 +20,12 @@ const testController = (overlay, shadow = false) => { } const testComponent = (overlay, shadow = false) => { - cy.get(`ion-radio#${overlay}`).click(); - cy.get('ion-radio#component').click(); + cy.get(`ion-radio#${overlay}`) + .scrollIntoView({ offset: { top: -100, left: 0 } }) + .click({ force: true }); + cy.get('ion-radio#component') + .scrollIntoView({ offset: { top: -100, left: 0 } }) + .click({ force: true }); cy.get('ion-button#present-overlay').click(); cy.get(overlay).should('exist').should('be.visible'); @@ -40,8 +48,12 @@ const testComponent = (overlay, shadow = false) => { } const testInlineOverlay = (overlay, shadow = false) => { - cy.get(`ion-radio#${overlay}`).click(); - cy.get('ion-radio#component').click(); + cy.get(`ion-radio#${overlay}`) + .scrollIntoView({ offset: { top: -100, left: 0 } }) + .click({ force: true }); + cy.get('ion-radio#component') + .scrollIntoView({ offset: { top: -100, left: 0 } }) + .click({ force: true }); cy.get('ion-button#present-overlay').click(); cy.get(overlay).should('exist').should('be.visible'); @@ -214,6 +226,135 @@ describe('Overlays', () => { }); }); + it('should fire long-form lifecycle events on overlays', () => { + 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'); + + testLongLifecycle('overlays', { + willPresent: 1, + didPresent: 1, + willDismiss: 0, + didDismiss: 0 + }); + + cy.get('ion-modal #dismiss').click(); + + testLongLifecycle('overlays', { + willPresent: 1, + didPresent: 1, + willDismiss: 1, + didDismiss: 1 + }); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + testLongLifecycle('overlays', { + willPresent: 2, + didPresent: 2, + willDismiss: 1, + didDismiss: 1 + }); + + cy.get('ion-modal #dismiss').click(); + + testLongLifecycle('overlays', { + willPresent: 2, + didPresent: 2, + willDismiss: 2, + didDismiss: 2 + }); + }); + + it('should fire lifecycle events on controller overlays', () => { + 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'); + + testLifecycle('overlays', { + willPresent: 1, + didPresent: 1, + willDismiss: 0, + didDismiss: 0 + }); + + cy.get('ion-modal #dismiss').click(); + + testLifecycle('overlays', { + willPresent: 1, + didPresent: 1, + willDismiss: 1, + didDismiss: 1 + }); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + testLifecycle('overlays', { + willPresent: 2, + didPresent: 2, + willDismiss: 1, + didDismiss: 1 + }); + + cy.get('ion-modal #dismiss').click(); + + testLifecycle('overlays', { + willPresent: 2, + didPresent: 2, + willDismiss: 2, + didDismiss: 2 + }); + }); + + it('should fire long-form lifecycle events on controller overlays', () => { + 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'); + + testLongLifecycle('overlays', { + willPresent: 1, + didPresent: 1, + willDismiss: 0, + didDismiss: 0 + }); + + cy.get('ion-modal #dismiss').click(); + + testLongLifecycle('overlays', { + willPresent: 1, + didPresent: 1, + willDismiss: 1, + didDismiss: 1 + }); + + cy.get('ion-button#present-overlay').click(); + cy.get('ion-modal').should('exist'); + + testLongLifecycle('overlays', { + willPresent: 2, + didPresent: 2, + willDismiss: 1, + didDismiss: 1 + }); + + cy.get('ion-modal #dismiss').click(); + + testLongLifecycle('overlays', { + willPresent: 2, + didPresent: 2, + willDismiss: 2, + didDismiss: 2 + }); + }); + it('should unmount modal via component', () => { cy.get('ion-radio#ion-modal').click(); cy.get('ion-radio#component').click(); @@ -260,3 +401,9 @@ const testLifecycle = (selector, expected = {}) => { cy.get(`[data-pageid=${selector}] #didDismiss`).should('have.text', expected.didDismiss); } +const testLongLifecycle = (selector, expected = {}) => { + cy.get(`[data-pageid=${selector}] #ionModalWillPresent`).should('have.text', expected.willPresent); + cy.get(`[data-pageid=${selector}] #ionModalDidPresent`).should('have.text', expected.didPresent); + cy.get(`[data-pageid=${selector}] #ionModalWillDismiss`).should('have.text', expected.willDismiss); + cy.get(`[data-pageid=${selector}] #ionModalDidDismiss`).should('have.text', expected.didDismiss); +}