Skip to content

Commit

Permalink
feat(datepicker): add navigation notification with 'navigate' output
Browse files Browse the repository at this point in the history
Fixes #986
Closes #1002
  • Loading branch information
maxokorokov authored and pkozlowski-opensource committed Nov 4, 2016
1 parent 217218e commit 1639626
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 6 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 @@ -9,6 +9,7 @@ import {DEMO_SNIPPETS} from './demos';
<ngbd-api-docs directive="NgbInputDatepicker"></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>
<ngbd-api-docs-class type="NgbDatepickerI18n"></ngbd-api-docs-class>
<ngbd-api-docs-class type="NgbDateParserFormatter"></ngbd-api-docs-class>
<ngbd-api-docs-config type="NgbDatepickerConfig"></ngbd-api-docs-config>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p>Simple datepicker</p>

<ngb-datepicker #dp [(ngModel)]="model"></ngb-datepicker>
<ngb-datepicker #dp [(ngModel)]="model" (navigate)="date = $event.next"></ngb-datepicker>

<hr/>

Expand All @@ -10,4 +10,5 @@

<hr/>

<pre>Month: {{ date.month }}.{{ date.year }}</pre>
<pre>Model: {{ model | json }}</pre>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const now = new Date();
export class NgbdDatepickerBasic {

model: NgbDateStruct;
date: {year: number, month: number};

selectToday() {
this.model = {year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate()};
Expand Down
19 changes: 19 additions & 0 deletions src/datepicker/datepicker-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,23 @@ describe('NgbInputDatepicker', () => {
const dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
expect(dp.startDate).toEqual(NgbDate.from({year: 2016, month: 9, day: 13}));
}));

it('should relay the "navigate" event', () => {
const fixture =
createTestCmpt(`<input ngbDatepicker [startDate]="{year: 2016, month: 9}" (navigate)="onNavigate($event)">`);
const dpInput = fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);

spyOn(fixture.componentInstance, 'onNavigate');

dpInput.open();
fixture.detectChanges();
expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith({current: null, next: {year: 2016, month: 9}});

const dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
dp.navigateTo({year: 2018, month: 4});
expect(fixture.componentInstance.onNavigate)
.toHaveBeenCalledWith({current: {year: 2016, month: 9}, next: {year: 2018, month: 4}});
});
});
});

Expand All @@ -287,6 +304,8 @@ class TestComponent {
date: NgbDateStruct;
isDisabled;

onNavigate() {}

open(d: NgbInputDatepicker) { d.open(); }

close(d: NgbInputDatepicker) { d.close(); }
Expand Down
17 changes: 15 additions & 2 deletions src/datepicker/datepicker-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import {
ComponentFactoryResolver,
NgZone,
TemplateRef,
forwardRef
forwardRef,
EventEmitter,
Output
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';

import {NgbDate} from './ngb-date';
import {NgbDatepicker} from './datepicker';
import {NgbDatepicker, NgbDatepickerNavigateEvent} from './datepicker';
import {DayTemplateContext} from './datepicker-day-template-context';
import {NgbDateParserFormatter} from './ngb-date-parser-formatter';

Expand Down Expand Up @@ -102,6 +104,12 @@ export class NgbInputDatepicker implements ControlValueAccessor {
*/
@Input() startDate: {year: number, month: number};

/**
* An event fired when navigation happens and currently displayed month changes.
* See NgbDatepickerNavigateEvent for the payload info.
*/
@Output() navigate = new EventEmitter<NgbDatepickerNavigateEvent>();

private _onChange = (_: any) => {};
private _onTouched = () => {};

Expand Down Expand Up @@ -151,6 +159,7 @@ export class NgbInputDatepicker implements ControlValueAccessor {
this._applyPopupStyling(this._cRef.location.nativeElement);
this._cRef.instance.writeValue(this._model);
this._applyDatepickerInputs(this._cRef.instance);
this._subscribeForDatepickerOutputs(this._cRef.instance);
this._cRef.instance.ngOnInit();

// date selection event handling
Expand Down Expand Up @@ -214,6 +223,10 @@ export class NgbInputDatepicker implements ControlValueAccessor {
this._renderer.setElementStyle(nativeElement, 'padding', '0.40rem');
}

private _subscribeForDatepickerOutputs(datepickerInstance: NgbDatepicker) {
datepickerInstance.navigate.subscribe(date => this.navigate.emit(date));
}

private _writeModelValue(model: NgbDate) {
this._renderer.setElementProperty(this._elRef.nativeElement, 'value', this._parserFormatter.format(model));
if (this.isOpen()) {
Expand Down
4 changes: 2 additions & 2 deletions src/datepicker/datepicker.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {NgModule, ModuleWithProviders} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NgbDatepicker} from './datepicker';
import {NgbDatepicker, NgbDatepickerNavigateEvent} from './datepicker';
import {NgbDatepickerMonthView} from './datepicker-month-view';
import {NgbDatepickerNavigation} from './datepicker-navigation';
import {NgbInputDatepicker} from './datepicker-input';
Expand All @@ -13,7 +13,7 @@ import {NgbDatepickerService} from './datepicker-service';
import {NgbDatepickerNavigationSelect} from './datepicker-navigation-select';
import {NgbDatepickerConfig} from './datepicker-config';

export {NgbDatepicker} from './datepicker';
export {NgbDatepicker, NgbDatepickerNavigateEvent} from './datepicker';
export {NgbInputDatepicker} from './datepicker-input';
export {NgbDatepickerMonthView} from './datepicker-month-view';
export {NgbDatepickerDayView} from './datepicker-day-view';
Expand Down
54 changes: 54 additions & 0 deletions src/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,59 @@ describe('ngb-datepicker', () => {
]);
});

it('should emit navigate event when startDate is defined', () => {
TestBed.overrideComponent(
TestComponent,
{set: {template: `<ngb-datepicker [startDate]="date" (navigate)="onNavigate($event)"></ngb-datepicker>`}});
const fixture = TestBed.createComponent(TestComponent);

spyOn(fixture.componentInstance, 'onNavigate');
fixture.detectChanges();

expect(fixture.componentInstance.onNavigate).toHaveBeenCalledWith({current: null, next: {year: 2016, month: 8}});
});

it('should emit navigate event without startDate defined', () => {
TestBed.overrideComponent(
TestComponent, {set: {template: `<ngb-datepicker (navigate)="onNavigate($event)"></ngb-datepicker>`}});
const fixture = TestBed.createComponent(TestComponent);
const now = new Date();

spyOn(fixture.componentInstance, 'onNavigate');
fixture.detectChanges();

expect(fixture.componentInstance.onNavigate)
.toHaveBeenCalledWith({current: null, next: {year: now.getFullYear(), month: now.getMonth() + 1}});
});

it('should emit navigate event using built-in navigation arrows', () => {
const fixture =
createTestComponent(`<ngb-datepicker [startDate]="date" (navigate)="onNavigate($event)"></ngb-datepicker>`);

spyOn(fixture.componentInstance, 'onNavigate');
const navigation = getNavigationLinks(fixture.nativeElement);

// JUL 2016
navigation[0].click();
fixture.detectChanges();
expect(fixture.componentInstance.onNavigate)
.toHaveBeenCalledWith({current: {year: 2016, month: 8}, next: {year: 2016, month: 7}});
});

it('should emit navigate event using navigateTo({date})', () => {
const fixture =
createTestComponent(`<ngb-datepicker #dp [startDate]="date" (navigate)="onNavigate($event)"></ngb-datepicker>
<button id="btn"(click)="dp.navigateTo({year: 2015, month: 6})"></button>`);

spyOn(fixture.componentInstance, 'onNavigate');
const button = fixture.nativeElement.querySelector('button#btn');
button.click();

fixture.detectChanges();
expect(fixture.componentInstance.onNavigate)
.toHaveBeenCalledWith({current: {year: 2016, month: 8}, next: {year: 2015, month: 6}});
});

describe('ngModel', () => {

it('should update model based on calendar clicks', () => {
Expand Down Expand Up @@ -487,4 +540,5 @@ class TestComponent {
disabledForm = new FormGroup({control: new FormControl({value: null, disabled: true})});
model;
markDisabled = (date: NgbDateStruct) => { return NgbDate.from(date).equals(new NgbDate(2016, 8, 22)); };
onNavigate = () => {};
}
44 changes: 43 additions & 1 deletion src/datepicker/datepicker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import {Component, Input, OnChanges, TemplateRef, forwardRef, OnInit, SimpleChange, SimpleChanges} from '@angular/core';
import {
Component,
Input,
OnChanges,
TemplateRef,
forwardRef,
OnInit,
SimpleChanges,
EventEmitter,
Output
} from '@angular/core';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
import {NgbCalendar} from './ngb-calendar';
import {NgbDate} from './ngb-date';
Expand All @@ -16,6 +26,21 @@ const NGB_DATEPICKER_VALUE_ACCESSOR = {
multi: true
};

/**
* The payload of the datepicker navigation event
*/
export interface NgbDatepickerNavigateEvent {
/**
* Currently displayed month
*/
current: {year: number, month: number};

/**
* Month we're navigating to
*/
next: {year: number, month: number};
}

/**
* A lightweight and highly configurable datepicker directive
*/
Expand Down Expand Up @@ -139,6 +164,12 @@ export class NgbDatepicker implements OnChanges,
*/
@Input() startDate: {year: number, month: number};

/**
* An event fired when navigation happens and currently displayed month changes.
* See NgbDatepickerNavigateEvent for the payload info.
*/
@Output() navigate = new EventEmitter<NgbDatepickerNavigateEvent>();

disabled = false;

onChange = (_: any) => {};
Expand Down Expand Up @@ -274,6 +305,17 @@ export class NgbDatepicker implements OnChanges,
}
}

const newDate = newMonths[0].firstDate;
const oldDate = this.months[0] ? this.months[0].firstDate : null;

this.months = newMonths;

// emitting navigation event if the first month changes
if (!newDate.equals(oldDate)) {
this.navigate.emit({
current: oldDate ? {year: oldDate.year, month: oldDate.month} : null,
next: {year: newDate.year, month: newDate.month}
});
}
}
}

0 comments on commit 1639626

Please sign in to comment.