fix(vue): account for event name changes in vue 3.0.6+ for overlay components (#23100)

This commit is contained in:
Liam DeBeasi
2021-03-29 15:30:31 -04:00
committed by GitHub
parent ba51daf17c
commit 27318cf585
6 changed files with 108 additions and 12 deletions

View File

@ -1,8 +1,7 @@
import { App, Plugin } from 'vue';
import { IonicConfig, setupConfig } from '@ionic/core';
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
const needsKebabCase = (version: string) => !['3.0.0', '3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5'].includes(version);
import { needsKebabCase } from './utils';
/**
* We need to make sure that the web component fires an event

View File

@ -57,3 +57,5 @@ export const getConfig = (): CoreConfig | 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);

View File

@ -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 {
isOpen?: boolean;
}
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 eventListeners = [
const lowerCaseListeners = [
{ componentEv: `${eventPrefix}willpresent`, frameworkEv: 'onWillPresent' },
{ componentEv: `${eventPrefix}didpresent`, frameworkEv: 'onDidPresent' },
{ componentEv: `${eventPrefix}willdismiss`, frameworkEv: 'onWillDismiss' },
{ 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 instance = getCurrentInstance();
const adjustedEventListeners = needsKebabCase(instance.appContext.app.version) ? kebabCaseListeners : lowerCaseListeners;
const overlay = ref();
const onVnodeMounted = async () => {
const isOpen = props.isOpen;
@ -76,7 +93,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
overlay.value = await overlay.value;
eventListeners.forEach(eventListener => {
adjustedEventListeners.forEach(eventListener => {
overlay.value.addEventListener(eventListener.componentEv, () => {
emit(eventListener.frameworkEv);
});
@ -101,7 +118,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, compo
Container.displayName = name;
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;
}

View File

@ -3,7 +3,7 @@
<ion-header>
<ion-toolbar>
<ion-buttons>
<ion-button @click="dismiss">Dismiss</ion-button>
<ion-button @click="dismiss" id="dismiss">Dismiss</ion-button>
</ion-buttons>
<ion-title>Modal</ion-title>
</ion-toolbar>

View File

@ -1,5 +1,5 @@
<template>
<ion-page>
<ion-page data-pageid="overlays">
<ion-header :translucent="true">
<ion-toolbar>
<ion-buttons>
@ -66,6 +66,13 @@
<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
:is-open="isActionSheetOpen"
:buttons="actionSheetButtons"
@ -93,7 +100,10 @@
<ion-modal
:is-open="isModalOpen"
:componentProps="overlayProps"
@onDidDismiss="setModalRef(false)"
@onWillPresent="onModalWillPresent"
@onDidPresent="onModalDidPresent"
@onWillDismiss="onModalWillDismiss"
@onDidDismiss="onModalDidDismiss"
>
<ModalContent></ModalContent>
</ion-modal>
@ -326,7 +336,25 @@ export default defineComponent({
}, 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 {
onModalWillPresent,
onModalDidPresent,
onModalWillDismiss,
onModalDidDismiss,
willPresent,
didPresent,
willDismiss,
didDismiss,
changeLoadingProps,
overlayProps,
present,

View File

@ -1,5 +1,6 @@
describe('Overlays', () => {
beforeEach(() => {
cy.viewport(1000, 900);
cy.visit('http://localhost:8080/overlays')
})
@ -7,7 +8,6 @@ describe('Overlays', () => {
for (let overlay of overlays) {
it(`should open and close ${overlay} via controller`, () => {
console.log(overlay)
cy.get(`ion-radio#${overlay}`).click();
cy.get('ion-radio#controller').click();
@ -34,7 +34,6 @@ describe('Overlays', () => {
for (let overlay of overlays) {
it(`should open and close ${overlay} via component`, () => {
console.log(overlay)
cy.get(`ion-radio#${overlay}`).click();
cy.get('ion-radio#component').click();
@ -104,4 +103,55 @@ describe('Overlays', () => {
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);
}