diff --git a/ionic/components/action-sheet/action-sheet.ts b/ionic/components/action-sheet/action-sheet.ts index b93db0ed95..732f7c0fa7 100644 --- a/ionic/components/action-sheet/action-sheet.ts +++ b/ionic/components/action-sheet/action-sheet.ts @@ -78,20 +78,21 @@ import {ViewController} from '../nav/view-controller'; * @demo /docs/v2/demos/action-sheet/ * @see {@link /docs/v2/components#action-sheets ActionSheet Component Docs} */ - export class ActionSheet extends ViewController { +export class ActionSheet extends ViewController { - constructor(opts: ActionSheetOptions = {}) { - opts.buttons = opts.buttons || []; - opts.enableBackdropDismiss = isDefined(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true; + constructor(opts: ActionSheetOptions = {}) { + opts.buttons = opts.buttons || []; + opts.enableBackdropDismiss = isDefined(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true; - super(ActionSheetCmp, opts); - this.viewType = 'action-sheet'; + super(ActionSheetCmp, opts); + this.viewType = 'action-sheet'; + this.isOverlay = true; // by default, actionsheets should not fire lifecycle events of other views // for example, when an actionsheets enters, the current active view should // not fire its lifecycle events because it's not conceptually leaving this.fireOtherLifecycles = false; - } + } /** * @private diff --git a/ionic/components/alert/alert.ts b/ionic/components/alert/alert.ts index 57199e6878..8743b24936 100644 --- a/ionic/components/alert/alert.ts +++ b/ionic/components/alert/alert.ts @@ -126,6 +126,7 @@ export class Alert extends ViewController { super(AlertCmp, opts); this.viewType = 'alert'; + this.isOverlay = true; // by default, alerts should not fire lifecycle events of other views // for example, when an alert enters, the current active view should diff --git a/ionic/components/alert/test/basic/index.ts b/ionic/components/alert/test/basic/index.ts index aeea05ede2..2902c5b2f5 100644 --- a/ionic/components/alert/test/basic/index.ts +++ b/ionic/components/alert/test/basic/index.ts @@ -1,4 +1,4 @@ -import {App, Page, Alert, NavController} from '../../../../../ionic/ionic'; +import {App, Page, Alert, Modal, NavController, ViewController} from '../../../../../ionic/ionic'; @Page({ @@ -71,9 +71,20 @@ class E2EPage { let alert = Alert.create({ title: 'Alert', subTitle: 'Subtitle', - message: 'This is an alert message.', - buttons: ['Cancel', 'Continue', 'Delete'] + message: 'This is an alert message.' }); + alert.addButton('Cancel'); + alert.addButton({ + text: 'Open Modal', + handler: () => { + let modal = Modal.create(ModalPage); + this.nav.present(modal); + + // do not close the alert when this button is pressed + return false; + } + }); + alert.addButton('Delete'); this.nav.present(alert); } @@ -98,6 +109,7 @@ class E2EPage { alert.dismiss(data); }, 500); + // do not close the alert when this button is pressed return false; } }); @@ -270,11 +282,33 @@ class E2EPage { } } +@Page({ + template: ` + + + + + Modal + + + Hi, I'm Bob, and I'm a modal. + + ` +}) +class ModalPage { + constructor(private viewCtrl: ViewController) {} + + dismiss() { + this.viewCtrl.dismiss(); + } +} + @App({ template: '' }) class E2EApp { + root; constructor() { this.root = E2EPage; } diff --git a/ionic/components/modal/modal.ts b/ionic/components/modal/modal.ts index 2ed614c42f..b8c0009c7c 100644 --- a/ionic/components/modal/modal.ts +++ b/ionic/components/modal/modal.ts @@ -106,6 +106,7 @@ export class Modal extends ViewController { constructor(componentType, data={}) { super(componentType, data); this.viewType = 'modal'; + this.isOverlay = true; } /** diff --git a/ionic/components/nav/nav-controller.ts b/ionic/components/nav/nav-controller.ts index aa8eab1a7e..8d30071acb 100644 --- a/ionic/components/nav/nav-controller.ts +++ b/ionic/components/nav/nav-controller.ts @@ -990,10 +990,19 @@ export class NavController extends Ion { } else { // there are no other transitions happening but this one // only entering/leaving should show, all others hidden - this._views.forEach(view => { - let shouldShow = (view === enteringView) || (view === leavingView); + // also if a view is an overlay or the previous view is an + // overlay then always show the overlay and the view before it + var view: ViewController; + var shouldShow: boolean; + + for (var i = 0, ii = this._views.length; i < ii; i++) { + view = this._views[i]; + shouldShow = (view === enteringView) || + (view === leavingView) || + view.isOverlay || + (i < ii - 1 ? this._views[i + 1].isOverlay : false); view.domCache(shouldShow, this._renderer); - }); + } } // call each view's lifecycle events diff --git a/ionic/components/nav/test/nav-controller.spec.ts b/ionic/components/nav/test/nav-controller.spec.ts index d4ec11ec3c..fa95264e0b 100644 --- a/ionic/components/nav/test/nav-controller.spec.ts +++ b/ionic/components/nav/test/nav-controller.spec.ts @@ -477,6 +477,73 @@ export function run() { expect(navOptions.animate).toBe(false); }); + it('should set domCache true when isAlreadyTransitioning', () => { + let enteringView = new ViewController(Page1); + let leavingView = new ViewController(Page2); + let isAlreadyTransitioning = true; + var navOptions: NavOptions = {}; + var done = () => {}; + nav._beforeTrans = () => {}; //prevent running beforeTrans for tests + nav._renderer = null; + + spyOn(enteringView, 'domCache'); + spyOn(leavingView, 'domCache'); + + nav._postRender(1, enteringView, leavingView, isAlreadyTransitioning, navOptions, done); + + expect(enteringView.domCache).toHaveBeenCalledWith(true, nav._renderer); + expect(leavingView.domCache).toHaveBeenCalledWith(true, nav._renderer); + }); + + it('should set domCache true when isAlreadyTransitioning false for the entering/leaving views', () => { + let view1 = new ViewController(Page1); + let view2 = new ViewController(Page2); + let view3 = new ViewController(Page3); + let isAlreadyTransitioning = false; + var navOptions: NavOptions = {}; + var done = () => {}; + nav._beforeTrans = () => {}; //prevent running beforeTrans for tests + nav._renderer = null; + nav._views = [view1, view2, view3]; + + spyOn(view1, 'domCache'); + spyOn(view2, 'domCache'); + spyOn(view3, 'domCache'); + + nav._postRender(1, view3, view2, isAlreadyTransitioning, navOptions, done); + + expect(view1.domCache).toHaveBeenCalledWith(false, nav._renderer); + expect(view2.domCache).toHaveBeenCalledWith(true, nav._renderer); + expect(view3.domCache).toHaveBeenCalledWith(true, nav._renderer); + }); + + it('should set domCache true when isAlreadyTransitioning false for views when a view has isOverlay=true', () => { + let view1 = new ViewController(Page1); + let view2 = new ViewController(Page2); + let view3 = new ViewController(Page3); + let view4 = new ViewController(Page4); + let isAlreadyTransitioning = false; + var navOptions: NavOptions = {}; + var done = () => {}; + nav._beforeTrans = () => {}; //prevent running beforeTrans for tests + nav._renderer = null; + nav._views = [view1, view2, view3, view4]; + + view3.isOverlay = true; + + spyOn(view1, 'domCache'); + spyOn(view2, 'domCache'); + spyOn(view3, 'domCache'); + spyOn(view4, 'domCache'); + + nav._postRender(1, view4, view3, isAlreadyTransitioning, navOptions, done); + + expect(view1.domCache).toHaveBeenCalledWith(false, nav._renderer); + expect(view2.domCache).toHaveBeenCalledWith(true, nav._renderer); + expect(view3.domCache).toHaveBeenCalledWith(true, nav._renderer); + expect(view4.domCache).toHaveBeenCalledWith(true, nav._renderer); + }); + }); describe('_setZIndex', () => { diff --git a/ionic/components/nav/view-controller.ts b/ionic/components/nav/view-controller.ts index ad8792aaa3..f5637cca65 100644 --- a/ionic/components/nav/view-controller.ts +++ b/ionic/components/nav/view-controller.ts @@ -71,6 +71,11 @@ export class ViewController { */ fireOtherLifecycles: boolean = true; + /** + * @private + */ + isOverlay: boolean = false; + /** * @private */