Skip to content

Commit

Permalink
feat(datepicker): close popup on outside click
Browse files Browse the repository at this point in the history
BREAKING CHANGE: datepicker popup will now close on outside click by default.

Because of this datepicker will NOT open with `(click)="d.open()"` and `(click)="d.toggle()"`
unless you mark the element with click handler with `[ngbDatepickerToggle]="d"`

BEFORE:
```html
<input ngbDatepicker #d="ngbDatepicker" />
<button (click)="d.toggle()">...</button>
<button (click)="d.open()">...</button>
```

AFTER:
```html
<input ngbDatepicker #d="ngbDatepicker" />
<button [ngbDatepickerToggle]="d" (click)="d.toggle()">...</button>
<button [ngbDatepickerToggle]="d" (click)="d.open()">...</button>
```

Also note that `[autoClose]` input now accepts additional 'inside' and 'outside' values.

Closes #783
Closes #2440
  • Loading branch information
maxokorokov authored and pkozlowski-opensource committed Jul 6, 2018
1 parent 2495570 commit a9ab409
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 33 deletions.
1 change: 1 addition & 0 deletions demo/src/app/components/datepicker/datepicker.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {DEMO_SNIPPETS} from './demos';
</ngbd-overview>
<ngbd-api-docs directive="NgbDatepicker"></ngbd-api-docs>
<ngbd-api-docs directive="NgbInputDatepicker"></ngbd-api-docs>
<ngbd-api-docs directive="NgbDatepickerToggle"></ngbd-api-docs>
<ngbd-api-docs-class type="NgbDateStruct"></ngbd-api-docs-class>
<ngbd-api-docs-class type="DayTemplateContext"></ngbd-api-docs-class>
<ngbd-api-docs-class type="NgbDatepickerNavigateEvent"></ngbd-api-docs-class>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<input class="form-control" placeholder="yyyy-mm-dd"
name="d2" #c2="ngModel" [(ngModel)]="model2" ngbDatepicker #d2="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="d2.toggle()" type="button">
<button type="button" class="btn btn-outline-secondary" [ngbDatepickerToggle]="d2" (click)="d2.toggle()">
<img src="img/calendar-icon.svg" style="width: 1.2rem; height: 1rem; cursor: pointer;"/>
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<input class="form-control" placeholder="yyyy-mm-dd"
name="dp" [(ngModel)]="model" ngbDatepicker [dayTemplate]="customDay" [markDisabled]="isDisabled" #d="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">
<button type="button" class="btn btn-outline-secondary" [ngbDatepickerToggle]="d" (click)="d.toggle()">
<img src="img/calendar-icon.svg" style="width: 1.2rem; height: 1rem; cursor: pointer;"/>
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
name="dp" [displayMonths]="displayMonths" [navigation]="navigation" [outsideDays]="outsideDays"
[showWeekNumbers]="showWeekNumbers" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">
<button type="button" class="btn btn-outline-secondary" [ngbDatepickerToggle]="d" (click)="d.toggle()">
<img src="img/calendar-icon.svg" style="width: 1.2rem; height: 1rem; cursor: pointer;"/>
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<input class="form-control" placeholder="yyyy-mm-dd"
name="dp" [(ngModel)]="model" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">
<button type="button" class="btn btn-outline-secondary" [ngbDatepickerToggle]="d" (click)="d.toggle()">
<img src="img/calendar-icon.svg" style="width: 1.2rem; height: 1rem; cursor: pointer;"/>
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion demo/src/app/components/modal/demos/basic/modal-basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ <h4 class="modal-title" id="modal-basic-title">Profile update</h4>
<div class="input-group">
<input id="dateOfBirth" class="form-control" placeholder="yyyy-mm-dd" name="dp" ngbDatepicker #dp="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="dp.toggle()" type="button">
<button #b1 class="btn btn-outline-secondary" [ngbDatepickerToggle]="dp" (click)="dp.toggle()" type="button">
<img src="img/calendar-icon.svg" style="width: 1.2rem; height: 1rem; cursor: pointer;"/>
</button>
</div>
Expand Down
170 changes: 155 additions & 15 deletions src/datepicker/datepicker-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {createGenericTestComponent} from '../test/common';

import {Component, Injectable} from '@angular/core';
import {FormsModule, NgForm} from '@angular/forms';
import {Key} from '../util/key';

import {Key} from '../util/key';
import {NgbDateAdapter, NgbDatepickerModule} from './datepicker.module';
import {NgbInputDatepicker} from './datepicker-input';
import {NgbDatepicker} from './datepicker';
Expand Down Expand Up @@ -38,9 +38,9 @@ describe('NgbInputDatepicker', () => {
it('should allow controlling datepicker popup from outside', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button (click)="open(d)">Open</button>
<button (click)="close(d)">Close</button>
<button (click)="toggle(d)">Toggle</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button [ngbDatepickerToggle]="d" (click)="close(d)">Close</button>
<button [ngbDatepickerToggle]="d" (click)="toggle(d)">Toggle</button>`);

const buttons = fixture.nativeElement.querySelectorAll('button');

Expand All @@ -67,7 +67,7 @@ describe('NgbInputDatepicker', () => {
it('should focus the datepicker after opening', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button (click)="open(d)">Open</button>
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
`);

// open
Expand All @@ -80,7 +80,7 @@ describe('NgbInputDatepicker', () => {
it('should close datepicker on ESC key', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button (click)="open(d)">Open</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>`);

// open
const button = fixture.nativeElement.querySelector('button');
Expand All @@ -93,10 +93,79 @@ describe('NgbInputDatepicker', () => {
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();
});

it('should close datepicker on date selection', () => {
it('should not close datepicker when clicking on input element', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button (click)="open(d)">Open</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button id="outside-button">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
button.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click on input
const input = fixture.nativeElement.querySelector('input[ngbDatepicker]');
input.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();
});

it('should not close datepicker when clicking elements with [ngbDatepickerToggle]', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button [ngbDatepickerToggle]="d" id="outside-button">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
button.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside
const outsideButton = fixture.nativeElement.querySelector('#outside-button');
outsideButton.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();
});

it('should not close datepicker when clicking elements added with "registerClickableElement()"', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button [ngbDatepickerToggle]="d" (click)="d.registerClickableElement(b1); open(d); d.registerClickableElement(b2);">Open</button>
<button #b1 id="outside-button1">Outside button</button>
<button #b2 id="outside-button2">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
button.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside 1
const outsideButton1 = fixture.nativeElement.querySelector('#outside-button1');
outsideButton1.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside 2
const outsideButton2 = fixture.nativeElement.querySelector('#outside-button2');
outsideButton2.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();
});

it('should close datepicker on date selection and outside click', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button id="outside-button">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
Expand All @@ -109,12 +178,25 @@ describe('NgbInputDatepicker', () => {
dp.select.emit();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();

// open
button.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside
const outsideButton = fixture.nativeElement.querySelector('#outside-button');
outsideButton.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();
});

it(`should not close datepicker if 'autoClose' set to 'false'`, () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker" [autoClose]="false">
<button (click)="open(d)">Open</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button id="outside-button">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
Expand All @@ -127,6 +209,64 @@ describe('NgbInputDatepicker', () => {
dp.select.emit();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside
const outsideButton = fixture.nativeElement.querySelector('#outside-button');
outsideButton.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();
});

it(`should close datepicker only on date selection if 'autoClose' set to 'inside'`, () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker" autoClose="inside">
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button id="outside-button">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
button.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside
const outsideButton = fixture.nativeElement.querySelector('#outside-button');
outsideButton.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// select
const dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
dp.select.emit();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();
});

it(`should close datepicker only on outside click if 'autoClose' set to 'outside'`, () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker" autoClose="outside">
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button id="outside-button">Outside button</button>
`);

// open
const button = fixture.nativeElement.querySelector('button');
button.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// select
const dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
dp.select.emit();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

// click outside
const outsideButton = fixture.nativeElement.querySelector('#outside-button');
outsideButton.click();
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();
});
});

Expand Down Expand Up @@ -208,7 +348,7 @@ describe('NgbInputDatepicker', () => {
it('should propagate disabled state', fakeAsync(() => {
const fixture = createTestCmpt(`
<input ngbDatepicker [(ngModel)]="date" #d="ngbDatepicker" [disabled]="isDisabled">
<button (click)="open(d)">Open</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>`);
fixture.componentInstance.isDisabled = true;
fixture.detectChanges();

Expand Down Expand Up @@ -247,7 +387,7 @@ describe('NgbInputDatepicker', () => {
it('should propagate disabled state without form control', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker" [disabled]="isDisabled">
<button (click)="open(d)">Open</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>`);
fixture.componentInstance.isDisabled = true;
fixture.detectChanges();

Expand Down Expand Up @@ -298,7 +438,7 @@ describe('NgbInputDatepicker', () => {
it('should propagate touched state when setting a date', fakeAsync(() => {
const fixture = createTestCmpt(`
<input ngbDatepicker [(ngModel)]="date" #d="ngbDatepicker">
<button (click)="open(d)">Open</button>`);
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>`);

const buttonDebugEl = fixture.debugElement.query(By.css('button'));
const inputDebugEl = fixture.debugElement.query(By.css('input'));
Expand Down Expand Up @@ -674,7 +814,7 @@ describe('NgbInputDatepicker', () => {
const selector = 'body';
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker" container="${selector}">
<button (click)="open(d)">Open</button>
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
`);

// open date-picker
Expand All @@ -690,8 +830,8 @@ describe('NgbInputDatepicker', () => {
const selector = 'body';
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker" container="${selector}">
<button (click)="open(d)">Open</button>
<button (click)="close(d)">Close</button>
<button [ngbDatepickerToggle]="d" (click)="open(d)">Open</button>
<button [ngbDatepickerToggle]="d" (click)="close(d)">Close</button>
`);

// open date-picker
Expand Down

0 comments on commit a9ab409

Please sign in to comment.