Skip to content

feat(vue): support autoMountComponent #25538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions angular/src/directives/overlays/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export declare interface IonModal extends Components.IonModal {
@ProxyCmp({
inputs: [
'animated',
'autoMountComponent',
'backdropBreakpoint',
'backdropDismiss',
'breakpoints',
Expand Down Expand Up @@ -81,6 +82,7 @@ export declare interface IonModal extends Components.IonModal {
template: `<div class="ion-page" *ngIf="isCmpOpen"><ng-container [ngTemplateOutlet]="template"></ng-container></div>`,
inputs: [
'animated',
'autoMountComponent',
'backdropBreakpoint',
'backdropDismiss',
'breakpoints',
Expand Down
2 changes: 2 additions & 0 deletions angular/src/directives/overlays/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export declare interface IonPopover extends Components.IonPopover {
'alignment',
'animated',
'arrow',
'autoMountComponent',
'backdropDismiss',
'cssClass',
'dismissOnSelect',
Expand Down Expand Up @@ -78,6 +79,7 @@ export declare interface IonPopover extends Components.IonPopover {
'alignment',
'animated',
'arrow',
'autoMountComponent',
'backdropDismiss',
'cssClass',
'dismissOnSelect',
Expand Down
2 changes: 2 additions & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,7 @@ ion-menu-toggle,prop,menu,string | undefined,undefined,false,false

ion-modal,shadow
ion-modal,prop,animated,boolean,true,false,false
ion-modal,prop,autoMountComponent,boolean,false,false,false
ion-modal,prop,backdropBreakpoint,number,0,false,false
ion-modal,prop,backdropDismiss,boolean,true,false,false
ion-modal,prop,breakpoints,number[] | undefined,undefined,false,false
Expand Down Expand Up @@ -887,6 +888,7 @@ ion-popover,shadow
ion-popover,prop,alignment,"center" | "end" | "start" | undefined,undefined,false,false
ion-popover,prop,animated,boolean,true,false,false
ion-popover,prop,arrow,boolean,true,false,false
ion-popover,prop,autoMountComponent,boolean,false,false,false
ion-popover,prop,backdropDismiss,boolean,true,false,false
ion-popover,prop,component,Function | HTMLElement | null | string | undefined,undefined,false,false
ion-popover,prop,componentProps,undefined | { [key: string]: any; },undefined,false,false
Expand Down
16 changes: 16 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,10 @@ export namespace Components {
* If `true`, the modal will animate.
*/
"animated": boolean;
/**
* If `true`, the component passed into `ion-modal` will automatically be mounted when the modal is created. The component will remain mounted even when the modal is dismissed. However, the component will be destroyed when the modal is destroyed. This property is not reactive and should only be used when initially creating a modal. Note: This feature only applies to inline modals in JavaScript frameworks such as Angular, React, and Vue.
*/
"autoMountComponent": boolean;
/**
* A decimal value between 0 and 1 that indicates the point after which the backdrop will begin to fade in when using a sheet modal. Prior to this point, the backdrop will be hidden and the content underneath the sheet can be interacted with. This value is exclusive meaning the backdrop will become active after the value specified.
*/
Expand Down Expand Up @@ -1890,6 +1894,10 @@ export namespace Components {
* If `true`, the popover will display an arrow that points at the `reference` when running in `ios` mode. Does not apply in `md` mode.
*/
"arrow": boolean;
/**
* If `true`, the component passed into `ion-popover` will automatically be mounted when the popover is created. The component will remain mounted even when the popover is dismissed. However, the component will be destroyed when the popover is destroyed. This property is not reactive and should only be used when initially creating a popover. Note: This feature only applies to inline popovers in JavaScript frameworks such as Angular, React, and Vue.
*/
"autoMountComponent": boolean;
/**
* If `true`, the popover will be dismissed when the backdrop is clicked.
*/
Expand Down Expand Up @@ -5434,6 +5442,10 @@ declare namespace LocalJSX {
* If `true`, the modal will animate.
*/
"animated"?: boolean;
/**
* If `true`, the component passed into `ion-modal` will automatically be mounted when the modal is created. The component will remain mounted even when the modal is dismissed. However, the component will be destroyed when the modal is destroyed. This property is not reactive and should only be used when initially creating a modal. Note: This feature only applies to inline modals in JavaScript frameworks such as Angular, React, and Vue.
*/
"autoMountComponent"?: boolean;
/**
* A decimal value between 0 and 1 that indicates the point after which the backdrop will begin to fade in when using a sheet modal. Prior to this point, the backdrop will be hidden and the content underneath the sheet can be interacted with. This value is exclusive meaning the backdrop will become active after the value specified.
*/
Expand Down Expand Up @@ -5737,6 +5749,10 @@ declare namespace LocalJSX {
* If `true`, the popover will display an arrow that points at the `reference` when running in `ios` mode. Does not apply in `md` mode.
*/
"arrow"?: boolean;
/**
* If `true`, the component passed into `ion-popover` will automatically be mounted when the popover is created. The component will remain mounted even when the popover is dismissed. However, the component will be destroyed when the popover is destroyed. This property is not reactive and should only be used when initially creating a popover. Note: This feature only applies to inline popovers in JavaScript frameworks such as Angular, React, and Vue.
*/
"autoMountComponent"?: boolean;
/**
* If `true`, the popover will be dismissed when the backdrop is clicked.
*/
Expand Down
13 changes: 13 additions & 0 deletions core/src/components/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,19 @@ export class Modal implements ComponentInterface, OverlayInterface {
this.configureTriggerInteraction();
}

/**
* If `true`, the component passed into `ion-modal` will
* automatically be mounted when the modal is created. The
* component will remain mounted even when the modal is dismissed.
* However, the component will be destroyed when the modal is
* destroyed. This property is not reactive and should only be
* used when initially creating a modal.
*
* Note: This feature only applies to inline modals in JavaScript
* frameworks such as Angular, React, and Vue.
*/
@Prop() autoMountComponent = false;

/**
* TODO (FW-937)
* This needs to default to true in the next
Expand Down
13 changes: 13 additions & 0 deletions core/src/components/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,19 @@ export class Popover implements ComponentInterface, PopoverInterface {
}
}

/**
* If `true`, the component passed into `ion-popover` will
* automatically be mounted when the popover is created. The
* component will remain mounted even when the popover is dismissed.
* However, the component will be destroyed when the popover is
* destroyed. This property is not reactive and should only be
* used when initially creating a popover.
*
* Note: This feature only applies to inline popovers in JavaScript
* frameworks such as Angular, React, and Vue.
*/
@Prop() autoMountComponent = false;

/**
* Emitted after the popover has presented.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/vue/src/components/Overlays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const IonPicker = /*@__PURE__*/ defineOverlayContainer<JSX.IonPicker>('io

export const IonToast = /*@__PURE__*/ defineOverlayContainer<JSX.IonToast>('ion-toast', defineIonToastCustomElement, ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'keyboardClose', 'leaveAnimation', 'message', 'mode', 'position', 'translucent'], toastController);

export const IonModal = /*@__PURE__*/ defineOverlayContainer<JSX.IonModal>('ion-modal', defineIonModalCustomElement, ['animated', 'backdropBreakpoint', 'backdropDismiss', 'breakpoints', 'canDismiss', 'enterAnimation', 'handle', 'htmlAttributes', 'initialBreakpoint', 'isOpen', 'keyboardClose', 'leaveAnimation', 'mode', 'presentingElement', 'showBackdrop', 'swipeToClose', 'trigger']);
export const IonModal = /*@__PURE__*/ defineOverlayContainer<JSX.IonModal>('ion-modal', defineIonModalCustomElement, ['animated', 'autoMountComponent', 'backdropBreakpoint', 'backdropDismiss', 'breakpoints', 'canDismiss', 'enterAnimation', 'handle', 'htmlAttributes', 'initialBreakpoint', 'isOpen', 'keyboardClose', 'leaveAnimation', 'mode', 'presentingElement', 'showBackdrop', 'swipeToClose', 'trigger']);

export const IonPopover = /*@__PURE__*/ defineOverlayContainer<JSX.IonPopover>('ion-popover', defineIonPopoverCustomElement, ['alignment', 'animated', 'arrow', 'backdropDismiss', 'component', 'componentProps', 'dismissOnSelect', 'enterAnimation', 'event', 'htmlAttributes', 'isOpen', 'keyboardClose', 'leaveAnimation', 'mode', 'reference', 'showBackdrop', 'side', 'size', 'translucent', 'trigger', 'triggerAction']);
export const IonPopover = /*@__PURE__*/ defineOverlayContainer<JSX.IonPopover>('ion-popover', defineIonPopoverCustomElement, ['alignment', 'animated', 'arrow', 'autoMountComponent', 'backdropDismiss', 'component', 'componentProps', 'dismissOnSelect', 'enterAnimation', 'event', 'htmlAttributes', 'isOpen', 'keyboardClose', 'leaveAnimation', 'mode', 'reference', 'showBackdrop', 'side', 'size', 'translucent', 'trigger', 'triggerAction']);

2 changes: 1 addition & 1 deletion packages/vue/src/vue-component-lib/overlays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, defin
return h(
name,
{ ...restOfProps, ref: elementRef },
(isOpen.value) ? slots : undefined
(isOpen.value || restOfProps.autoMountComponent) ? slots : undefined
)
}
});
Expand Down
8 changes: 7 additions & 1 deletion packages/vue/test-app/src/components/PopoverContent.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<template>
<ion-content class="ion-padding">
{{ title }}
<ion-button id="dismiss" @click="dismiss">Dismiss</ion-button> <br />

<div id="title">
{{ title }}
</div>
</ion-content>
</template>

<script lang="ts">
import {
IonButton,
IonContent,
popoverController
} from '@ionic/vue';
Expand All @@ -16,6 +21,7 @@ export default defineComponent({
title: { type: String, default: 'Default Title' }
},
components: {
IonButton,
IonContent
},
setup() {
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/test-app/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const routes: Array<RouteRecordRaw> = [
path: '/overlays',
component: () => import('@/views/Overlays.vue')
},
{
path: '/overlays-auto-mount',
component: () => import('@/views/OverlaysAutoMount.vue')
},
{
path: '/inputs',
component: () => import('@/views/Inputs.vue')
Expand Down
81 changes: 81 additions & 0 deletions packages/vue/test-app/src/views/OverlaysAutoMount.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<ion-page data-pageid="overlays-automount">
<ion-header :translucent="true">
<ion-toolbar>
<ion-buttons>
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Overlays - Auto Mount</ion-title>
</ion-toolbar>
</ion-header>

<ion-content class="ion-padding" :fullscreen="true">
<ion-button id="open-auto-mount-modal">Open Auto Mount Modal</ion-button>

<ion-button id="open-auto-mount-popover">Open Auto Mount Popover</ion-button>

<br /><br />

<ion-modal
id="auto-mount-modal"
:auto-mount-component="true"
trigger="open-auto-mount-modal"
>
<ModalContent :title="overlayProps.title"></ModalContent>
</ion-modal>

<ion-popover
id="auto-mount-popover"
:auto-mount-component="true"
trigger="open-auto-mount-popover"
>
<PopoverContent :title="overlayProps.title"></PopoverContent>
</ion-popover>
</ion-content>
</ion-page>
</template>

<script lang="ts">
import {
IonBackButton,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonModal,
IonPopover,
} from '@ionic/vue';
import { defineComponent, ref } from 'vue';
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';
import ModalContent from '@/components/ModalContent.vue';
import PopoverContent from '@/components/PopoverContent.vue';

export default defineComponent({
components: {
ModalContent,
PopoverContent,
IonBackButton,
IonButton,
IonButtons,
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonModal,
IonPopover,
},
setup() {
const overlayProps = {
title: 'Custom Title'
}

return {
overlayProps
}
}
});
</script>
52 changes: 52 additions & 0 deletions packages/vue/test-app/tests/e2e/specs/overlays-auto-mount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
describe('overlays - autoMountComponent', () => {
beforeEach(() => {
cy.viewport(1000, 900);
cy.visit('http://localhost:8080/overlays-auto-mount')
})
describe('modal', () => {
it('should not mount component if false', () => {
cy.get('ion-modal#default-modal ion-content').should('not.exist');
});

it('should mount component if true', () => {
cy.get('ion-modal#auto-mount-modal ion-content').should('exist');
});

it('should keep component mounted after dismissing if true', () => {
cy.get('#open-auto-mount-modal').click();

cy.get('ion-modal#auto-mount-modal ion-content').should('exist');

cy.get('ion-modal#auto-mount-modal #dismiss').click();

cy.get('ion-modal#auto-mount-modal')
.should('not.be.visible')
.should('have.class', 'overlay-hidden');

cy.get('ion-modal#auto-mount-modal ion-content').should('exist');
});
})
describe('popover', () => {
it('should not mount component if false', () => {
cy.get('ion-popover#default-popover ion-content').should('not.exist');
});

it('should mount component if true', () => {
cy.get('ion-popover#auto-mount-popover ion-content').should('exist');
});

it('should keep component mounted after dismissing if true', () => {
cy.get('#open-auto-mount-popover').click();

cy.get('ion-popover#auto-mount-popover ion-content').should('exist');

cy.get('ion-popover#auto-mount-popover #dismiss').click();

cy.get('ion-popover#auto-mount-popover')
.should('not.be.visible')
.should('have.class', 'overlay-hidden');

cy.get('ion-popover#auto-mount-popover ion-content').should('exist');
});
})
})
4 changes: 2 additions & 2 deletions packages/vue/test-app/tests/e2e/specs/overlays.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ describe('Overlays', () => {
cy.get('ion-button#present-overlay').click();
cy.get('ion-popover.ion-popover-controller').should('exist');

cy.get('ion-popover.ion-popover-controller ion-content').should('have.text', 'Custom Title');
cy.get('ion-popover.ion-popover-controller ion-content #title').should('have.text', 'Custom Title');
});

it('should pass props to popover via component', () => {
Expand All @@ -144,7 +144,7 @@ describe('Overlays', () => {
cy.get('ion-button#present-overlay').click();
cy.get('ion-popover').should('exist');

cy.get('ion-popover.popover-inline ion-content').should('have.text', 'Custom Title');
cy.get('ion-popover.popover-inline ion-content #title').should('have.text', 'Custom Title');
});

it('should only open one instance at a time when props change quickly on component', () => {
Expand Down