mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 01:52:19 +08:00
feat(angular, react, vue): add support for autoMountComponent (#25552)
This commit is contained in:
@ -54,6 +54,7 @@ export declare interface IonModal extends Components.IonModal {
|
|||||||
@ProxyCmp({
|
@ProxyCmp({
|
||||||
inputs: [
|
inputs: [
|
||||||
'animated',
|
'animated',
|
||||||
|
'keepContentsMounted',
|
||||||
'backdropBreakpoint',
|
'backdropBreakpoint',
|
||||||
'backdropDismiss',
|
'backdropDismiss',
|
||||||
'breakpoints',
|
'breakpoints',
|
||||||
@ -78,9 +79,12 @@ export declare interface IonModal extends Components.IonModal {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-modal',
|
selector: 'ion-modal',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
template: `<div class="ion-page" *ngIf="isCmpOpen"><ng-container [ngTemplateOutlet]="template"></ng-container></div>`,
|
template: `<div class="ion-page" *ngIf="isCmpOpen || keepContentsMounted">
|
||||||
|
<ng-container [ngTemplateOutlet]="template"></ng-container>
|
||||||
|
</div>`,
|
||||||
inputs: [
|
inputs: [
|
||||||
'animated',
|
'animated',
|
||||||
|
'keepContentsMounted',
|
||||||
'backdropBreakpoint',
|
'backdropBreakpoint',
|
||||||
'backdropDismiss',
|
'backdropDismiss',
|
||||||
'breakpoints',
|
'breakpoints',
|
||||||
|
@ -51,6 +51,7 @@ export declare interface IonPopover extends Components.IonPopover {
|
|||||||
'alignment',
|
'alignment',
|
||||||
'animated',
|
'animated',
|
||||||
'arrow',
|
'arrow',
|
||||||
|
'keepContentsMounted',
|
||||||
'backdropDismiss',
|
'backdropDismiss',
|
||||||
'cssClass',
|
'cssClass',
|
||||||
'dismissOnSelect',
|
'dismissOnSelect',
|
||||||
@ -73,11 +74,12 @@ export declare interface IonPopover extends Components.IonPopover {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-popover',
|
selector: 'ion-popover',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
|
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen || keepContentsMounted"></ng-container>`,
|
||||||
inputs: [
|
inputs: [
|
||||||
'alignment',
|
'alignment',
|
||||||
'animated',
|
'animated',
|
||||||
'arrow',
|
'arrow',
|
||||||
|
'keepContentsMounted',
|
||||||
'backdropDismiss',
|
'backdropDismiss',
|
||||||
'cssClass',
|
'cssClass',
|
||||||
'dismissOnSelect',
|
'dismissOnSelect',
|
||||||
|
60
angular/test/test-app/e2e/src/keep-contents-mounted.spec.ts
Normal file
60
angular/test/test-app/e2e/src/keep-contents-mounted.spec.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
describe('overlays - keepContentsMounted', () => {
|
||||||
|
describe('modal', () => {
|
||||||
|
it('should not mount component if false', () => {
|
||||||
|
cy.visit('/modal-inline');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mount component if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep component mounted after dismissing if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('#open-modal').click();
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('exist');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-button').click();
|
||||||
|
|
||||||
|
cy.get('ion-modal')
|
||||||
|
.should('not.be.visible')
|
||||||
|
.should('have.class', 'overlay-hidden');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('exist');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
describe('popover', () => {
|
||||||
|
it('should not mount component if false', () => {
|
||||||
|
cy.visit('/popover-inline');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mount component if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep component mounted after dismissing if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('#open-popover').click();
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('exist');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-button').click();
|
||||||
|
|
||||||
|
cy.get('ion-popover')
|
||||||
|
.should('not.be.visible')
|
||||||
|
.should('have.class', 'overlay-hidden');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('exist');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -4,10 +4,17 @@ describe('Popovers: Inline', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should initially have no items', () => {
|
it('should initially have no items', () => {
|
||||||
|
cy.get('ion-button').click();
|
||||||
|
|
||||||
|
cy.get('ion-popover').should('be.visible');
|
||||||
cy.get('ion-list ion-item').should('not.exist');
|
cy.get('ion-list ion-item').should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have items after 1500ms', () => {
|
it('should have items after 1500ms', () => {
|
||||||
|
cy.get('ion-button').click();
|
||||||
|
|
||||||
|
cy.get('ion-popover').should('be.visible');
|
||||||
|
|
||||||
cy.wait(1500);
|
cy.wait(1500);
|
||||||
|
|
||||||
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
|
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
|
||||||
|
@ -31,6 +31,7 @@ const routes: Routes = [
|
|||||||
{ path: 'modals', component: ModalComponent },
|
{ path: 'modals', component: ModalComponent },
|
||||||
{ path: 'modal-inline', loadChildren: () => import('./modal-inline').then(m => m.ModalInlineModule) },
|
{ path: 'modal-inline', loadChildren: () => import('./modal-inline').then(m => m.ModalInlineModule) },
|
||||||
{ path: 'view-child', component: ViewChildComponent },
|
{ path: 'view-child', component: ViewChildComponent },
|
||||||
|
{ path: 'keep-contents-mounted', loadChildren: () => import('./keep-contents-mounted').then(m => m.OverlayAutoMountModule) },
|
||||||
{ path: 'popover-inline', loadChildren: () => import('./popover-inline').then(m => m.PopoverInlineModule) },
|
{ path: 'popover-inline', loadChildren: () => import('./popover-inline').then(m => m.PopoverInlineModule) },
|
||||||
{ path: 'providers', component: ProvidersComponent },
|
{ path: 'providers', component: ProvidersComponent },
|
||||||
{ path: 'router-link', component: RouterLinkComponent },
|
{ path: 'router-link', component: RouterLinkComponent },
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from './keep-contents-mounted.component';
|
||||||
|
export * from './keep-contents-mounted.module';
|
@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
import { RouterModule } from "@angular/router";
|
||||||
|
import { OverlayKeepContentsMounted } from ".";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild([
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: OverlayKeepContentsMounted
|
||||||
|
}
|
||||||
|
])
|
||||||
|
],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class OverlayKeepContentsMountedRoutingModule { }
|
@ -0,0 +1,22 @@
|
|||||||
|
<ion-content>
|
||||||
|
<ion-button id="open-modal" (click)="modal.present()">Open Modal</ion-button>
|
||||||
|
<ion-button id="open-popover" (click)="popover.present()">Open Popover</ion-button>
|
||||||
|
|
||||||
|
<ion-modal [keepContentsMounted]="true" #modal>
|
||||||
|
<ng-template>
|
||||||
|
<ion-content>
|
||||||
|
<ion-button (click)="modal.dismiss()">Dismiss</ion-button>
|
||||||
|
Modal Content
|
||||||
|
</ion-content>
|
||||||
|
</ng-template>
|
||||||
|
</ion-modal>
|
||||||
|
|
||||||
|
<ion-popover [keepContentsMounted]="true" #popover>
|
||||||
|
<ng-template>
|
||||||
|
<ion-content>
|
||||||
|
<ion-button (click)="popover.dismiss()">Dismiss</ion-button>
|
||||||
|
Popover Content
|
||||||
|
</ion-content>
|
||||||
|
</ng-template>
|
||||||
|
</ion-popover>
|
||||||
|
</ion-content>
|
@ -0,0 +1,13 @@
|
|||||||
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that inline modals correctly mount
|
||||||
|
* inner components when keepContentsMounted is
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'app-keep-contents-mounted',
|
||||||
|
templateUrl: 'keep-contents-mounted.component.html'
|
||||||
|
})
|
||||||
|
export class OverlayKeepContentsMounted {
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
import { IonicModule } from "@ionic/angular";
|
||||||
|
import { OverlayKeepContentsMountedRoutingModule } from "./keep-contents-mounted-routing.module";
|
||||||
|
import { OverlayKeepContentsMounted } from "./keep-contents-mounted.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule, IonicModule, OverlayKeepContentsMountedRoutingModule],
|
||||||
|
declarations: [OverlayKeepContentsMounted],
|
||||||
|
exports: [OverlayKeepContentsMounted]
|
||||||
|
})
|
||||||
|
export class OverlayAutoMountModule { }
|
@ -1,4 +1,6 @@
|
|||||||
<ion-popover [isOpen]="true">
|
<ion-button (click)="openPopover(popover)">Open Popover</ion-button>
|
||||||
|
|
||||||
|
<ion-popover #popover>
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AfterViewInit, Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that inline popovers will correctly display
|
* Validates that inline popovers will correctly display
|
||||||
@ -9,11 +9,13 @@ import { AfterViewInit, Component } from "@angular/core";
|
|||||||
selector: 'app-popover-inline',
|
selector: 'app-popover-inline',
|
||||||
templateUrl: 'popover-inline.component.html'
|
templateUrl: 'popover-inline.component.html'
|
||||||
})
|
})
|
||||||
export class PopoverInlineComponent implements AfterViewInit {
|
export class PopoverInlineComponent {
|
||||||
|
|
||||||
items: string[] = [];
|
items: string[] = [];
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
openPopover(popover: HTMLIonPopoverElement) {
|
||||||
|
popover.present();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.items = ['A', 'B', 'C', 'D'];
|
this.items = ['A', 'B', 'C', 'D'];
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
@ -776,6 +776,7 @@ ion-modal,prop,handle,boolean | undefined,undefined,false,false
|
|||||||
ion-modal,prop,htmlAttributes,undefined | { [key: string]: any; },undefined,false,false
|
ion-modal,prop,htmlAttributes,undefined | { [key: string]: any; },undefined,false,false
|
||||||
ion-modal,prop,initialBreakpoint,number | undefined,undefined,false,false
|
ion-modal,prop,initialBreakpoint,number | undefined,undefined,false,false
|
||||||
ion-modal,prop,isOpen,boolean,false,false,false
|
ion-modal,prop,isOpen,boolean,false,false,false
|
||||||
|
ion-modal,prop,keepContentsMounted,boolean,false,false,false
|
||||||
ion-modal,prop,keyboardClose,boolean,true,false,false
|
ion-modal,prop,keyboardClose,boolean,true,false,false
|
||||||
ion-modal,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
|
ion-modal,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
|
||||||
ion-modal,prop,mode,"ios" | "md",undefined,false,false
|
ion-modal,prop,mode,"ios" | "md",undefined,false,false
|
||||||
@ -895,6 +896,7 @@ ion-popover,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undef
|
|||||||
ion-popover,prop,event,any,undefined,false,false
|
ion-popover,prop,event,any,undefined,false,false
|
||||||
ion-popover,prop,htmlAttributes,undefined | { [key: string]: any; },undefined,false,false
|
ion-popover,prop,htmlAttributes,undefined | { [key: string]: any; },undefined,false,false
|
||||||
ion-popover,prop,isOpen,boolean,false,false,false
|
ion-popover,prop,isOpen,boolean,false,false,false
|
||||||
|
ion-popover,prop,keepContentsMounted,boolean,false,false,false
|
||||||
ion-popover,prop,keyboardClose,boolean,true,false,false
|
ion-popover,prop,keyboardClose,boolean,true,false,false
|
||||||
ion-popover,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
|
ion-popover,prop,leaveAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
|
||||||
ion-popover,prop,mode,"ios" | "md",undefined,false,false
|
ion-popover,prop,mode,"ios" | "md",undefined,false,false
|
||||||
|
16
core/src/components.d.ts
vendored
16
core/src/components.d.ts
vendored
@ -1571,6 +1571,10 @@ export namespace Components {
|
|||||||
* If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code.
|
* If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code.
|
||||||
*/
|
*/
|
||||||
"isOpen": boolean;
|
"isOpen": 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.
|
||||||
|
*/
|
||||||
|
"keepContentsMounted": boolean;
|
||||||
/**
|
/**
|
||||||
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
||||||
*/
|
*/
|
||||||
@ -1940,6 +1944,10 @@ export namespace Components {
|
|||||||
* If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code.
|
* If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code.
|
||||||
*/
|
*/
|
||||||
"isOpen": boolean;
|
"isOpen": 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.
|
||||||
|
*/
|
||||||
|
"keepContentsMounted": boolean;
|
||||||
/**
|
/**
|
||||||
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
||||||
*/
|
*/
|
||||||
@ -5488,6 +5496,10 @@ declare namespace LocalJSX {
|
|||||||
* If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code.
|
* If `true`, the modal will open. If `false`, the modal will close. Use this if you need finer grained control over presentation, otherwise just use the modalController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the modal dismisses. You will need to do that in your code.
|
||||||
*/
|
*/
|
||||||
"isOpen"?: boolean;
|
"isOpen"?: 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.
|
||||||
|
*/
|
||||||
|
"keepContentsMounted"?: boolean;
|
||||||
/**
|
/**
|
||||||
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
||||||
*/
|
*/
|
||||||
@ -5779,6 +5791,10 @@ declare namespace LocalJSX {
|
|||||||
* If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code.
|
* If `true`, the popover will open. If `false`, the popover will close. Use this if you need finer grained control over presentation, otherwise just use the popoverController or the `trigger` property. Note: `isOpen` will not automatically be set back to `false` when the popover dismisses. You will need to do that in your code.
|
||||||
*/
|
*/
|
||||||
"isOpen"?: boolean;
|
"isOpen"?: 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.
|
||||||
|
*/
|
||||||
|
"keepContentsMounted"?: boolean;
|
||||||
/**
|
/**
|
||||||
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
* If `true`, the keyboard will be automatically dismissed when the overlay is presented.
|
||||||
*/
|
*/
|
||||||
|
@ -222,6 +222,19 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
|||||||
this.configureTriggerInteraction();
|
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() keepContentsMounted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO (FW-937)
|
* TODO (FW-937)
|
||||||
* This needs to default to true in the next
|
* This needs to default to true in the next
|
||||||
|
@ -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() keepContentsMounted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted after the popover has presented.
|
* Emitted after the popover has presented.
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +21,7 @@ interface IonicReactInternalProps<ElementType> extends React.HTMLAttributes<Elem
|
|||||||
onDidPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
onDidPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||||
onWillDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
|
onWillDismiss?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||||
onWillPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
onWillPresent?: (event: CustomEvent<OverlayEventDetail>) => void;
|
||||||
|
keepContentsMounted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createInlineOverlayComponent = <PropType, ElementType>(
|
export const createInlineOverlayComponent = <PropType, ElementType>(
|
||||||
@ -128,7 +129,7 @@ export const createInlineOverlayComponent = <PropType, ElementType>(
|
|||||||
* so conditionally render the component
|
* so conditionally render the component
|
||||||
* based on the isOpen state.
|
* based on the isOpen state.
|
||||||
*/
|
*/
|
||||||
return createElement(tagName, newProps, (this.state.isOpen) ?
|
return createElement(tagName, newProps, (this.state.isOpen || this.props.keepContentsMounted) ?
|
||||||
createElement('div', {
|
createElement('div', {
|
||||||
id: 'ion-react-wrapper',
|
id: 'ion-react-wrapper',
|
||||||
ref: this.wrapperRef,
|
ref: this.wrapperRef,
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
describe('keepContentsMounted', () => {
|
||||||
|
describe('modal', () => {
|
||||||
|
it('should not mount component if false', () => {
|
||||||
|
cy.visit('/overlay-components/modal');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mount component if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep component mounted after dismissing if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('#open-modal').click();
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('exist');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-button').click();
|
||||||
|
|
||||||
|
cy.get('ion-modal')
|
||||||
|
.should('not.be.visible')
|
||||||
|
.should('have.class', 'overlay-hidden');
|
||||||
|
|
||||||
|
cy.get('ion-modal ion-content').should('exist');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
describe('popover', () => {
|
||||||
|
it('should not mount component if false', () => {
|
||||||
|
cy.visit('/overlay-components/popover');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mount component if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep component mounted after dismissing if true', () => {
|
||||||
|
cy.visit('/keep-contents-mounted');
|
||||||
|
|
||||||
|
cy.get('#open-popover').click();
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('exist');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-button').click();
|
||||||
|
|
||||||
|
cy.get('ion-popover')
|
||||||
|
.should('not.be.visible')
|
||||||
|
.should('have.class', 'overlay-hidden');
|
||||||
|
|
||||||
|
cy.get('ion-popover ion-content').should('exist');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
@ -24,6 +24,7 @@ import './theme/variables.css';
|
|||||||
import Main from './pages/Main';
|
import Main from './pages/Main';
|
||||||
import OverlayHooks from './pages/overlay-hooks/OverlayHooks';
|
import OverlayHooks from './pages/overlay-hooks/OverlayHooks';
|
||||||
import OverlayComponents from './pages/overlay-components/OverlayComponents';
|
import OverlayComponents from './pages/overlay-components/OverlayComponents';
|
||||||
|
import KeepContentsMounted from './pages/overlay-components/KeepContentsMounted';
|
||||||
import Tabs from './pages/Tabs';
|
import Tabs from './pages/Tabs';
|
||||||
|
|
||||||
setupIonicReact();
|
setupIonicReact();
|
||||||
@ -35,6 +36,7 @@ const App: React.FC = () => (
|
|||||||
<Route path="/" component={Main} />
|
<Route path="/" component={Main} />
|
||||||
<Route path="/overlay-hooks" component={OverlayHooks} />
|
<Route path="/overlay-hooks" component={OverlayHooks} />
|
||||||
<Route path="/overlay-components" component={OverlayComponents} />
|
<Route path="/overlay-components" component={OverlayComponents} />
|
||||||
|
<Route path="/keep-contents-mounted" component={KeepContentsMounted} />
|
||||||
<Route path="/tabs" component={Tabs} />
|
<Route path="/tabs" component={Tabs} />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
</IonReactRouter>
|
</IonReactRouter>
|
||||||
|
@ -31,6 +31,11 @@ const Main: React.FC<MainProps> = () => {
|
|||||||
<IonLabel>Overlay Components</IonLabel>
|
<IonLabel>Overlay Components</IonLabel>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
<IonList>
|
||||||
|
<IonItem routerLink="/keep-contents-mounted">
|
||||||
|
<IonLabel>Keep Contents Mounted Overlay Components</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
<IonList>
|
<IonList>
|
||||||
<IonItem routerLink="/tabs">
|
<IonItem routerLink="/tabs">
|
||||||
<IonLabel>Tabs</IonLabel>
|
<IonLabel>Tabs</IonLabel>
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonContent,
|
||||||
|
IonPage,
|
||||||
|
IonModal,
|
||||||
|
IonPopover,
|
||||||
|
} from '@ionic/react';
|
||||||
|
|
||||||
|
const KeepContentsMounted: React.FC = () => {
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
const [showPopover, setShowPopover] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonButton id="open-modal" onClick={() => setShowModal(true)}>Open Modal</IonButton>
|
||||||
|
<IonButton id="open-popover" onClick={() => setShowPopover(true)}>Open Popover</IonButton>
|
||||||
|
|
||||||
|
<IonModal keepContentsMounted={true} id="default-modal" isOpen={showModal} onDidDismiss={() => setShowPopover(false)}>
|
||||||
|
<IonContent>
|
||||||
|
<IonButton onClick={() => setShowModal(false)}>Dismiss</IonButton>
|
||||||
|
Modal Content
|
||||||
|
</IonContent>
|
||||||
|
</IonModal>
|
||||||
|
|
||||||
|
<IonPopover keepContentsMounted={true} isOpen={showPopover} onDidDismiss={() => setShowPopover(false)}>
|
||||||
|
<IonContent>
|
||||||
|
<IonButton onClick={() => setShowPopover(false)}>Dismiss</IonButton>
|
||||||
|
Popover Content
|
||||||
|
</IonContent>
|
||||||
|
</IonPopover>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeepContentsMounted;
|
@ -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 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', 'backdropBreakpoint', 'backdropDismiss', 'breakpoints', 'canDismiss', 'enterAnimation', 'handle', 'htmlAttributes', 'initialBreakpoint', 'isOpen', 'keepContentsMounted', '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', 'backdropDismiss', 'component', 'componentProps', 'dismissOnSelect', 'enterAnimation', 'event', 'htmlAttributes', 'isOpen', 'keepContentsMounted', 'keyboardClose', 'leaveAnimation', 'mode', 'reference', 'showBackdrop', 'side', 'size', 'translucent', 'trigger', 'triggerAction']);
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ export const defineOverlayContainer = <Props extends object>(name: string, defin
|
|||||||
return h(
|
return h(
|
||||||
name,
|
name,
|
||||||
{ ...restOfProps, ref: elementRef },
|
{ ...restOfProps, ref: elementRef },
|
||||||
(isOpen.value) ? slots : undefined
|
(isOpen.value || restOfProps.keepContentsMounted) ? slots : undefined
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<ion-content class="ion-padding">
|
<ion-content class="ion-padding">
|
||||||
{{ title }}
|
<ion-button id="dismiss" @click="dismiss">Dismiss</ion-button> <br />
|
||||||
|
|
||||||
|
<div id="title">
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
|
IonButton,
|
||||||
IonContent,
|
IonContent,
|
||||||
popoverController
|
popoverController
|
||||||
} from '@ionic/vue';
|
} from '@ionic/vue';
|
||||||
@ -16,6 +21,7 @@ export default defineComponent({
|
|||||||
title: { type: String, default: 'Default Title' }
|
title: { type: String, default: 'Default Title' }
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
IonButton,
|
||||||
IonContent
|
IonContent
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -25,6 +25,10 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
path: '/overlays',
|
path: '/overlays',
|
||||||
component: () => import('@/views/Overlays.vue')
|
component: () => import('@/views/Overlays.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/keep-contents-mounted',
|
||||||
|
component: () => import('@/views/OverlaysKeepContentsMounted.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/inputs',
|
path: '/inputs',
|
||||||
component: () => import('@/views/Inputs.vue')
|
component: () => import('@/views/Inputs.vue')
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
<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"
|
||||||
|
:keep-contents-mounted="true"
|
||||||
|
trigger="open-auto-mount-modal"
|
||||||
|
>
|
||||||
|
<ModalContent :title="overlayProps.title"></ModalContent>
|
||||||
|
</ion-modal>
|
||||||
|
|
||||||
|
<ion-popover
|
||||||
|
id="auto-mount-popover"
|
||||||
|
:keep-contents-mounted="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 } from 'vue';
|
||||||
|
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>
|
@ -0,0 +1,52 @@
|
|||||||
|
describe('overlays - keepContentsMounted', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.viewport(1000, 900);
|
||||||
|
cy.visit('http://localhost:8080/keep-contents-mounted')
|
||||||
|
})
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
@ -134,7 +134,7 @@ describe('Overlays', () => {
|
|||||||
cy.get('ion-button#present-overlay').click();
|
cy.get('ion-button#present-overlay').click();
|
||||||
cy.get('ion-popover.ion-popover-controller').should('exist');
|
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', () => {
|
it('should pass props to popover via component', () => {
|
||||||
@ -144,7 +144,7 @@ describe('Overlays', () => {
|
|||||||
cy.get('ion-button#present-overlay').click();
|
cy.get('ion-button#present-overlay').click();
|
||||||
cy.get('ion-popover').should('exist');
|
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', () => {
|
it('should only open one instance at a time when props change quickly on component', () => {
|
||||||
|
Reference in New Issue
Block a user