Skip to content

Commit 024d090

Browse files
authored
fix(vue): emit component-specific overlay events (#30688)
Issue number: resolves #30641 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> Currently, Vue modals do not emit `ionModal...` events. This happened due to a change in the way the stencil output targets for Vue changed. Christian [updated the overlays](https://github.com/ionic-team/ionic-framework/pull/30227/files#diff-7e46aba01094c4917cd55e8eebd263fc4a297a2d62143f1ae30959ec4e023b6f) to support the base events, but not the component-specific events. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> With this change, you'll be able to bind to the events as described in the Ionic documentation. ## Does this introduce a breaking change? - [ ] Yes - [X] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information Current dev build: ``` 8.7.5-dev.11758311583.14f4e9d9 ```
1 parent 36c56e7 commit 024d090

File tree

3 files changed

+201
-8
lines changed

3 files changed

+201
-8
lines changed

packages/vue/src/utils/overlays.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { VNode, ComponentOptions } from "vue";
2-
import { defineComponent, h, ref, onMounted } from "vue";
1+
import type { ComponentOptions, VNode } from "vue";
2+
import { defineComponent, h, onMounted, ref } from "vue";
33

44
// TODO(FW-2969): types
55

@@ -147,23 +147,32 @@ export const defineOverlayContainer = <Props extends object>(
147147
const elementRef = ref();
148148

149149
onMounted(() => {
150+
// Convert name from kebab-case to camelCase
151+
const componentName = name.replace(/-([a-z])/g, (_, letter) =>
152+
letter.toUpperCase()
153+
);
150154
elementRef.value.addEventListener("ionMount", (ev: Event) => {
151155
emit("ionMount", ev);
156+
emit(componentName + "IonMount", ev);
152157
isOpen.value = true;
153158
});
154159
elementRef.value.addEventListener("willPresent", (ev: Event) => {
155160
emit("willPresent", ev);
161+
emit(componentName + "WillPresent", ev);
156162
isOpen.value = true;
157163
});
158164
elementRef.value.addEventListener("didDismiss", (ev: Event) => {
159165
emit("didDismiss", ev);
166+
emit(componentName + "DidDismiss", ev);
160167
isOpen.value = false;
161168
});
162169
elementRef.value.addEventListener("willDismiss", (ev: Event) => {
163170
emit("willDismiss", ev);
171+
emit(componentName + "WillDismiss", ev);
164172
});
165173
elementRef.value.addEventListener("didPresent", (ev: Event) => {
166174
emit("didPresent", ev);
175+
emit(componentName + "DidPresent", ev);
167176
});
168177
});
169178

packages/vue/test/base/src/views/Overlays.vue

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@
6464
Modal onDidPresent: <div id="didPresent">{{ didPresent }}</div><br />
6565
Modal onWillDismiss: <div id="willDismiss">{{ willDismiss }}</div><br />
6666
Modal onDidDismiss: <div id="didDismiss">{{ didDismiss }}</div><br />
67+
Modal ionModalWillPresent: <div id="ionModalWillPresent">{{ ionModalWillPresent }}</div><br />
68+
Modal ionModalDidPresent: <div id="ionModalDidPresent">{{ ionModalDidPresent }}</div><br />
69+
Modal ionModalWillDismiss: <div id="ionModalWillDismiss">{{ ionModalWillDismiss }}</div><br />
70+
Modal ionModalDidDismiss: <div id="ionModalDidDismiss">{{ ionModalDidDismiss }}</div><br />
6771
</div>
6872

6973
<ion-action-sheet
@@ -98,6 +102,10 @@
98102
@didPresent="onModalDidPresent"
99103
@willDismiss="onModalWillDismiss"
100104
@didDismiss="onModalDidDismiss"
105+
@ionModalWillPresent="onIonModalWillPresent"
106+
@ionModalDidPresent="onIonModalDidPresent"
107+
@ionModalWillDismiss="onIonModalWillDismiss"
108+
@ionModalDidDismiss="onIonModalDidDismiss"
101109
>
102110
<ModalContent :title="overlayProps.title"></ModalContent>
103111
</ion-modal>
@@ -266,6 +274,19 @@ export default defineComponent({
266274
267275
const openModal = async () => {
268276
const modal = await modalController.create({ cssClass: "ion-modal-controller", component: ModalContent, componentProps: overlayProps });
277+
278+
// Attach lifecycle listeners for controller-created modal
279+
modal.addEventListener('willPresent', () => { willPresent.value += 1; });
280+
modal.addEventListener('didPresent', () => { didPresent.value += 1; });
281+
modal.addEventListener('willDismiss', () => { willDismiss.value += 1; });
282+
modal.addEventListener('didDismiss', () => { didDismiss.value += 1; });
283+
284+
// Long-form event names
285+
modal.addEventListener('ionModalWillPresent', () => { ionModalWillPresent.value += 1; });
286+
modal.addEventListener('ionModalDidPresent', () => { ionModalDidPresent.value += 1; });
287+
modal.addEventListener('ionModalWillDismiss', () => { ionModalWillDismiss.value += 1; });
288+
modal.addEventListener('ionModalDidDismiss', () => { ionModalDidDismiss.value += 1; });
289+
269290
await modal.present();
270291
}
271292
@@ -335,21 +356,37 @@ export default defineComponent({
335356
const didPresent = ref(0);
336357
const willDismiss = ref(0);
337358
const didDismiss = ref(0);
359+
const ionModalWillPresent = ref(0);
360+
const ionModalDidPresent = ref(0);
361+
const ionModalWillDismiss = ref(0);
362+
const ionModalDidDismiss = ref(0);
338363
339364
const onModalWillPresent = () => willPresent.value += 1;
340365
const onModalDidPresent = () => { didPresent.value += 1; setModalRef(true); }
341366
const onModalWillDismiss = () => willDismiss.value += 1;
342367
const onModalDidDismiss = () => { didDismiss.value += 1; setModalRef(false); }
368+
const onIonModalWillPresent = () => ionModalWillPresent.value += 1;
369+
const onIonModalDidPresent = () => ionModalDidPresent.value += 1;
370+
const onIonModalWillDismiss = () => ionModalWillDismiss.value += 1;
371+
const onIonModalDidDismiss = () => ionModalDidDismiss.value += 1;
343372
344373
return {
345374
onModalWillPresent,
346375
onModalDidPresent,
347376
onModalWillDismiss,
348377
onModalDidDismiss,
378+
onIonModalWillPresent,
379+
onIonModalDidPresent,
380+
onIonModalWillDismiss,
381+
onIonModalDidDismiss,
349382
willPresent,
350383
didPresent,
351384
willDismiss,
352385
didDismiss,
386+
ionModalWillPresent,
387+
ionModalDidPresent,
388+
ionModalWillDismiss,
389+
ionModalDidDismiss,
353390
changeLoadingProps,
354391
overlayProps,
355392
present,

packages/vue/test/base/tests/e2e/specs/overlays.cy.js

Lines changed: 153 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
const testController = (overlay, shadow = false) => {
22
const selector = `.${overlay}-controller`;
3-
cy.get(`ion-radio#${overlay}`).click();
4-
cy.get('ion-radio#controller').click();
3+
cy.get(`ion-radio#${overlay}`)
4+
.scrollIntoView({ offset: { top: -100, left: 0 } })
5+
.click({ force: true });
6+
cy.get('ion-radio#controller')
7+
.scrollIntoView({ offset: { top: -100, left: 0 } })
8+
.click({ force: true });
59

610
cy.get('ion-button#present-overlay').click();
711
cy.get(selector).should('exist').should('be.visible');
@@ -16,8 +20,12 @@ const testController = (overlay, shadow = false) => {
1620
}
1721

1822
const testComponent = (overlay, shadow = false) => {
19-
cy.get(`ion-radio#${overlay}`).click();
20-
cy.get('ion-radio#component').click();
23+
cy.get(`ion-radio#${overlay}`)
24+
.scrollIntoView({ offset: { top: -100, left: 0 } })
25+
.click({ force: true });
26+
cy.get('ion-radio#component')
27+
.scrollIntoView({ offset: { top: -100, left: 0 } })
28+
.click({ force: true });
2129

2230
cy.get('ion-button#present-overlay').click();
2331
cy.get(overlay).should('exist').should('be.visible');
@@ -40,8 +48,12 @@ const testComponent = (overlay, shadow = false) => {
4048
}
4149

4250
const testInlineOverlay = (overlay, shadow = false) => {
43-
cy.get(`ion-radio#${overlay}`).click();
44-
cy.get('ion-radio#component').click();
51+
cy.get(`ion-radio#${overlay}`)
52+
.scrollIntoView({ offset: { top: -100, left: 0 } })
53+
.click({ force: true });
54+
cy.get('ion-radio#component')
55+
.scrollIntoView({ offset: { top: -100, left: 0 } })
56+
.click({ force: true });
4557

4658
cy.get('ion-button#present-overlay').click();
4759
cy.get(overlay).should('exist').should('be.visible');
@@ -214,6 +226,135 @@ describe('Overlays', () => {
214226
});
215227
});
216228

229+
it('should fire long-form lifecycle events on overlays', () => {
230+
cy.get('ion-radio#ion-modal').click();
231+
cy.get('ion-radio#component').click();
232+
233+
cy.get('ion-button#present-overlay').click();
234+
cy.get('ion-modal').should('exist');
235+
236+
testLongLifecycle('overlays', {
237+
willPresent: 1,
238+
didPresent: 1,
239+
willDismiss: 0,
240+
didDismiss: 0
241+
});
242+
243+
cy.get('ion-modal #dismiss').click();
244+
245+
testLongLifecycle('overlays', {
246+
willPresent: 1,
247+
didPresent: 1,
248+
willDismiss: 1,
249+
didDismiss: 1
250+
});
251+
252+
cy.get('ion-button#present-overlay').click();
253+
cy.get('ion-modal').should('exist');
254+
255+
testLongLifecycle('overlays', {
256+
willPresent: 2,
257+
didPresent: 2,
258+
willDismiss: 1,
259+
didDismiss: 1
260+
});
261+
262+
cy.get('ion-modal #dismiss').click();
263+
264+
testLongLifecycle('overlays', {
265+
willPresent: 2,
266+
didPresent: 2,
267+
willDismiss: 2,
268+
didDismiss: 2
269+
});
270+
});
271+
272+
it('should fire lifecycle events on controller overlays', () => {
273+
cy.get('ion-radio#ion-modal').click();
274+
cy.get('ion-radio#controller').click();
275+
276+
cy.get('ion-button#present-overlay').click();
277+
cy.get('ion-modal').should('exist');
278+
279+
testLifecycle('overlays', {
280+
willPresent: 1,
281+
didPresent: 1,
282+
willDismiss: 0,
283+
didDismiss: 0
284+
});
285+
286+
cy.get('ion-modal #dismiss').click();
287+
288+
testLifecycle('overlays', {
289+
willPresent: 1,
290+
didPresent: 1,
291+
willDismiss: 1,
292+
didDismiss: 1
293+
});
294+
295+
cy.get('ion-button#present-overlay').click();
296+
cy.get('ion-modal').should('exist');
297+
298+
testLifecycle('overlays', {
299+
willPresent: 2,
300+
didPresent: 2,
301+
willDismiss: 1,
302+
didDismiss: 1
303+
});
304+
305+
cy.get('ion-modal #dismiss').click();
306+
307+
testLifecycle('overlays', {
308+
willPresent: 2,
309+
didPresent: 2,
310+
willDismiss: 2,
311+
didDismiss: 2
312+
});
313+
});
314+
315+
it('should fire long-form lifecycle events on controller overlays', () => {
316+
cy.get('ion-radio#ion-modal').click();
317+
cy.get('ion-radio#controller').click();
318+
319+
cy.get('ion-button#present-overlay').click();
320+
cy.get('ion-modal').should('exist');
321+
322+
testLongLifecycle('overlays', {
323+
willPresent: 1,
324+
didPresent: 1,
325+
willDismiss: 0,
326+
didDismiss: 0
327+
});
328+
329+
cy.get('ion-modal #dismiss').click();
330+
331+
testLongLifecycle('overlays', {
332+
willPresent: 1,
333+
didPresent: 1,
334+
willDismiss: 1,
335+
didDismiss: 1
336+
});
337+
338+
cy.get('ion-button#present-overlay').click();
339+
cy.get('ion-modal').should('exist');
340+
341+
testLongLifecycle('overlays', {
342+
willPresent: 2,
343+
didPresent: 2,
344+
willDismiss: 1,
345+
didDismiss: 1
346+
});
347+
348+
cy.get('ion-modal #dismiss').click();
349+
350+
testLongLifecycle('overlays', {
351+
willPresent: 2,
352+
didPresent: 2,
353+
willDismiss: 2,
354+
didDismiss: 2
355+
});
356+
});
357+
217358
it('should unmount modal via component', () => {
218359
cy.get('ion-radio#ion-modal').click();
219360
cy.get('ion-radio#component').click();
@@ -260,3 +401,9 @@ const testLifecycle = (selector, expected = {}) => {
260401
cy.get(`[data-pageid=${selector}] #didDismiss`).should('have.text', expected.didDismiss);
261402
}
262403

404+
const testLongLifecycle = (selector, expected = {}) => {
405+
cy.get(`[data-pageid=${selector}] #ionModalWillPresent`).should('have.text', expected.willPresent);
406+
cy.get(`[data-pageid=${selector}] #ionModalDidPresent`).should('have.text', expected.didPresent);
407+
cy.get(`[data-pageid=${selector}] #ionModalWillDismiss`).should('have.text', expected.willDismiss);
408+
cy.get(`[data-pageid=${selector}] #ionModalDidDismiss`).should('have.text', expected.didDismiss);
409+
}

0 commit comments

Comments
 (0)