mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 10:01:59 +08:00
fix(vue): account for event name changes in vue 3.0.6+ for overlay components (#23100)
This commit is contained in:
@ -1,8 +1,7 @@
|
|||||||
import { App, Plugin } from 'vue';
|
import { App, Plugin } from 'vue';
|
||||||
import { IonicConfig, setupConfig } from '@ionic/core';
|
import { IonicConfig, setupConfig } from '@ionic/core';
|
||||||
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
|
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
|
||||||
|
import { needsKebabCase } from './utils';
|
||||||
const needsKebabCase = (version: string) => !['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5'].includes(version);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We need to make sure that the web component fires an event
|
* We need to make sure that the web component fires an event
|
||||||
|
@ -57,3 +57,5 @@ export const getConfig = (): CoreConfig | null => {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const needsKebabCase = (version: string) => !['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5'].includes(version);
|
||||||
|
@ -1,20 +1,37 @@
|
|||||||
import { defineComponent, h, ref, VNode } from 'vue';
|
import { defineComponent, h, ref, VNode, getCurrentInstance } from 'vue';
|
||||||
|
import { needsKebabCase } from '../utils';
|
||||||
|
|
||||||
export interface OverlayProps {
|
export interface OverlayProps {
|
||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defineOverlayContainer = <Props extends object>(name: string, componentProps: string[] = [], controller: any) => {
|
export const defineOverlayContainer = <Props extends object>(name: string, componentProps: string[] = [], controller: any) => {
|
||||||
// TODO
|
/**
|
||||||
|
* Vue 3.0.6 fixed a bug where events on custom elements
|
||||||
|
* were always converted to lower case, so "ionRefresh"
|
||||||
|
* became "ionRefresh". We need to account for the old
|
||||||
|
* issue as well as the new behavior where "ionRefresh"
|
||||||
|
* is converted to "ion-refresh".
|
||||||
|
* See https://github.com/vuejs/vue-next/pull/2847
|
||||||
|
*/
|
||||||
const eventPrefix = name.toLowerCase().split('-').join('');
|
const eventPrefix = name.toLowerCase().split('-').join('');
|
||||||
const eventListeners = [
|
const lowerCaseListeners = [
|
||||||
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'onWillPresent' },
|
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'onWillPresent' },
|
||||||
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'onDidPresent' },
|
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'onDidPresent' },
|
||||||
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'onWillDismiss' },
|
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'onWillDismiss' },
|
||||||
{ componentEv: `${eventPrefix}diddismiss`, frameworkEv: 'onDidDismiss' },
|
{ componentEv: `${eventPrefix}diddismiss`, frameworkEv: 'onDidDismiss' },
|
||||||
];
|
];
|
||||||
|
const kebabCaseListeners = [
|
||||||
|
{ componentEv: `${name}-will-present`, frameworkEv: 'onWillPresent' },
|
||||||
|
{ componentEv: `${name}-did-present`, frameworkEv: 'onDidPresent' },
|
||||||
|
{ componentEv: `${name}-will-dismiss`, frameworkEv: 'onWillDismiss' },
|
||||||
|
{ componentEv: `${name}-did-dismiss`, frameworkEv: 'onDidDismiss' },
|
||||||
|
];
|
||||||
|
|
||||||
const Container = defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
|
const Container = defineComponent<Props & OverlayProps>((props, { slots, emit }) => {
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
const adjustedEventListeners = needsKebabCase(instance.appContext.app.version) ? kebabCaseListeners : lowerCaseListeners;
|
||||||
|
|
||||||
const overlay = ref();
|
const overlay = ref();
|
||||||
const onVnodeMounted = async () => {
|
const onVnodeMounted = async () => {
|
||||||
const isOpen = props.isOpen;
|
const isOpen = props.isOpen;
|
||||||
@ -76,7 +93,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
|
|||||||
|
|
||||||
overlay.value = await overlay.value;
|
overlay.value = await overlay.value;
|
||||||
|
|
||||||
eventListeners.forEach(eventListener => {
|
adjustedEventListeners.forEach(eventListener => {
|
||||||
overlay.value.addEventListener(eventListener.componentEv, () => {
|
overlay.value.addEventListener(eventListener.componentEv, () => {
|
||||||
emit(eventListener.frameworkEv);
|
emit(eventListener.frameworkEv);
|
||||||
});
|
});
|
||||||
@ -101,7 +118,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
|
|||||||
|
|
||||||
Container.displayName = name;
|
Container.displayName = name;
|
||||||
Container.props = [...componentProps, 'isOpen'];
|
Container.props = [...componentProps, 'isOpen'];
|
||||||
Container.emits = eventListeners.map(ev => ev.frameworkEv);
|
Container.emits = [...lowerCaseListeners.map(ev => ev.frameworkEv), ...kebabCaseListeners.map(ev => ev.frameworkEv)];
|
||||||
|
|
||||||
return Container;
|
return Container;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-buttons>
|
<ion-buttons>
|
||||||
<ion-button @click="dismiss">Dismiss</ion-button>
|
<ion-button @click="dismiss" id="dismiss">Dismiss</ion-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<ion-title>Modal</ion-title>
|
<ion-title>Modal</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ion-page>
|
<ion-page data-pageid="overlays">
|
||||||
<ion-header :translucent="true">
|
<ion-header :translucent="true">
|
||||||
<ion-toolbar>
|
<ion-toolbar>
|
||||||
<ion-buttons>
|
<ion-buttons>
|
||||||
@ -66,6 +66,13 @@
|
|||||||
|
|
||||||
<ion-button @click="changeLoadingProps()" id="change-loading-props">Quickly Change Loading Props</ion-button>
|
<ion-button @click="changeLoadingProps()" id="change-loading-props">Quickly Change Loading Props</ion-button>
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
Modal onWillPresent: <div id="willPresent">{{ willPresent }}</div><br />
|
||||||
|
Modal onDidPresent: <div id="didPresent">{{ didPresent }}</div><br />
|
||||||
|
Modal onWillDismiss: <div id="willDismiss">{{ willDismiss }}</div><br />
|
||||||
|
Modal onDidDismiss: <div id="didDismiss">{{ didDismiss }}</div><br />
|
||||||
|
|
||||||
<ion-action-sheet
|
<ion-action-sheet
|
||||||
:is-open="isActionSheetOpen"
|
:is-open="isActionSheetOpen"
|
||||||
:buttons="actionSheetButtons"
|
:buttons="actionSheetButtons"
|
||||||
@ -93,7 +100,10 @@
|
|||||||
<ion-modal
|
<ion-modal
|
||||||
:is-open="isModalOpen"
|
:is-open="isModalOpen"
|
||||||
:componentProps="overlayProps"
|
:componentProps="overlayProps"
|
||||||
@onDidDismiss="setModalRef(false)"
|
@onWillPresent="onModalWillPresent"
|
||||||
|
@onDidPresent="onModalDidPresent"
|
||||||
|
@onWillDismiss="onModalWillDismiss"
|
||||||
|
@onDidDismiss="onModalDidDismiss"
|
||||||
>
|
>
|
||||||
<ModalContent></ModalContent>
|
<ModalContent></ModalContent>
|
||||||
</ion-modal>
|
</ion-modal>
|
||||||
@ -326,7 +336,25 @@ export default defineComponent({
|
|||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const willPresent = ref(0);
|
||||||
|
const didPresent = ref(0);
|
||||||
|
const willDismiss = ref(0);
|
||||||
|
const didDismiss = 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); }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
onModalWillPresent,
|
||||||
|
onModalDidPresent,
|
||||||
|
onModalWillDismiss,
|
||||||
|
onModalDidDismiss,
|
||||||
|
willPresent,
|
||||||
|
didPresent,
|
||||||
|
willDismiss,
|
||||||
|
didDismiss,
|
||||||
changeLoadingProps,
|
changeLoadingProps,
|
||||||
overlayProps,
|
overlayProps,
|
||||||
present,
|
present,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
describe('Overlays', () => {
|
describe('Overlays', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
cy.viewport(1000, 900);
|
||||||
cy.visit('http://localhost:8080/overlays')
|
cy.visit('http://localhost:8080/overlays')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -7,7 +8,6 @@ describe('Overlays', () => {
|
|||||||
|
|
||||||
for (let overlay of overlays) {
|
for (let overlay of overlays) {
|
||||||
it(`should open and close ${overlay} via controller`, () => {
|
it(`should open and close ${overlay} via controller`, () => {
|
||||||
console.log(overlay)
|
|
||||||
cy.get(`ion-radio#${overlay}`).click();
|
cy.get(`ion-radio#${overlay}`).click();
|
||||||
cy.get('ion-radio#controller').click();
|
cy.get('ion-radio#controller').click();
|
||||||
|
|
||||||
@ -34,7 +34,6 @@ describe('Overlays', () => {
|
|||||||
|
|
||||||
for (let overlay of overlays) {
|
for (let overlay of overlays) {
|
||||||
it(`should open and close ${overlay} via component`, () => {
|
it(`should open and close ${overlay} via component`, () => {
|
||||||
console.log(overlay)
|
|
||||||
cy.get(`ion-radio#${overlay}`).click();
|
cy.get(`ion-radio#${overlay}`).click();
|
||||||
cy.get('ion-radio#component').click();
|
cy.get('ion-radio#component').click();
|
||||||
|
|
||||||
@ -104,4 +103,55 @@ describe('Overlays', () => {
|
|||||||
|
|
||||||
cy.get('ion-loading').should('have.length', 1);
|
cy.get('ion-loading').should('have.length', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fire 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');
|
||||||
|
|
||||||
|
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
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const testLifecycle = (selector, expected = {}) => {
|
||||||
|
cy.get(`[data-pageid=${selector}] #willPresent`).should('have.text', expected.willPresent);
|
||||||
|
cy.get(`[data-pageid=${selector}] #didPresent`).should('have.text', expected.didPresent);
|
||||||
|
cy.get(`[data-pageid=${selector}] #willDismiss`).should('have.text', expected.willDismiss);
|
||||||
|
cy.get(`[data-pageid=${selector}] #didDismiss`).should('have.text', expected.didDismiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user