-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(modal): ignore accidental backdrop clicks
This uses a combination of 'mousedown/up' events instead of 'click'. This aligns behavior with bootstrap and helps with closing modal accidentally (ex. when selecting text) Some tests had to be converted to e2e Closes #3384 Fixes #1950
- Loading branch information
1 parent
9597e2e
commit cbf2b3c
Showing
10 changed files
with
191 additions
and
288 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
e2e-app/src/app/modal/autoclose/modal-autoclose.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<h3> | ||
Modal autoclose tests | ||
<span class="ml-1 badge badge-info" id="dismiss-reason">{{ reason }}</span> | ||
</h3> | ||
|
||
<!-- Modal content --> | ||
<ng-template #content> | ||
<div class="modal-body"> | ||
<h2>Hello from modal</h2> | ||
<button id="modal-close-button" class="btn btn-primary" (click)="closeModal()">Close modal</button> | ||
</div> | ||
</ng-template> | ||
|
||
<!-- Controls --> | ||
<form id="default" (contextmenu)="$event.preventDefault()"> | ||
<div class="form-group form-inline"> | ||
<div class="input-group"> | ||
<button class="btn btn-outline-secondary" type="button" id="open-modal" (click)="openModal(content)">Open modal</button> | ||
</div> | ||
|
||
<button class="btn btn-outline-secondary ml-3" id="reset-button" (click)="reset()">Reset</button> | ||
<button class="btn btn-outline-secondary ml-1" id="option-backdrop-static" (click)="options['backdrop'] = 'static'">backdrop = 'static'</button> | ||
<button class="btn btn-outline-secondary ml-1" id="option-backdrop-false" (click)="options['backdrop'] = false">backdrop = false</button> | ||
<button class="btn btn-outline-secondary ml-1" id="option-no-keyboard" (click)="options['keyboard'] = false">keyboard = 'false'</button> | ||
</div> | ||
</form> | ||
|
||
<!-- Options --> | ||
<h4>Options</h4> | ||
<pre>{{ options | json }}</pre> |
43 changes: 43 additions & 0 deletions
43
e2e-app/src/app/modal/autoclose/modal-autoclose.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, TemplateRef} from '@angular/core'; | ||
import {ModalDismissReasons, NgbModal, NgbModalRef} from '@ng-bootstrap/ng-bootstrap'; | ||
|
||
@Component({templateUrl: './modal-autoclose.component.html', changeDetection: ChangeDetectionStrategy.OnPush}) | ||
export class ModalAutoCloseComponent { | ||
private modalRef: NgbModalRef = null; | ||
reason = ''; | ||
options = {}; | ||
|
||
constructor(private modalService: NgbModal, private cd: ChangeDetectorRef) {} | ||
|
||
openModal(content?: TemplateRef<any>) { | ||
this.modalRef = this.modalService.open(content, this.options); | ||
this.modalRef.result.then( | ||
() => { | ||
this.reason = `Closed`; | ||
this.cd.markForCheck(); | ||
}, | ||
reason => { | ||
if (reason === ModalDismissReasons.BACKDROP_CLICK) { | ||
this.reason = 'Click'; | ||
} else if (reason === ModalDismissReasons.ESC) { | ||
this.reason = 'Escape'; | ||
} else { | ||
this.reason = 'Other'; | ||
} | ||
this.cd.markForCheck(); | ||
}); | ||
} | ||
|
||
closeModal() { | ||
if (this.modalRef) { | ||
this.modalRef.close(); | ||
this.modalRef = null; | ||
} | ||
} | ||
|
||
reset() { | ||
this.closeModal(); | ||
this.reason = ''; | ||
this.options = {}; | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
e2e-app/src/app/modal/autoclose/modal-autoclose.e2e-spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import {ModalAutoClosePage} from './modal-autoclose.po'; | ||
import {expectNoOpenModals, openUrl, sendKey} from '../../tools.po'; | ||
import {Key} from 'protractor'; | ||
|
||
describe('Modal', () => { | ||
let page: ModalAutoClosePage; | ||
|
||
beforeAll(() => page = new ModalAutoClosePage()); | ||
|
||
beforeEach(async() => { | ||
await openUrl('modal/autoclose'); | ||
await page.getResetButton().click(); | ||
}); | ||
|
||
afterEach(async() => { await expectNoOpenModals(); }); | ||
|
||
it('should close modal from the inside', async() => { | ||
const modal = await page.openModal(); | ||
|
||
// close | ||
await page.getModalCloseButton().click(); | ||
expect(await modal.isPresent()).toBeFalsy('The modal should be closed imperatively'); | ||
expect(await page.getDismissReason()).toBe('Closed', `Modal should have been closed`); | ||
}); | ||
|
||
it('should close modal on ESC', async() => { | ||
await page.openModal(); | ||
|
||
// close | ||
await sendKey(Key.ESCAPE); | ||
expect(await page.getModal().isPresent()).toBeFalsy('The modal should be closed on ESC'); | ||
expect(await page.getDismissReason()).toBe('Escape', `Modal should have been dismissed with 'Escape' reason`); | ||
}); | ||
|
||
it(`should NOT close modal on ESC when keyboard === 'false'`, async() => { | ||
const modal = await page.openModal('no-keyboard'); | ||
|
||
// close | ||
await sendKey(Key.ESCAPE); | ||
expect(await modal.isPresent()).toBeTruthy('The modal should stay opened on ESC'); | ||
await page.getModalCloseButton().click(); | ||
}); | ||
|
||
it('should close modal on backdrop click', async() => { | ||
const modal = await page.openModal(); | ||
|
||
// close | ||
await modal.click(); | ||
expect(await modal.isPresent()).toBeFalsy('The modal should be closed on backdrop click'); | ||
expect(await page.getDismissReason()).toBe('Click', `Modal should have been dismissed with 'Click' reason`); | ||
}); | ||
|
||
it(`should NOT close modal on 'static' backdrop click`, async() => { | ||
const modal = await page.openModal('backdrop-static'); | ||
|
||
// close | ||
await modal.click(); | ||
expect(await modal.isPresent()).toBeTruthy('The modal should stay opened on backdrop click'); | ||
await page.getModalCloseButton().click(); | ||
}); | ||
|
||
it(`should NOT close modal on click with no backdrop`, async() => { | ||
const modal = await page.openModal('backdrop-false'); | ||
|
||
// close | ||
await modal.click(); | ||
expect(await modal.isPresent()).toBeTruthy('The modal should stay opened on backdrop click'); | ||
await page.getModalCloseButton().click(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import {$} from 'protractor'; | ||
|
||
export class ModalAutoClosePage { | ||
getModal(selector = 'ngb-modal-window') { return $(selector); } | ||
|
||
getModalCloseButton() { return $('#modal-close-button'); } | ||
|
||
getDismissReason() { return $('#dismiss-reason').getText(); } | ||
|
||
getResetButton() { return $('#reset-button'); } | ||
|
||
async openModal(option = '') { | ||
if (option !== '') { | ||
await $(`#option-${option}`).click(); | ||
} | ||
|
||
await $('#open-modal').click(); | ||
|
||
const modal = this.getModal(); | ||
expect(await modal.isPresent()).toBeTruthy(`A modal should have been opened`); | ||
return modal; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.