Skip to content
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

feat(modal): stacked modals #2640

Merged
merged 2 commits into from Aug 29, 2018
Merged

feat(modal): stacked modals #2640

merged 2 commits into from Aug 29, 2018

Conversation

ghost
Copy link

@ghost ghost commented Aug 24, 2018

Before submitting a pull request, please make sure you have at least performed the following:

  • read and followed the CONTRIBUTING.md guide.
  • built and tested the changes locally.
  • added/updated any applicable tests.
  • added/updated any applicable API documentation.
  • added/updated any applicable demos.

P.S. if you think the project is not ready to have stacked modals in the demo, at least consider merging changes related to the modal-open class.

@@ -3,7 +3,8 @@ import {Component, Input} from '@angular/core';
@Component({
selector: 'ngb-modal-backdrop',
template: '',
host: {'[class]': '"modal-backdrop fade show" + (backdropClass ? " " + backdropClass : "")'}
host:
{'[class]': '"modal-backdrop fade show" + (backdropClass ? " " + backdropClass : "")', 'style': 'z-index: 1050'}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that we don't need to adjust z-index on windows themselves...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pkozlowski-opensource it's ok to not adjust manually z-index.
According to W3C:

Each box belongs to one stacking context. Each positioned box in a given stacking context has an integer stack level, which is its position on the z-axis relative other stack levels within the same stacking context. Boxes with greater stack levels are always formatted in front of boxes with lower stack levels. Boxes may have negative stack levels. Boxes with the same stack level in a stacking context are stacked back-to-front according to document tree order.

So it should be fine for us.

activeModal.close = (result: any) => { ngbModalRef.close(result); };
activeModal.dismiss = (reason: any) => { ngbModalRef.dismiss(reason); };

this._applyWindowOptions(windowCmptRef.instance, options);
renderer.addClass(this._document.body, 'modal-open');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we check if there are no open modals open already and skip DOM manipulation if there is already an open modal?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. addClass will be called only when _modalRefs has 1 modal.

@pkozlowski-opensource
Copy link
Member

@Maxkorz thnx for the PR - I did a first pass on it and it looks really good! Just left minor nit comment.

I would like to spend more time testing it but if all goes well we will land it in 3.2.0. Thnx again!

@pkozlowski-opensource pkozlowski-opensource added this to the 3.2.0 milestone Aug 25, 2018
Copy link
Member

@benouat benouat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please have a look ?


open(moduleCFR: ComponentFactoryResolver, contentInjector: Injector, content: any, options): NgbModalRef {
const containerEl =
isDefined(options.container) ? this._document.querySelector(options.container) : this._document.body;
const renderer = this._rendererFactory.createRenderer(null, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any specific reason you are using RendererFactory2 here to create a new renderer instance instead of injecting directly private _renderer: Renderer2 in constructor ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benouat this is a service so I don't think you can inject Renderer2 (it is only injectable in components)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right ! my bad. I tend to always forget modal things are occurring from services

@benouat
Copy link
Member

benouat commented Aug 28, 2018

The overall code LGTM! Nonetheless I have a few comments:

  • Keyboard: it seems broken (or simply missing) 😄. I was just playing with the new demo, open both modals, hit Esc -> it is closing the first one! A test should be added.

  • Backdrops: Each new modal created has its own NgbModalBackdrop. Do we really want that behaviour ? Can't we have only one backdrop, and move it dynamically in the DOM to be in between opened modals ? Maybe it sounds kind of unnecessary optimisation ?

@ghost
Copy link
Author

ghost commented Aug 28, 2018

@benouat Thanks for the review!

@ghost
Copy link
Author

ghost commented Aug 28, 2018

@benouat Keyboard issue has been fixed.


ngbWindowCmpt.onDestroy(() => {
const index = this._windowCmpts.indexOf(ngbWindowCmpt);
if (index > -1) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this check from _registerModalRef. Is it really necessary to check the existence of an element before removing it from an array?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With splice()yes it is needed, because splice( negative-index-here, ...) will remove from the end, e.g. : [1, 2 , 3].splice(-1, 1) -> [1, 2]

As a sidenote, I think we could only fire this._activeWindowCmptHasChanged.next(); if we remove a componentRef. If nothing is removed it is not needed.

Copy link
Author

@ghost ghost Aug 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a sidenote, I think we could only fire this._activeWindowCmptHasChanged.next(); if we remove a componentRef. If nothing is removed it is not needed.

I subscribe to _activeWindowCmptHasChanged in constructor in order to call ngbFocusTrap on the top/activated/latest (you get the idea) WindowCmpt. So it should fire on push, and on splice (to trap the focus again on the previously opened modal window)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just talking about moving the call inside the if :

ngbWindowCmpt.onDestroy(() => {
    const index = this._windowCmpts.indexOf(ngbWindowCmpt);
    if (index > -1) {
        this._windowCmpts.splice(index, 1);
        this._activeWindowCmptHasChanged.next();
    }
    // this._activeWindowCmptHasChanged.next();  move it inside the upper if
});

Copy link
Author

@ghost ghost Aug 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, sure, I misunderstood you.
Done

});
}));

it('should dismiss modals on ESC in correct order', () => {
Copy link
Author

@ghost ghost Aug 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESC didn't work because ngbFocusTrap was preventing focus to go out of the first modal window.

I fixed it by moving ngbFocusTrap to the NgbModalStack service, but I'm not sure how to test it. The test seems right, but no matter where I call ngbFocusTrap it still passes. And I couldn't find any tests for Focus Trap, so I assume it's not testable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your changes look good to me. No matter where we call the ngbFocusTrap, test does not need to be changed I think.

@benouat
Copy link
Member

benouat commented Aug 28, 2018

@Maxkorz for the backdrop I don't think we need to change anything right now. Let's keep it as it, with one new backdrop per modal. We can always refactor and change it later.

Copy link
Member

@benouat benouat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more time a very big thank you for this contribution 👍
Let's merge it.

@benouat benouat merged commit 2409572 into ng-bootstrap:master Aug 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants