Skip to content

Commit

Permalink
feat(datepicker): keep day/month when adding/removing month/year in n…
Browse files Browse the repository at this point in the history
…gb-calendar

(cherry picked from commit 497a9c017a8c4b3d50f5b9cea1e7e594ce7b322b)
  • Loading branch information
divdavem authored and gpolychronis-amadeus committed Sep 24, 2019
1 parent 0186404 commit cca081b
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 36 deletions.
16 changes: 8 additions & 8 deletions e2e-app/src/app/datepicker/focus/datepicker-focus.e2e-spec.ts
Expand Up @@ -262,36 +262,36 @@ describe('Datepicker', () => {
await expectFocused(page.getDayElement(new Date(2018, 7, 31)), `Last day of month should be focused`);
});

it(`should focus first day of previous month with 'PageUp'`, async() => {
it(`should focus same day of previous month with 'PageUp'`, async() => {
await page.preSelectDate(); // 10 AUG 2018
await page.openDatepicker();

await sendKey(Key.PAGE_UP);
await expectFocused(page.getDayElement(new Date(2018, 6, 1)), `First day of previous month should be focused`);
await expectFocused(page.getDayElement(new Date(2018, 6, 10)), `Same day of previous month should be focused`);
});

it(`should focus first day of next month with 'PageDown'`, async() => {
it(`should focus same day of next month with 'PageDown'`, async() => {
await page.preSelectDate(); // 10 AUG 2018
await page.openDatepicker();

await sendKey(Key.PAGE_DOWN);
await expectFocused(page.getDayElement(new Date(2018, 8, 1)), `First day of next month should be focused`);
await expectFocused(page.getDayElement(new Date(2018, 8, 10)), `Same day of next month should be focused`);
});

it(`should focus first day of previous year with 'Shift+PageUp'`, async() => {
it(`should focus same day of previous year with 'Shift+PageUp'`, async() => {
await page.preSelectDate(); // 10 AUG 2018
await page.openDatepicker();

await sendKey(Key.SHIFT, Key.PAGE_UP);
await expectFocused(page.getDayElement(new Date(2017, 0, 1)), `First day of previous year should be focused`);
await expectFocused(page.getDayElement(new Date(2017, 7, 10)), `Same day of previous year should be focused`);
});

it(`should focus first day of next year with 'Shift+PageDown'`, async() => {
it(`should focus same day of next year with 'Shift+PageDown'`, async() => {
await page.preSelectDate(); // 10 AUG 2018
await page.openDatepicker();

await sendKey(Key.SHIFT, Key.PAGE_DOWN);
await expectFocused(page.getDayElement(new Date(2019, 0, 1)), `First day of next year should be focused`);
await expectFocused(page.getDayElement(new Date(2019, 7, 10)), `Same day of next year should be focused`);
});

it(`should focus min available day with 'Shift+Home'`, async() => {
Expand Down
8 changes: 4 additions & 4 deletions src/datepicker/datepicker-service.spec.ts
Expand Up @@ -1022,20 +1022,20 @@ describe('ngb-datepicker-service', () => {
// months
service.focus(date);
service.focusMove('m', 1);
expect(model.focusDate).toEqual(new NgbDate(2017, 6, 1));
expect(model.focusDate).toEqual(new NgbDate(2017, 6, 5));

service.focus(date);
service.focusMove('m', -1);
expect(model.focusDate).toEqual(new NgbDate(2017, 4, 1));
expect(model.focusDate).toEqual(new NgbDate(2017, 4, 5));

// years
service.focus(date);
service.focusMove('y', 1);
expect(model.focusDate).toEqual(new NgbDate(2018, 1, 1));
expect(model.focusDate).toEqual(new NgbDate(2018, 5, 5));

service.focus(date);
service.focusMove('y', -1);
expect(model.focusDate).toEqual(new NgbDate(2016, 1, 1));
expect(model.focusDate).toEqual(new NgbDate(2016, 5, 5));
});

it(`should move focus when 'minDate' changes`, () => {
Expand Down
8 changes: 1 addition & 7 deletions src/datepicker/datepicker-tools.spec.ts
Expand Up @@ -305,16 +305,12 @@ describe(`datepicker-tools`, () => {
months = buildMonths(calendar, new NgbDate(2018, 6, 5), state, i18n, false);
expect(months).toBe(state.months);
expect(months.length).toBe(2);
// the structures should be swapped:
monthsStructure.push(monthsStructure.shift());
expect(storeMonthsDataStructure(months))['toHaveTheSameMonthDataStructureAs'](monthsStructure);

// previous month
months = buildMonths(calendar, new NgbDate(2018, 5, 5), state, i18n, false);
expect(months).toBe(state.months);
expect(months.length).toBe(2);
// the structures should be swapped (again):
monthsStructure.push(monthsStructure.shift());
expect(storeMonthsDataStructure(months))['toHaveTheSameMonthDataStructureAs'](monthsStructure);

state.displayMonths = 5;
Expand All @@ -328,14 +324,12 @@ describe(`datepicker-tools`, () => {
months = buildMonths(calendar, new NgbDate(2018, 7, 5), state, i18n, false);
expect(months).toBe(state.months);
expect(months.length).toBe(5);
monthsStructure.unshift(...monthsStructure.splice(2, 3));
expect(storeMonthsDataStructure(months))['toHaveTheSameMonthDataStructureAs'](monthsStructure);

// go to two months before, the 3 first months are reused as is
months = buildMonths(calendar, new NgbDate(2018, 5, 5), state, i18n, false);
expect(months).toBe(state.months);
expect(months.length).toBe(5);
monthsStructure.push(...monthsStructure.splice(0, 3));
expect(storeMonthsDataStructure(months))['toHaveTheSameMonthDataStructureAs'](monthsStructure);

// completely change the dates, nothing is shifted in monthsStructure
Expand All @@ -349,7 +343,7 @@ describe(`datepicker-tools`, () => {
months = buildMonths(calendar, new NgbDate(2018, 11, 5), state, i18n, false);
expect(months).toBe(state.months);
expect(months.length).toBe(2);
monthsStructure = monthsStructure.slice(1, 3);
monthsStructure = monthsStructure.slice(0, 2);
expect(storeMonthsDataStructure(months))['toHaveTheSameMonthDataStructureAs'](monthsStructure);
});

Expand Down
5 changes: 4 additions & 1 deletion src/datepicker/datepicker-tools.ts
Expand Up @@ -78,11 +78,14 @@ export function generateSelectBoxYears(date: NgbDate, minDate: NgbDate, maxDate:
}

export function nextMonthDisabled(calendar: NgbCalendar, date: NgbDate, maxDate: NgbDate) {
return maxDate && calendar.getNext(date, 'm').after(maxDate);
const nextDate = calendar.getNext(date, 'm');
nextDate.day = 1;
return maxDate && nextDate.after(maxDate);
}

export function prevMonthDisabled(calendar: NgbCalendar, date: NgbDate, minDate: NgbDate) {
const prevDate = calendar.getPrev(date, 'm');
prevDate.day = 1;
return minDate && (prevDate.year === minDate.year && prevDate.month < minDate.month ||
prevDate.year < minDate.year && minDate.month === 1);
}
Expand Down
12 changes: 6 additions & 6 deletions src/datepicker/datepicker.spec.ts
Expand Up @@ -980,24 +980,24 @@ describe('ngb-datepicker', () => {

triggerKeyDown(getMonthContainer(datepicker), 33 /* page up */);
fixture.detectChanges();
expectFocusedDate(datepicker, new NgbDate(2016, 7, 1));
expectFocusedDate(datepicker, new NgbDate(2016, 7, 2));
expectSelectedDate(datepicker, null);

triggerKeyDown(getMonthContainer(datepicker), 34 /* page down */);
fixture.detectChanges();
expectFocusedDate(datepicker, new NgbDate(2016, 8, 1));
expectFocusedDate(datepicker, new NgbDate(2016, 8, 2));
expectSelectedDate(datepicker, null);

triggerKeyDown(getMonthContainer(datepicker), 34 /* page down */);
fixture.detectChanges();

expectFocusedDate(datepicker, new NgbDate(2016, 9, 1));
expectFocusedDate(datepicker, new NgbDate(2016, 9, 2));
expectSelectedDate(datepicker, null);

triggerKeyDown(getMonthContainer(datepicker), 34 /* page down */);
fixture.detectChanges();
datepicker = fixture.debugElement.query(By.directive(NgbDatepicker));
expectFocusedDate(datepicker, new NgbDate(2016, 10, 1));
expectFocusedDate(datepicker, new NgbDate(2016, 10, 2));
expectSelectedDate(datepicker, null);
});

Expand All @@ -1016,13 +1016,13 @@ describe('ngb-datepicker', () => {
triggerKeyDown(getMonthContainer(datepicker), 33 /* page up */, true /* shift */);
fixture.detectChanges();

expectFocusedDate(datepicker, new NgbDate(2015, 1, 1), true);
expectFocusedDate(datepicker, new NgbDate(2015, 8, 1), true);
expectSelectedDate(datepicker, null);

triggerKeyDown(getMonthContainer(datepicker), 34 /* page down */, true /* shift */);
fixture.detectChanges();

expectFocusedDate(datepicker, new NgbDate(2016, 1, 1));
expectFocusedDate(datepicker, new NgbDate(2016, 8, 1));
expectSelectedDate(datepicker, null);
});

Expand Down
32 changes: 24 additions & 8 deletions src/datepicker/ngb-calendar.spec.ts
Expand Up @@ -43,25 +43,41 @@ describe('ngb-calendar-gregorian', () => {
});

it('should add months to date', () => {
expect(calendar.getNext(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 8, 1));
expect(calendar.getNext(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 8, 22));
expect(calendar.getNext(new NgbDate(2016, 7, 1), 'm')).toEqual(new NgbDate(2016, 8, 1));
expect(calendar.getNext(new NgbDate(2016, 12, 22), 'm')).toEqual(new NgbDate(2017, 1, 1));
expect(calendar.getNext(new NgbDate(2016, 12, 22), 'm')).toEqual(new NgbDate(2017, 1, 22));
expect(calendar.getNext(new NgbDate(2016, 1, 29), 'm')).toEqual(new NgbDate(2016, 2, 29));
expect(calendar.getNext(new NgbDate(2016, 1, 30), 'm')).toEqual(new NgbDate(2016, 2, 29));
expect(calendar.getNext(new NgbDate(2016, 10, 30), 'm', 6)).toEqual(new NgbDate(2017, 4, 30));
expect(calendar.getNext(new NgbDate(2016, 10, 31), 'm', 6)).toEqual(new NgbDate(2017, 4, 30));
});

it('should subtract months from date', () => {
expect(calendar.getPrev(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 6, 1));
expect(calendar.getPrev(new NgbDate(2016, 7, 22), 'm')).toEqual(new NgbDate(2016, 6, 22));
expect(calendar.getPrev(new NgbDate(2016, 8, 1), 'm')).toEqual(new NgbDate(2016, 7, 1));
expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'm')).toEqual(new NgbDate(2016, 12, 1));
expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'm')).toEqual(new NgbDate(2016, 12, 22));
expect(calendar.getPrev(new NgbDate(2016, 3, 29), 'm')).toEqual(new NgbDate(2016, 2, 29));
expect(calendar.getPrev(new NgbDate(2016, 3, 30), 'm')).toEqual(new NgbDate(2016, 2, 29));
expect(calendar.getPrev(new NgbDate(2016, 10, 30), 'm', 4)).toEqual(new NgbDate(2016, 6, 30));
expect(calendar.getPrev(new NgbDate(2016, 10, 31), 'm', 4)).toEqual(new NgbDate(2016, 6, 30));
});

it('should add years to date', () => {
expect(calendar.getNext(new NgbDate(2016, 1, 22), 'y')).toEqual(new NgbDate(2017, 1, 1));
expect(calendar.getNext(new NgbDate(2017, 12, 22), 'y')).toEqual(new NgbDate(2018, 1, 1));
expect(calendar.getNext(new NgbDate(2016, 1, 22), 'y')).toEqual(new NgbDate(2017, 1, 22));
expect(calendar.getNext(new NgbDate(2017, 12, 22), 'y')).toEqual(new NgbDate(2018, 12, 22));
expect(calendar.getNext(new NgbDate(2016, 2, 29), 'y')).toEqual(new NgbDate(2017, 2, 28));
expect(calendar.getNext(new NgbDate(2016, 2, 28), 'y')).toEqual(new NgbDate(2017, 2, 28));
expect(calendar.getNext(new NgbDate(2016, 2, 29), 'y', 4)).toEqual(new NgbDate(2020, 2, 29));
expect(calendar.getNext(new NgbDate(2016, 2, 29), 'y', 3)).toEqual(new NgbDate(2019, 2, 28));
});

it('should subtract years from date', () => {
expect(calendar.getPrev(new NgbDate(2016, 12, 22), 'y')).toEqual(new NgbDate(2015, 1, 1));
expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'y')).toEqual(new NgbDate(2016, 1, 1));
expect(calendar.getPrev(new NgbDate(2016, 12, 22), 'y')).toEqual(new NgbDate(2015, 12, 22));
expect(calendar.getPrev(new NgbDate(2017, 1, 22), 'y')).toEqual(new NgbDate(2016, 1, 22));
expect(calendar.getPrev(new NgbDate(2016, 2, 28), 'y')).toEqual(new NgbDate(2015, 2, 28));
expect(calendar.getPrev(new NgbDate(2016, 2, 29), 'y')).toEqual(new NgbDate(2015, 2, 28));
expect(calendar.getPrev(new NgbDate(2016, 2, 29), 'y', 4)).toEqual(new NgbDate(2012, 2, 29));
expect(calendar.getPrev(new NgbDate(2016, 2, 29), 'y', 3)).toEqual(new NgbDate(2013, 2, 28));
});

it('should properly recognize invalid javascript date', () => {
Expand Down
19 changes: 17 additions & 2 deletions src/datepicker/ngb-calendar.ts
Expand Up @@ -98,20 +98,35 @@ export class NgbCalendarGregorian extends NgbCalendar {

getNext(date: NgbDate, period: NgbPeriod = 'd', number = 1) {
let jsDate = toJSDate(date);
let checkMonth = true;
let expectedMonth = jsDate.getMonth();

switch (period) {
case 'y':
return new NgbDate(date.year + number, 1, 1);
jsDate.setFullYear(jsDate.getFullYear() + number);
break;
case 'm':
jsDate = new Date(date.year, date.month + number - 1, 1, 12);
expectedMonth += number;
jsDate.setMonth(expectedMonth);
expectedMonth = expectedMonth % 12;
if (expectedMonth < 0) {
expectedMonth = expectedMonth + 12;
}
break;
case 'd':
jsDate.setDate(jsDate.getDate() + number);
checkMonth = false;
break;
default:
return date;
}

if (checkMonth && jsDate.getMonth() !== expectedMonth) {
// this means the destination month has less days than the initial month
// let's go back to the end of the previous month:
jsDate.setDate(0);
}

return fromJSDate(jsDate);
}

Expand Down

0 comments on commit cca081b

Please sign in to comment.