From 36d0302fb7b452345c28780ba5372dd3cace9cbb Mon Sep 17 00:00:00 2001 From: ddivernois Date: Tue, 31 Jan 2017 17:20:34 +0100 Subject: [PATCH] feat(datepicker): keep day/month when adding/removing month/year in ngb-calendar --- .../focus/datepicker-focus.e2e-spec.ts | 16 +++++----- src/datepicker/datepicker-service.spec.ts | 8 ++--- src/datepicker/datepicker-tools.spec.ts | 8 +---- src/datepicker/datepicker-tools.ts | 5 ++- src/datepicker/datepicker.spec.ts | 12 +++---- src/datepicker/ngb-calendar.spec.ts | 32 ++++++++++++++----- src/datepicker/ngb-calendar.ts | 19 +++++++++-- 7 files changed, 64 insertions(+), 36 deletions(-) diff --git a/e2e-app/src/app/datepicker/focus/datepicker-focus.e2e-spec.ts b/e2e-app/src/app/datepicker/focus/datepicker-focus.e2e-spec.ts index b50d7ea480..8794435035 100644 --- a/e2e-app/src/app/datepicker/focus/datepicker-focus.e2e-spec.ts +++ b/e2e-app/src/app/datepicker/focus/datepicker-focus.e2e-spec.ts @@ -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 'Alt+PageUp'`, async() => { + it(`should focus same day of previous year with 'Alt+PageUp'`, async() => { await page.preSelectDate(); // 10 AUG 2018 await page.openDatepicker(); await sendKey(Key.ALT, 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 'Alt+PageDown'`, async() => { + it(`should focus same day of next year with 'Alt+PageDown'`, async() => { await page.preSelectDate(); // 10 AUG 2018 await page.openDatepicker(); await sendKey(Key.ALT, 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 'Alt+Home'`, async() => { diff --git a/src/datepicker/datepicker-service.spec.ts b/src/datepicker/datepicker-service.spec.ts index 0538876d14..722e0d663f 100644 --- a/src/datepicker/datepicker-service.spec.ts +++ b/src/datepicker/datepicker-service.spec.ts @@ -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`, () => { diff --git a/src/datepicker/datepicker-tools.spec.ts b/src/datepicker/datepicker-tools.spec.ts index 89916c51be..06636ca7b9 100644 --- a/src/datepicker/datepicker-tools.spec.ts +++ b/src/datepicker/datepicker-tools.spec.ts @@ -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; @@ -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 @@ -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); }); diff --git a/src/datepicker/datepicker-tools.ts b/src/datepicker/datepicker-tools.ts index 29eb5d0596..0149013571 100644 --- a/src/datepicker/datepicker-tools.ts +++ b/src/datepicker/datepicker-tools.ts @@ -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); } diff --git a/src/datepicker/datepicker.spec.ts b/src/datepicker/datepicker.spec.ts index c926d39182..68748ba022 100644 --- a/src/datepicker/datepicker.spec.ts +++ b/src/datepicker/datepicker.spec.ts @@ -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); }); @@ -1016,13 +1016,13 @@ describe('ngb-datepicker', () => { triggerKeyDown(getMonthContainer(datepicker), 33 /* page up */, true /* alt */); 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 /* alt */); fixture.detectChanges(); - expectFocusedDate(datepicker, new NgbDate(2016, 1, 1)); + expectFocusedDate(datepicker, new NgbDate(2016, 8, 1)); expectSelectedDate(datepicker, null); }); diff --git a/src/datepicker/ngb-calendar.spec.ts b/src/datepicker/ngb-calendar.spec.ts index 781decf748..adae02de9e 100644 --- a/src/datepicker/ngb-calendar.spec.ts +++ b/src/datepicker/ngb-calendar.spec.ts @@ -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', () => { diff --git a/src/datepicker/ngb-calendar.ts b/src/datepicker/ngb-calendar.ts index f42a1debd1..0c71a3484d 100644 --- a/src/datepicker/ngb-calendar.ts +++ b/src/datepicker/ngb-calendar.ts @@ -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); }