Skip to content

Commit

Permalink
fix(datepicker): 'startDate' change was preventing arrow navigation (#…
Browse files Browse the repository at this point in the history
…3179)

Fixes #3178
  • Loading branch information
maxokorokov committed May 7, 2019
1 parent 3e1dba9 commit 8bb6ddd
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
11 changes: 11 additions & 0 deletions src/datepicker/datepicker-service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,17 @@ describe('ngb-datepicker-service', () => {
expect(model.months[0]).toBe(month);
expect(getDay(0).date).toBe(date);
});

it(`should not rebuild anything when opening dates from the same month`, () => {
service.open(new NgbDate(2017, 5, 5));
expect(model.months.length).toBe(1);
expect(model.firstDate).toEqual(new NgbDate(2017, 5, 1));
expect(mock.onNext).toHaveBeenCalledTimes(1);

service.open(new NgbDate(2017, 5, 5)); // new object, same date
service.open(new NgbDate(2017, 5, 1)); // another date
expect(mock.onNext).toHaveBeenCalledTimes(1);
});
});

describe(`selection handling`, () => {
Expand Down
3 changes: 2 additions & 1 deletion src/datepicker/datepicker-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
checkDateInRange,
checkMinBeforeMax,
isChangedDate,
isChangedMonth,
isDateSelectable,
generateSelectBoxYears,
generateSelectBoxMonths,
Expand Down Expand Up @@ -128,7 +129,7 @@ export class NgbDatepickerService {

open(date: NgbDate) {
const firstDate = this.toValidDate(date, this._calendar.getToday());
if (!this._state.disabled) {
if (!this._state.disabled && (!this._state.firstDate || isChangedMonth(this._state.firstDate, date))) {
this._nextState({firstDate});
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/datepicker/datepicker-tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
dateComparator,
generateSelectBoxMonths,
getFirstViewDate,
isChangedMonth,
isDateSelectable,
generateSelectBoxYears
} from './datepicker-tools';
Expand Down Expand Up @@ -624,4 +625,24 @@ describe(`datepicker-tools`, () => {
});
});

describe(`isChangedMonth()`, () => {

it(`should compare valid dates`, () => {
expect(isChangedMonth(new NgbDate(2017, 1, 1), new NgbDate(2017, 1, 1))).toBe(false);
expect(isChangedMonth(new NgbDate(2017, 1, 1), new NgbDate(2017, 1, 10))).toBe(false);
expect(isChangedMonth(new NgbDate(2017, 1, 1), new NgbDate(2017, 2, 1))).toBe(true);
expect(isChangedMonth(new NgbDate(2017, 1, 1), new NgbDate(2018, 1, 1))).toBe(true);
expect(isChangedMonth(new NgbDate(2017, 1, 1), new NgbDate(2018, 2, 1))).toBe(true);
});

it(`should compare invalid dates`, () => {
expect(isChangedMonth(undefined, undefined)).toBe(false);
expect(isChangedMonth(null, null)).toBe(false);

expect(isChangedMonth(new NgbDate(2017, 5, 2), null)).toBe(true);
expect(isChangedMonth(new NgbDate(2017, 5, 2), undefined)).toBe(true);
expect(isChangedMonth(null, new NgbDate(2017, 5, 2))).toBe(true);
expect(isChangedMonth(undefined, new NgbDate(2017, 5, 2))).toBe(true);
});
});
});
4 changes: 4 additions & 0 deletions src/datepicker/datepicker-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export function isChangedDate(prev: NgbDate, next: NgbDate) {
return !dateComparator(prev, next);
}

export function isChangedMonth(prev: NgbDate, next: NgbDate) {
return !prev && !next ? false : !prev || !next ? true : prev.year !== next.year || prev.month !== next.month;
}

export function dateComparator(prev: NgbDate, next: NgbDate) {
return (!prev && !next) || (!!prev && !!next && prev.equals(next));
}
Expand Down
33 changes: 31 additions & 2 deletions src/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,22 @@ describe('ngb-datepicker', () => {
expect(getYearSelect(fixture.nativeElement).value).toBe(currentYear);
});

it(`should allow navigation work when startDate value changes`, () => {
const fixture = createTestComponent(`<ngb-datepicker [startDate]="getDate()"></ngb-datepicker>`);

expect(getMonthSelect(fixture.nativeElement).value).toBe('8');
expect(getYearSelect(fixture.nativeElement).value).toBe('2016');

const navigation = getNavigationLinks(fixture.nativeElement);

// JUL 2016
navigation[0].click();
fixture.detectChanges();

expect(getMonthSelect(fixture.nativeElement).value).toBe('7');
expect(getYearSelect(fixture.nativeElement).value).toBe('2016');
});

it('should allow infinite navigation when min/max dates are not set', () => {
const fixture = createTestComponent(`<ngb-datepicker [startDate]="date"></ngb-datepicker>`);

Expand Down Expand Up @@ -260,9 +276,10 @@ describe('ngb-datepicker', () => {

it('should handle minDate edge case values', () => {
const fixture = createTestComponent(`<ngb-datepicker [minDate]="minDate" [startDate]="date"></ngb-datepicker>`);
const datepicker = fixture.debugElement.query(By.directive(NgbDatepicker)).injector.get(NgbDatepicker);

function expectMinDate(year: number, month: number) {
fixture.componentInstance.date = {year: 1000, month: 1};
datepicker.navigateTo({year: 1000, month: 1});
fixture.detectChanges();
expect(getMonthSelect(fixture.nativeElement).value).toBe(`${month}`);
expect(getYearSelect(fixture.nativeElement).value).toBe(`${year}`);
Expand All @@ -272,30 +289,36 @@ describe('ngb-datepicker', () => {

// resetting
fixture.componentInstance.minDate = <any>{};
fixture.detectChanges();
expectMinDate(1000, 1);

// resetting
fixture.componentInstance.minDate = <any>new Date();
fixture.detectChanges();
expectMinDate(1000, 1);

// resetting
fixture.componentInstance.minDate = new NgbDate(3000000, 1, 1);
fixture.detectChanges();
expectMinDate(1000, 1);

// resetting
fixture.componentInstance.minDate = null;
fixture.detectChanges();
expectMinDate(1000, 1);

// resetting
fixture.componentInstance.minDate = undefined;
fixture.detectChanges();
expectMinDate(1000, 1);
});

it('should handle maxDate edge case values', () => {
const fixture = createTestComponent(`<ngb-datepicker [maxDate]="maxDate" [startDate]="date"></ngb-datepicker>`);
const datepicker = fixture.debugElement.query(By.directive(NgbDatepicker)).injector.get(NgbDatepicker);

function expectMaxDate(year: number, month: number) {
fixture.componentInstance.date = {year: 10000, month: 1};
datepicker.navigateTo({year: 10000, month: 1});
fixture.detectChanges();
expect(getMonthSelect(fixture.nativeElement).value).toBe(`${month}`);
expect(getYearSelect(fixture.nativeElement).value).toBe(`${year}`);
Expand All @@ -305,22 +328,27 @@ describe('ngb-datepicker', () => {

// resetting
fixture.componentInstance.maxDate = <any>{};
fixture.detectChanges();
expectMaxDate(10000, 1);

// resetting
fixture.componentInstance.maxDate = <any>new Date();
fixture.detectChanges();
expectMaxDate(10000, 1);

// resetting
fixture.componentInstance.maxDate = new NgbDate(3000000, 1, 1);
fixture.detectChanges();
expectMaxDate(10000, 1);

// resetting
fixture.componentInstance.maxDate = null;
fixture.detectChanges();
expectMaxDate(10000, 1);

// resetting
fixture.componentInstance.maxDate = undefined;
fixture.detectChanges();
expectMaxDate(10000, 1);
});

Expand Down Expand Up @@ -1184,5 +1212,6 @@ class TestComponent {
markDisabled = (date: NgbDateStruct) => { return NgbDate.from(date).equals(new NgbDate(2016, 8, 22)); };
onNavigate = () => {};
onSelect = () => {};
getDate = () => ({year: 2016, month: 8});
onPreventableNavigate = (event: NgbDatepickerNavigateEvent) => event.preventDefault();
}
7 changes: 5 additions & 2 deletions src/datepicker/datepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {NgbDatepickerConfig} from './datepicker-config';
import {NgbDateAdapter} from './adapters/ngb-date-adapter';
import {NgbDateStruct} from './ngb-date-struct';
import {NgbDatepickerI18n} from './datepicker-i18n';
import {isChangedDate} from './datepicker-tools';
import {isChangedDate, isChangedMonth} from './datepicker-tools';
import {hasClassName} from '../util/util';

const NGB_DATEPICKER_VALUE_ACCESSOR = {
Expand Down Expand Up @@ -355,7 +355,10 @@ export class NgbDatepicker implements OnDestroy,
.forEach(input => this._service[input] = this[input]);

if ('startDate' in changes) {
this.navigateTo(this.startDate);
const {currentValue, previousValue} = changes.startDate;
if (isChangedMonth(previousValue, currentValue)) {
this.navigateTo(this.startDate);
}
}
}

Expand Down

0 comments on commit 8bb6ddd

Please sign in to comment.