Skip to content

Commit

Permalink
refactor(datepicker): use ISO 8601 for month and weekday numbers (#797)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: now datepicker uses ISO 8601 for month and weekday numbers with default calendar
Before
0=Jan; 1=Feb; ... 11=Dec
0=Sun; 1=Mon; ... 6=Sat

Now
1=Jan; 2=Feb; ... 12=Dec
1=Mon; 2=Tue; ... 7=Sun

Fixes #728 
Closes #797
  • Loading branch information
maxokorokov committed Sep 26, 2016
1 parent a7cea35 commit a173e40
Show file tree
Hide file tree
Showing 17 changed files with 114 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<button class="btn btn-sm btn-outline-primary" (click)="selectToday()">Select Today</button>
<button class="btn btn-sm btn-outline-primary" (click)="dp.navigateTo()">To current month</button>
<button class="btn btn-sm btn-outline-primary" (click)="dp.navigateTo({year: 2013, month: 1})">To Feb 2013</button>
<button class="btn btn-sm btn-outline-primary" (click)="dp.navigateTo({year: 2013, month: 2})">To Feb 2013</button>

<hr/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ export class NgbdDatepickerBasic {
model: NgbDateStruct;

selectToday() {
this.model = {year: now.getFullYear(), month: now.getMonth(), day: now.getDate()};
this.model = {year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate()};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ export class NgbdDatepickerConfig {

constructor(config: NgbDatepickerConfig) {
// customize default values of datepickers used by this component tree
config.minDate = {year: 1900, month: 0, day: 1};
config.maxDate = {year: 2099, month: 11, day: 31};
config.minDate = {year: 1900, month: 1, day: 1};
config.maxDate = {year: 2099, month: 12, day: 31};

// weekends are disabled
config.markDisabled = (date: NgbDateStruct) => {
const d = new Date(date.year, date.month, date.day);
const d = new Date(date.year, date.month - 1, date.day);
return d.getDay() === 0 || d.getDay() === 6;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class NgbdDatepickerCustomDay {
model: NgbDateStruct;

isWeekend(date: NgbDateStruct) {
const d = new Date(date.year, date.month, date.day);
const d = new Date(date.year, date.month - 1, date.day);
return d.getDay() === 0 || d.getDay() === 6;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {NgbDatepickerI18n} from '@ng-bootstrap/ng-bootstrap';

const I18N_VALUES = {
en: {
weekdays: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
weekdays: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
},
fr: {
weekdays: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'],
weekdays: ['Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa', 'Di'],
months: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Aou', 'Sep', 'Oct', 'Nov', 'Déc'],
}
};
Expand All @@ -27,10 +27,10 @@ export class CustomDatepickerI18n extends NgbDatepickerI18n {
}

getWeekdayName(weekday: number): string {
return I18N_VALUES[this._i18n.language].weekdays[weekday];
return I18N_VALUES[this._i18n.language].weekdays[weekday - 1];
}
getMonthName(month: number): string {
return I18N_VALUES[this._i18n.language].months[month];
return I18N_VALUES[this._i18n.language].months[month - 1];
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/datepicker/datepicker-i18n.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ describe('ngb-datepicker-i18n-default', () => {
const i18n = new NgbDatepickerI18nDefault();

it('should return month name', () => {
expect(i18n.getMonthName(0)).toBe('Jan');
expect(i18n.getMonthName(11)).toBe('Dec');
expect(i18n.getMonthName(12)).toBe(undefined);
expect(i18n.getMonthName(0)).toBe(undefined);
expect(i18n.getMonthName(1)).toBe('Jan');
expect(i18n.getMonthName(12)).toBe('Dec');
expect(i18n.getMonthName(13)).toBe(undefined);
});

it('should return weekday name', () => {
expect(i18n.getWeekdayName(0)).toBe('Su');
expect(i18n.getWeekdayName(6)).toBe('Sa');
expect(i18n.getWeekdayName(7)).toBe(undefined);
expect(i18n.getWeekdayName(0)).toBe(undefined);
expect(i18n.getWeekdayName(1)).toBe('Mo');
expect(i18n.getWeekdayName(7)).toBe('Su');
expect(i18n.getWeekdayName(8)).toBe(undefined);
});

});
10 changes: 5 additions & 5 deletions src/datepicker/datepicker-i18n.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Injectable} from '@angular/core';

const WEEKDAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
const WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

/**
Expand All @@ -11,20 +11,20 @@ const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', '
export abstract class NgbDatepickerI18n {
/**
* Returns the short week day name to display in the heading of the month view.
* `weekday` is 0 for Sunday, 1 for Monday, etc.
* With default calendar we use ISO 8601: 'weekday' is 1=Mon ... 7=Sun
*/
abstract getWeekdayName(weekday: number): string;

/**
* Returns the month name to display in the date picker navigation.
* `month` is 0 for January, 1 for February, etc.
* With default calendar we use ISO 8601: 'month' is 1=Jan ... 12=Dec
*/
abstract getMonthName(month: number): string;
}

@Injectable()
export class NgbDatepickerI18nDefault extends NgbDatepickerI18n {
getWeekdayName(weekday: number): string { return WEEKDAYS[weekday]; }
getWeekdayName(weekday: number): string { return WEEKDAYS[weekday - 1]; }

getMonthName(month: number): string { return MONTHS[month]; }
getMonthName(month: number): string { return MONTHS[month - 1]; }
}
6 changes: 3 additions & 3 deletions src/datepicker/datepicker-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ describe('NgbInputDatepicker', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [ngModel]="date">`);
const input = fixture.nativeElement.querySelector('input');

fixture.componentInstance.date = {year: 2016, month: 9, day: 10};
fixture.componentInstance.date = {year: 2016, month: 10, day: 10};
fixture.detectChanges();
tick();
expect(input.value).toBe('2016-10-10');

fixture.componentInstance.date = {year: 2016, month: 9, day: 15};
fixture.componentInstance.date = {year: 2016, month: 10, day: 15};
fixture.detectChanges();
tick();
expect(input.value).toBe('2016-10-15');
Expand All @@ -70,7 +70,7 @@ describe('NgbInputDatepicker', () => {
const inputDebugEl = fixture.debugElement.query(By.css('input'));

inputDebugEl.triggerEventHandler('change', {target: {value: '2016-09-10'}});
expect(fixture.componentInstance.date).toEqual({year: 2016, month: 8, day: 10});
expect(fixture.componentInstance.date).toEqual({year: 2016, month: 9, day: 10});
});

it('should propagate null to model when a user enters invalid date', () => {
Expand Down
12 changes: 8 additions & 4 deletions src/datepicker/datepicker-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class NgbInputDatepicker implements ControlValueAccessor {
@Input() dayTemplate: TemplateRef<DayTemplateContext>;

/**
* First day of the week, 0=Sun, 1=Mon, etc.
* First day of the week. With default calendar we use ISO 8601: 1=Mon ... 7=Sun
*/
@Input() firstDayOfWeek: number;

Expand All @@ -67,7 +67,7 @@ export class NgbInputDatepicker implements ControlValueAccessor {
@Input() maxDate: NgbDateStruct;

/**
* Whether to display navigation or not
* Whether to display navigation
*/
@Input() showNavigation: boolean;

Expand All @@ -82,7 +82,9 @@ export class NgbInputDatepicker implements ControlValueAccessor {
@Input() showWeekNumbers: boolean;

/**
* Date to open calendar with. If nothing provided, calendar will open with current month.
* Date to open calendar with.
* With default calendar we use ISO 8601: 'month' is 1=Jan ... 12=Dec.
* If nothing provided, calendar will open with current month.
* Use 'navigateTo(date)' as an alternative
*/
@Input() startDate: {year: number, month: number};
Expand Down Expand Up @@ -172,7 +174,9 @@ export class NgbInputDatepicker implements ControlValueAccessor {
}

/**
* Navigates current view to provided date. If nothing provided calendar will open current month.
* Navigates current view to provided date.
* With default calendar we use ISO 8601: 'month' is 1=Jan ... 12=Dec.
* If nothing provided calendar will open current month.
* Use 'startDate' input as an alternative
*/
navigateTo(date?: {year: number, month: number}) {
Expand Down
2 changes: 1 addition & 1 deletion src/datepicker/datepicker-navigation-select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('ngb-datepicker-navigation-select', () => {
createTestComponent(`<ngb-datepicker-navigation-select [date]="date" [minYear]="minYear" [maxYear]="maxYear">`);

expect(getOptionValues(getMonthSelect(fixture.nativeElement))).toEqual([
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'
]);
});

Expand Down
44 changes: 22 additions & 22 deletions src/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ function customizeConfig(config: NgbDatepickerConfig) {
config.dayTemplate = {} as TemplateRef<DayTemplateContext>;
config.firstDayOfWeek = 2;
config.markDisabled = (date) => false;
config.minDate = {year: 2000, month: 0, day: 1};
config.maxDate = {year: 2030, month: 11, day: 31};
config.minDate = {year: 2000, month: 1, day: 1};
config.maxDate = {year: 2030, month: 12, day: 31};
config.showNavigation = false;
config.showWeekdays = false;
config.showWeekNumbers = true;
Expand All @@ -68,7 +68,7 @@ describe('ngb-datepicker', () => {
const fixture = createTestComponent(`<ngb-datepicker></ngb-datepicker>`);

const today = new Date();
expect(getMonthSelect(fixture.nativeElement).value).toBe(`${today.getMonth()}`);
expect(getMonthSelect(fixture.nativeElement).value).toBe(`${today.getMonth() + 1}`);
expect(getYearSelect(fixture.nativeElement).value).toBe(`${today.getFullYear()}`);
});

Expand All @@ -90,8 +90,8 @@ describe('ngb-datepicker', () => {
const fixture = createTestComponent(
`<ngb-datepicker [startDate]="date" [minDate]="minDate" [maxDate]="maxDate"></ngb-datepicker>`);

fixture.componentInstance.minDate = {year: 2016, month: 7, day: 20};
fixture.componentInstance.maxDate = {year: 2016, month: 7, day: 25};
fixture.componentInstance.minDate = {year: 2016, month: 8, day: 20};
fixture.componentInstance.maxDate = {year: 2016, month: 8, day: 25};
fixture.detectChanges();

// 19 AUG 2016
Expand All @@ -112,10 +112,10 @@ describe('ngb-datepicker', () => {

const dates = getDates(fixture.nativeElement);
dates[0].click(); // 1 AUG 2016
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 7, day: 1});
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 8, day: 1});

dates[1].click();
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 7, day: 2});
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 8, day: 2});
});

it('should not update model based on calendar clicks when disabled', async(() => {
Expand Down Expand Up @@ -143,7 +143,7 @@ describe('ngb-datepicker', () => {
const fixture = createTestComponent(
`<ngb-datepicker [startDate]="date" [minDate]="minDate" [maxDate]="maxDate" [(ngModel)]="model"></ngb-datepicker>`);

fixture.componentInstance.model = {year: 2016, month: 7, day: 1};
fixture.componentInstance.model = {year: 2016, month: 8, day: 1};

fixture.detectChanges();
fixture.whenStable()
Expand All @@ -154,7 +154,7 @@ describe('ngb-datepicker', () => {
.then(() => {
expect(getDay(fixture.nativeElement, 0)).toHaveCssClass('bg-primary');

fixture.componentInstance.model = {year: 2016, month: 7, day: 2};
fixture.componentInstance.model = {year: 2016, month: 8, day: 2};
fixture.detectChanges();
return fixture.whenStable();
})
Expand All @@ -177,7 +177,7 @@ describe('ngb-datepicker', () => {
let dates = getDates(fixture.nativeElement);

dates[31].click(); // 1 SEP 2016
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 8, day: 1});
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 9, day: 1});

// month changes to SEP
fixture.detectChanges();
Expand All @@ -194,38 +194,38 @@ describe('ngb-datepicker', () => {
const navigation = getNavigationLinks(fixture.nativeElement);

dates[0].click(); // 1 AUG 2016
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 7, day: 1});
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 8, day: 1});

// PREV
navigation[0].click();
fixture.detectChanges();
dates = getDates(fixture.nativeElement);
dates[4].click(); // 1 JUL 2016
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 6, day: 1});
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 7, day: 1});

// NEXT
navigation[1].click();
fixture.detectChanges();
dates = getDates(fixture.nativeElement);
dates[0].click(); // 1 AUG 2016
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 7, day: 1});
expect(fixture.componentInstance.model).toEqual({year: 2016, month: 8, day: 1});
});

it('should switch month using navigateTo({date})', () => {
const fixture = createTestComponent(
`<ngb-datepicker #dp [startDate]="date" [minDate]="minDate" [maxDate]="maxDate" [(ngModel)]="model"></ngb-datepicker>
<button id="btn"(click)="dp.navigateTo({year: 2015, month: 5})"></button>`);
<button id="btn"(click)="dp.navigateTo({year: 2015, month: 6})"></button>`);

const button = fixture.nativeElement.querySelector('button#btn');
button.click();

fixture.detectChanges();
expect(getMonthSelect(fixture.nativeElement).value).toBe('5');
expect(getMonthSelect(fixture.nativeElement).value).toBe('6');
expect(getYearSelect(fixture.nativeElement).value).toBe('2015');

const dates = getDates(fixture.nativeElement);
dates[0].click(); // 1 JUN 2015
expect(fixture.componentInstance.model).toEqual({year: 2015, month: 5, day: 1});
expect(fixture.componentInstance.model).toEqual({year: 2015, month: 6, day: 1});
});

it('should switch to current month using navigateTo() without arguments', () => {
Expand All @@ -238,7 +238,7 @@ describe('ngb-datepicker', () => {

fixture.detectChanges();
const today = new Date();
expect(getMonthSelect(fixture.nativeElement).value).toBe(`${today.getMonth()}`);
expect(getMonthSelect(fixture.nativeElement).value).toBe(`${today.getMonth() + 1}`);
expect(getYearSelect(fixture.nativeElement).value).toBe(`${today.getFullYear()}`);
});

Expand Down Expand Up @@ -286,7 +286,7 @@ describe('ngb-datepicker', () => {
expect(getDatepicker(compiled)).toHaveCssClass('ng-invalid');
expect(getDatepicker(compiled)).not.toHaveCssClass('ng-valid');

fixture.componentInstance.model = {year: 2016, month: 7, day: 1};
fixture.componentInstance.model = {year: 2016, month: 8, day: 1};
fixture.detectChanges();
return fixture.whenStable();
})
Expand Down Expand Up @@ -381,11 +381,11 @@ describe('ngb-datepicker', () => {

@Component({selector: 'test-cmp', template: ''})
class TestComponent {
date = {year: 2016, month: 7};
minDate: NgbDateStruct = {year: 2010, month: 0, day: 1};
maxDate: NgbDateStruct = {year: 2020, month: 11, day: 31};
date = {year: 2016, month: 8};
minDate: NgbDateStruct = {year: 2010, month: 1, day: 1};
maxDate: NgbDateStruct = {year: 2020, month: 12, day: 31};
form = new FormGroup({control: new FormControl('', Validators.required)});
disabledForm = new FormGroup({control: new FormControl({value: null, disabled: true})});
model;
markDisabled = (date: NgbDateStruct) => { return NgbDate.from(date).equals(new NgbDate(2016, 7, 22)); };
markDisabled = (date: NgbDateStruct) => { return NgbDate.from(date).equals(new NgbDate(2016, 8, 22)); };
}
Loading

0 comments on commit a173e40

Please sign in to comment.