Skip to content

Commit

Permalink
feat(datepicker): can easily hide days outside of current month
Browse files Browse the repository at this point in the history
Closes #937
  • Loading branch information
maxokorokov authored and pkozlowski-opensource committed Oct 21, 2016
1 parent 54cd30b commit a92c0e7
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export class NgbdDatepickerConfig {
config.minDate = {year: 1900, month: 1, day: 1};
config.maxDate = {year: 2099, month: 12, day: 31};

// days that don't belong to current month are not visible
config.outsideDays = 'hidden';

// weekends are disabled
config.markDisabled = (date: NgbDateStruct) => {
const d = new Date(date.year, date.month - 1, date.day);
Expand Down
1 change: 1 addition & 0 deletions src/datepicker/datepicker-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('ngb-datepicker-config', () => {
expect(config.markDisabled).toBeUndefined();
expect(config.minDate).toBeUndefined();
expect(config.maxDate).toBeUndefined();
expect(config.outsideDays).toBe('visible');
expect(config.showNavigation).toBe(true);
expect(config.showWeekdays).toBe(true);
expect(config.showWeekNumbers).toBe(false);
Expand Down
1 change: 1 addition & 0 deletions src/datepicker/datepicker-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class NgbDatepickerConfig {
markDisabled: (date: NgbDateStruct, current: {year: number, month: number}) => boolean;
minDate: NgbDateStruct;
maxDate: NgbDateStruct;
outsideDays: 'visible' | 'collapsed' | 'hidden' = 'visible';
showNavigation = true;
showWeekdays = true;
showWeekNumbers = false;
Expand Down
11 changes: 11 additions & 0 deletions src/datepicker/datepicker-input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,17 @@ describe('NgbInputDatepicker', () => {
expect(dp.maxDate).toEqual({year: 2016, month: 9, day: 13});
});

it('should propagate the "outsideDays" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker outsideDays="collapsed">`);
const dpInput = fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);

dpInput.open();
fixture.detectChanges();

const dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
expect(dp.outsideDays).toEqual('collapsed');
});

it('should propagate the "showNavigation" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [showNavigation]="true">`);
const dpInput = fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);
Expand Down
10 changes: 8 additions & 2 deletions src/datepicker/datepicker-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export class NgbInputDatepicker implements ControlValueAccessor {
*/
@Input() maxDate: NgbDateStruct;

/**
* The way to display days that don't belong to current month: `visible` (default),
* `hidden` (not displayed) or `collapsed` (not displayed with empty space collapsed)
*/
@Input() outsideDays: 'visible' | 'collapsed' | 'hidden';

/**
* Whether to display navigation
*/
Expand Down Expand Up @@ -184,8 +190,8 @@ export class NgbInputDatepicker implements ControlValueAccessor {
}

private _applyDatepickerInputs(datepickerInstance: NgbDatepicker): void {
['dayTemplate', 'firstDayOfWeek', 'markDisabled', 'minDate', 'maxDate', 'showNavigation', 'showWeekdays',
'showWeekNumbers']
['dayTemplate', 'firstDayOfWeek', 'markDisabled', 'minDate', 'maxDate', 'outsideDays', 'showNavigation',
'showWeekdays', 'showWeekNumbers']
.forEach((optionName: string) => {
if (this[optionName] !== undefined) {
datepickerInstance[optionName] = this[optionName];
Expand Down
49 changes: 47 additions & 2 deletions src/datepicker/datepicker-month-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('ngbDatepickerMonthView', () => {
<template #tpl let-date="date">{{ date.day }}</template>
<tbody ngbDatepickerMonthView [month]="month" [dayTemplate]="tpl"></tbody>
`);
expectDates(fixture.nativeElement, ['22']);
expectDates(fixture.nativeElement, ['22', '23']);
});

it('should send date selection events', () => {
Expand Down Expand Up @@ -163,8 +163,49 @@ describe('ngbDatepickerMonthView', () => {
const dates = getDates(fixture.nativeElement);
dates.forEach((date) => expect(window.getComputedStyle(date).getPropertyValue('cursor')).toBe('default'));
});

it('should set default cursor for other months days', () => {
const fixture =
createTestComponent('<tbody ngbDatepickerMonthView [month]="month" [outsideDays]="outsideDays"></tbody>');

const dates = getDates(fixture.nativeElement);
expect(window.getComputedStyle(dates[1]).getPropertyValue('cursor')).toBe('pointer');

fixture.componentInstance.outsideDays = 'collapsed';
fixture.detectChanges();
expect(window.getComputedStyle(dates[1]).getPropertyValue('cursor')).toBe('default');

fixture.componentInstance.outsideDays = 'hidden';
fixture.detectChanges();
expect(window.getComputedStyle(dates[1]).getPropertyValue('cursor')).toBe('default');
});
}

it('should apply proper visibility to other months days', () => {
const fixture =
createTestComponent('<tbody ngbDatepickerMonthView [month]="month" [outsideDays]="outsideDays"></tbody>');

let dates = getDates(fixture.nativeElement);
expect(dates[0]).not.toHaveCssClass('hidden');
expect(dates[0]).not.toHaveCssClass('collapsed');
expect(dates[1]).not.toHaveCssClass('hidden');
expect(dates[1]).not.toHaveCssClass('collapsed');

fixture.componentInstance.outsideDays = 'collapsed';
fixture.detectChanges();
expect(dates[0]).not.toHaveCssClass('hidden');
expect(dates[0]).not.toHaveCssClass('collapsed');
expect(dates[1]).not.toHaveCssClass('hidden');
expect(dates[1]).toHaveCssClass('collapsed');

fixture.componentInstance.outsideDays = 'hidden';
fixture.detectChanges();
expect(dates[0]).not.toHaveCssClass('hidden');
expect(dates[0]).not.toHaveCssClass('collapsed');
expect(dates[1]).toHaveCssClass('hidden');
expect(dates[1]).not.toHaveCssClass('collapsed');
});

});

@Component({selector: 'test-cmp', template: ''})
Expand All @@ -173,11 +214,15 @@ class TestComponent {
year: 2016,
number: 7,
weekdays: [1],
weeks: [{number: 2, days: [{date: new NgbDate(2016, 7, 22), disabled: false}]}]
weeks: [{
number: 2,
days: [{date: new NgbDate(2016, 7, 22), disabled: false}, {date: new NgbDate(2016, 8, 23), disabled: false}]
}]
};

showWeekdays = true;
showWeekNumbers = true;
outsideDays = 'visible';

onClick = () => {};
}
34 changes: 23 additions & 11 deletions src/datepicker/datepicker-month-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ import {DayTemplateContext} from './datepicker-day-template-context';
height: 100%;
cursor: pointer;
}
.day.disabled {
.day.disabled, .day.hidden, .day.collapsed {
cursor: default;
}
:host/deep/.day.collapsed > * {
display: none;
}
:host/deep/.day.hidden > * {
visibility: hidden;
}
`],
template: `
<tr *ngIf="showWeekdays">
Expand All @@ -28,21 +34,23 @@ import {DayTemplateContext} from './datepicker-day-template-context';
</tr>
<tr *ngFor="let week of month.weeks">
<td *ngIf="showWeekNumbers" class="weeknumber small text-xs-center">{{ week.number }}</td>
<td *ngFor="let day of week.days" (click)="doSelect(day)" class="day" [class.disabled]="isDisabled(day)">
<template [ngTemplateOutlet]="dayTemplate"
[ngOutletContext]="{date: {year: day.date.year, month: day.date.month, day: day.date.day},
currentMonth: month.number,
disabled: isDisabled(day),
selected: isSelected(day.date)}">
</template>
</td>
<td *ngFor="let day of week.days" (click)="doSelect(day)" class="day" [class.disabled]="isDisabled(day)"
[class.collapsed]="isCollapsed(day)" [class.hidden]="isHidden(day)">
<template [ngTemplateOutlet]="dayTemplate"
[ngOutletContext]="{date: {year: day.date.year, month: day.date.month, day: day.date.day},
currentMonth: month.number,
disabled: isDisabled(day),
selected: isSelected(day.date)}">
</template>
</td>
</tr>
`
})
export class NgbDatepickerMonthView {
@Input() dayTemplate: TemplateRef<DayTemplateContext>;
@Input() disabled: boolean;
@Input() month: MonthViewModel;
@Input() outsideDays: 'visible' | 'hidden' | 'collapsed';
@Input() selectedDate: NgbDate;
@Input() showWeekdays;
@Input() showWeekNumbers;
Expand All @@ -52,12 +60,16 @@ export class NgbDatepickerMonthView {
constructor(public i18n: NgbDatepickerI18n) {}

doSelect(day: DayViewModel) {
if (!this.isDisabled(day)) {
if (!this.isDisabled(day) && !this.isCollapsed(day) && !this.isHidden(day)) {
this.select.emit(NgbDate.from(day.date));
}
}

isDisabled(day) { return this.disabled || day.disabled; }
isDisabled(day: DayViewModel) { return this.disabled || day.disabled; }

isSelected(date: NgbDate) { return this.selectedDate && this.selectedDate.equals(date); }

isCollapsed(day: DayViewModel) { return this.outsideDays === 'collapsed' && this.month.number !== day.date.month; }

isHidden(day: DayViewModel) { return this.outsideDays === 'hidden' && this.month.number !== day.date.month; }
}
2 changes: 2 additions & 0 deletions src/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function expectSameValues(datepicker: NgbDatepicker, config: NgbDatepickerConfig
expect(datepicker.markDisabled).toBe(config.markDisabled);
expect(datepicker.minDate).toBe(config.minDate);
expect(datepicker.maxDate).toBe(config.maxDate);
expect(datepicker.outsideDays).toBe(config.outsideDays);
expect(datepicker.showNavigation).toBe(config.showNavigation);
expect(datepicker.showWeekdays).toBe(config.showWeekdays);
expect(datepicker.showWeekNumbers).toBe(config.showWeekNumbers);
Expand All @@ -45,6 +46,7 @@ function customizeConfig(config: NgbDatepickerConfig) {
config.markDisabled = (date, current) => false;
config.minDate = {year: 2000, month: 1, day: 1};
config.maxDate = {year: 2030, month: 12, day: 31};
config.outsideDays = 'collapsed';
config.showNavigation = false;
config.showWeekdays = false;
config.showWeekNumbers = true;
Expand Down
8 changes: 8 additions & 0 deletions src/datepicker/datepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const NGB_DATEPICKER_VALUE_ACCESSOR = {
[showWeekdays]="showWeekdays"
[showWeekNumbers]="showWeekNumbers"
[disabled]="disabled"
[outsideDays]="outsideDays"
(select)="onDateSelect($event)">
</tbody>
</table>
Expand Down Expand Up @@ -85,6 +86,12 @@ export class NgbDatepicker implements OnChanges,
*/
@Input() maxDate: NgbDateStruct;

/**
* The way to display days that don't belong to current month: `visible` (default),
* `hidden` (not displayed) or `collapsed` (not displayed with empty space collapsed)
*/
@Input() outsideDays: 'visible' | 'collapsed' | 'hidden';

/**
* Whether to display navigation
*/
Expand Down Expand Up @@ -119,6 +126,7 @@ export class NgbDatepicker implements OnChanges,
this.markDisabled = config.markDisabled;
this.minDate = config.minDate;
this.maxDate = config.maxDate;
this.outsideDays = config.outsideDays;
this.showNavigation = config.showNavigation;
this.showWeekdays = config.showWeekdays;
this.showWeekNumbers = config.showWeekNumbers;
Expand Down

0 comments on commit a92c0e7

Please sign in to comment.