Skip to content

Commit

Permalink
feat(datepicker): export NgbDatepickerMonthView
Browse files Browse the repository at this point in the history
  • Loading branch information
gpolychronis-amadeus authored and maxokorokov committed Feb 14, 2020
1 parent ddc4ceb commit 3b82948
Show file tree
Hide file tree
Showing 23 changed files with 710 additions and 481 deletions.
10 changes: 10 additions & 0 deletions demo/src/app/components/datepicker/datepicker.module.ts
Expand Up @@ -23,6 +23,8 @@ import { NgbdDatepickerFooterTemplateModule } from './demos/footertemplate/datep
import { NgbdDatepickerFootertemplate } from './demos/footertemplate/datepicker-footertemplate';
import { NgbdDatepickerI18n } from './demos/i18n/datepicker-i18n';
import { NgbdDatepickerI18nModule } from './demos/i18n/datepicker-i18n.module';
import { NgbdDatepickerCustommonth } from './demos/custommonth/datepicker-custommonth';
import { NgbdDatepickerCustommonthModule } from './demos/custommonth/datepicker-custommonth.module';
import { NgbdDatepickerMultiple } from './demos/multiple/datepicker-multiple';
import { NgbdDatepickerMultipleModule } from './demos/multiple/datepicker-multiple.module';
import { NgbdDatepickerPopup } from './demos/popup/datepicker-popup';
Expand All @@ -46,6 +48,7 @@ const OVERVIEW = {
'limiting-dates': 'Disabling and limiting dates',
'day-template': 'Day display customization',
today: 'Today\'s date',
'content-template': 'Content Template',
'footer-template': 'Custom footer',
range: 'Range selection',
i18n: 'Internationalization',
Expand Down Expand Up @@ -107,6 +110,12 @@ const DEMOS = {
code: require('!!raw-loader!./demos/customday/datepicker-customday').default,
markup: require('!!raw-loader!./demos/customday/datepicker-customday.html').default
},
custommonth: {
title: 'Custom month layout',
type: NgbdDatepickerCustommonth,
code: require('!!raw-loader!./demos/custommonth/datepicker-custommonth').default,
markup: require('!!raw-loader!./demos/custommonth/datepicker-custommonth.html').default
},
footertemplate: {
title: 'Footer template',
type: NgbdDatepickerFootertemplate,
Expand Down Expand Up @@ -165,6 +174,7 @@ export const ROUTES = [
NgbdDatepickerRangePopupModule,
NgbdDatepickerAdapterModule,
NgbdDatepickerKeyboardModule,
NgbdDatepickerCustommonthModule,
...DEMO_CALENDAR_MODULES
],
declarations: [
Expand Down
@@ -0,0 +1,14 @@
<p>This datepicker uses a custom month layout.</p>

<ngb-datepicker #dp
[startDate]="{month: 8, year: 2016}"
[displayMonths]="2"
[outsideDays]="'collapsed'"
[navigation]="'none'">
<ng-template ngbDatepickerMonths>
<div class="custom-month-view" *ngFor="let monthStruct of dp.state.months">
<span>{{i18n.getMonthFullName(monthStruct.month)}} {{monthStruct.year}}</span>
<ngb-datepicker-month [month]="monthStruct"></ngb-datepicker-month>
</div>
</ng-template>
</ngb-datepicker>
@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

import { NgbdDatepickerCustommonth } from './datepicker-custommonth';

@NgModule({
imports: [BrowserModule, NgbModule],
declarations: [NgbdDatepickerCustommonth],
exports: [NgbdDatepickerCustommonth],
bootstrap: [NgbdDatepickerCustommonth]
})
export class NgbdDatepickerCustommonthModule {}
@@ -0,0 +1,27 @@
import {Component} from '@angular/core';
import {NgbDatepickerI18n} from '@ng-bootstrap/ng-bootstrap';

@Component({
selector: 'ngbd-datepicker-custommonth',
templateUrl: './datepicker-custommonth.html',
styles: [`
ngb-datepicker {
display: flex;
border: none;
}
.custom-month-view {
margin: 1rem;
display: flex;
flex-direction: column;
align-items: center;
border: 1px solid gray;
border-radius: 1rem 1rem 0 0;
}
.custom-month-view span{
font-weight: bold;
}
`]
})
export class NgbdDatepickerCustommonth {
constructor(public i18n: NgbDatepickerI18n) {}
}
@@ -1,5 +1,5 @@
import {Component, Injectable} from '@angular/core';
import {NgbCalendar, NgbDatepicker, NgbDatepickerKeyboardService, NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';
import {NgbDatepicker, NgbDatepickerKeyboardService, NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';

const Key = {
PageUp: 'PageUp',
Expand All @@ -10,14 +10,14 @@ const Key = {

@Injectable()
export class CustomKeyboardService extends NgbDatepickerKeyboardService {
processKey(event: KeyboardEvent, dp: NgbDatepicker, calendar: NgbCalendar) {
processKey(event: KeyboardEvent, dp: NgbDatepicker) {
const state = dp.state;
switch (event.code) {
case Key.PageUp:
dp.focusDate(calendar.getPrev(state.focusedDate, event.altKey ? 'y' : 'm'));
dp.focusDate(dp.calendar.getPrev(state.focusedDate, event.altKey ? 'y' : 'm'));
break;
case Key.PageDown:
dp.focusDate(calendar.getNext(state.focusedDate, event.altKey ? 'y' : 'm'));
dp.focusDate(dp.calendar.getNext(state.focusedDate, event.altKey ? 'y' : 'm'));
break;
case Key.End:
dp.focusDate(event.altKey ? state.maxDate : state.lastDate);
Expand All @@ -26,7 +26,7 @@ export class CustomKeyboardService extends NgbDatepickerKeyboardService {
dp.focusDate(event.altKey ? state.minDate : state.firstDate);
break;
default:
super.processKey(event, dp, calendar);
super.processKey(event, dp);
return;
}
event.preventDefault();
Expand Down
Expand Up @@ -279,6 +279,19 @@ <h4>Input date parsing and formatting</h4>
</ngbd-overview-section>


<!-- MONTH LAYOUT CUSTOMIZATION-->
<ngbd-overview-section [section]="sections['content-template']">
<p>
You can replace the content of the datepicker.
Combined with the NgbDatepickerMonthView you can customize the layout of months as in the
<a routerLink="../examples" fragment="custommonth">custom month layout example</a>.
</p>

<ngbd-code [snippet]="snippets.contentTemplate"></ngbd-code>

</ngbd-overview-section>


<!-- FOOTER TEMPLATE -->
<ngbd-overview-section [section]="sections['footer-template']">
<p>
Expand Down
Expand Up @@ -119,6 +119,21 @@ export class NgbdDatepickerOverviewComponent {
<ngbDatepicker [dayTemplate]=“t”/>
`,
}),
contentTemplate: Snippet({
lang: 'html',
code: `
<ngb-datepicker #datepicker
[startDate]="{month: 8, year: 2016}"
[displayMonths]="2">
<ng-template ngbDatepickerContent>
<div *ngFor="let monthStruct of datepicker.state.months">
<span>{{i18n.getMonthFullName(monthStruct.month)}} {{monthStruct.year}}</span>
<ngb-datepicker-month-view [month]="monthStruct"></ngb-datepicker-month-view>
</div>
</ng-template>
</ngb-datepicker>
`,
}),
todayHTML: Snippet({
lang: 'html',
code: `
Expand Down
8 changes: 4 additions & 4 deletions src/datepicker/datepicker-input.spec.ts
Expand Up @@ -289,7 +289,7 @@ describe('NgbInputDatepicker', () => {
expect(input.disabled).toBeTruthy();
expect(buttonInDatePicker.disabled).toBeTruthy();

const dayElements = fixture.nativeElement.querySelectorAll('ngb-datepicker-month-view .ngb-dp-day');
const dayElements = fixture.nativeElement.querySelectorAll('ngb-datepicker-month .ngb-dp-day');
expect(dayElements[1]).toHaveCssClass('disabled');
expect(dayElements[11]).toHaveCssClass('disabled');
expect(dayElements[21]).toHaveCssClass('disabled');
Expand All @@ -303,7 +303,7 @@ describe('NgbInputDatepicker', () => {
expect(input.disabled).toBeFalsy();
expect(buttonInDatePicker.disabled).toBeFalsy();

const dayElements2 = fixture.nativeElement.querySelectorAll('ngb-datepicker-month-view .ngb-dp-day');
const dayElements2 = fixture.nativeElement.querySelectorAll('ngb-datepicker-month .ngb-dp-day');
expect(dayElements2[1]).not.toHaveCssClass('disabled');
expect(dayElements2[11]).not.toHaveCssClass('disabled');
expect(dayElements2[21]).not.toHaveCssClass('disabled');
Expand All @@ -329,7 +329,7 @@ describe('NgbInputDatepicker', () => {
expect(input.disabled).toBeTruthy();
expect(buttonInDatePicker.disabled).toBeTruthy();

const dayElements = fixture.nativeElement.querySelectorAll('ngb-datepicker-month-view .ngb-dp-day');
const dayElements = fixture.nativeElement.querySelectorAll('ngb-datepicker-month .ngb-dp-day');
expect(dayElements[1]).toHaveCssClass('disabled');
expect(dayElements[11]).toHaveCssClass('disabled');
expect(dayElements[21]).toHaveCssClass('disabled');
Expand All @@ -341,7 +341,7 @@ describe('NgbInputDatepicker', () => {
expect(input.disabled).toBeFalsy();
expect(buttonInDatePicker.disabled).toBeFalsy();

const dayElements2 = fixture.nativeElement.querySelectorAll('ngb-datepicker-month-view .ngb-dp-day');
const dayElements2 = fixture.nativeElement.querySelectorAll('ngb-datepicker-month .ngb-dp-day');
expect(dayElements2[1]).not.toHaveCssClass('disabled');
expect(dayElements2[11]).not.toHaveCssClass('disabled');
expect(dayElements2[21]).not.toHaveCssClass('disabled');
Expand Down
63 changes: 48 additions & 15 deletions src/datepicker/datepicker-integration.spec.ts
Expand Up @@ -8,6 +8,7 @@ import {getMonthSelect, getYearSelect} from '../test/datepicker/common';
import {NgbDatepickerI18n, NgbDatepickerI18nDefault} from './datepicker-i18n';
import {NgbDatepicker} from './datepicker';
import {NgbDatepickerKeyboardService} from './datepicker-keyboard-service';
import {NgbDatepickerMonth} from './datepicker-month';
import {Key} from '../util/key';

describe('ngb-datepicker integration', () => {
Expand Down Expand Up @@ -116,18 +117,18 @@ describe('ngb-datepicker integration', () => {

@Injectable()
class CustomKeyboardService extends NgbDatepickerKeyboardService {
processKey(event: KeyboardEvent, service: NgbDatepicker, calendar: NgbCalendar) {
processKey(event: KeyboardEvent, service: NgbDatepicker) {
const state = service.state;
// tslint:disable-next-line:deprecation
switch (event.which) {
case Key.PageUp:
service.focusDate(calendar.getPrev(state.focusedDate, event.altKey ? 'y' : 'm', 1));
service.focusDate(service.calendar.getPrev(state.focusedDate, event.altKey ? 'y' : 'm', 1));
break;
case Key.PageDown:
service.focusDate(calendar.getNext(state.focusedDate, event.altKey ? 'y' : 'm', 1));
service.focusDate(service.calendar.getNext(state.focusedDate, event.altKey ? 'y' : 'm', 1));
break;
default:
super.processKey(event, service, calendar);
super.processKey(event, service);
return;
}
event.preventDefault();
Expand All @@ -136,8 +137,8 @@ describe('ngb-datepicker integration', () => {
}

let fixture: ComponentFixture<TestComponent>;
let dp: NgbDatepicker;
let ngbCalendar: NgbCalendar;
let calendar: NgbCalendar;
let mv: NgbDatepickerMonth;
let startDate: NgbDateStruct = new NgbDate(2018, 1, 1);

beforeEach(() => {
Expand All @@ -151,22 +152,54 @@ describe('ngb-datepicker integration', () => {

fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
calendar = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker).calendar;
mv = fixture.debugElement.query(By.css('ngb-datepicker-month')).injector.get(NgbDatepickerMonth);

dp = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbDatepicker);
ngbCalendar = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbCalendar as Type<NgbCalendar>);

spyOn(ngbCalendar, 'getPrev');
spyOn(calendar, 'getPrev');
});

it('should allow customize keyboard navigation', () => {
dp.onKeyDown(<any>{which: Key.PageUp, altKey: true, preventDefault: () => {}, stopPropagation: () => {}});
expect(ngbCalendar.getPrev).toHaveBeenCalledWith(startDate, 'y', 1);
dp.onKeyDown(<any>{which: Key.PageUp, shiftKey: true, preventDefault: () => {}, stopPropagation: () => {}});
expect(ngbCalendar.getPrev).toHaveBeenCalledWith(startDate, 'm', 1);
mv.onKeyDown(<any>{which: Key.PageUp, altKey: true, preventDefault: () => {}, stopPropagation: () => {}});
expect(calendar.getPrev).toHaveBeenCalledWith(startDate, 'y', 1);
mv.onKeyDown(<any>{which: Key.PageUp, shiftKey: true, preventDefault: () => {}, stopPropagation: () => {}});
expect(calendar.getPrev).toHaveBeenCalledWith(startDate, 'm', 1);
});

it('should allow access to default keyboard navigation', () => {
dp.onKeyDown(<any>{which: Key.ArrowUp, altKey: true, preventDefault: () => {}, stopPropagation: () => {}});
mv.onKeyDown(<any>{which: Key.ArrowUp, altKey: true, preventDefault: () => {}, stopPropagation: () => {}});
expect(calendar.getPrev).toHaveBeenCalledWith(startDate, 'd', 7);
});

});

describe('ngb-datepicker-month', () => {
let fixture: ComponentFixture<TestComponent>;
let mv: NgbDatepickerMonth;
let startDate: NgbDateStruct = new NgbDate(2018, 1, 1);
let ngbCalendar: NgbCalendar;

beforeEach(() => {
TestBed.overrideComponent(TestComponent, {
set: {
template: `
<ngb-datepicker [startDate]="{year: 2018, month: 1}" [displayMonths]="1">
<ng-template ngbDatepickerMonths>
<ngb-datepicker-month [month]="{year: 2018, month: 1}"></ngb-datepicker-month>
</ng-template>
</ngb-datepicker>`
}
});

fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
mv = fixture.debugElement.query(By.css('ngb-datepicker-month')).injector.get(NgbDatepickerMonth);
ngbCalendar = fixture.debugElement.query(By.css('ngb-datepicker')).injector.get(NgbCalendar as Type<NgbCalendar>);

spyOn(ngbCalendar, 'getPrev');
});

it('should preserve the functionality of keyboard service', () => {
mv.onKeyDown(<any>{which: Key.ArrowUp, altKey: true, preventDefault: () => {}, stopPropagation: () => {}});
expect(ngbCalendar.getPrev).toHaveBeenCalledWith(startDate, 'd', 7);
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/datepicker/datepicker-keyboard-service.spec.ts
Expand Up @@ -14,7 +14,7 @@ describe('ngb-datepicker-keyboard-service', () => {
let service: NgbDatepickerKeyboardService;
let calendar: NgbCalendar;
let mock: Partial<NgbDatepicker>;
let processKey = function(e: KeyboardEvent) { service.processKey(e, mock as NgbDatepicker, calendar); };
let processKey = function(e: KeyboardEvent) { service.processKey(e, mock as NgbDatepicker); };
let state: NgbDatepickerState = Object.assign({focusedDate: {day: 1, month: 1, year: 2018}});

beforeEach(() => {
Expand All @@ -23,7 +23,7 @@ describe('ngb-datepicker-keyboard-service', () => {

calendar = TestBed.get(NgbCalendar as Type<NgbCalendar>);
service = TestBed.get(NgbDatepickerKeyboardService);
mock = {state, focusDate: () => {}, focusSelect: () => {}};
mock = {state, focusDate: () => {}, focusSelect: () => {}, calendar};

spyOn(mock, 'focusDate');
spyOn(mock, 'focusSelect');
Expand Down
5 changes: 2 additions & 3 deletions src/datepicker/datepicker-keyboard-service.ts
@@ -1,5 +1,4 @@
import {Injectable} from '@angular/core';
import {NgbCalendar} from './ngb-calendar';
import {NgbDatepicker} from './datepicker';
import {Key} from '../util/key';

Expand All @@ -15,8 +14,8 @@ export class NgbDatepickerKeyboardService {
/**
* Processes a keyboard event.
*/
processKey(event: KeyboardEvent, datepicker: NgbDatepicker, calendar: NgbCalendar) {
const state = datepicker.state;
processKey(event: KeyboardEvent, datepicker: NgbDatepicker) {
const {state, calendar} = datepicker;
// tslint:disable-next-line:deprecation
switch (event.which) {
case Key.PageUp:
Expand Down

0 comments on commit 3b82948

Please sign in to comment.