Skip to content

Commit

Permalink
feat(datepicker): add support for datepickers in popups
Browse files Browse the repository at this point in the history
Closes #715
  • Loading branch information
pkozlowski-opensource committed Sep 14, 2016
1 parent e793944 commit 390935b
Show file tree
Hide file tree
Showing 10 changed files with 578 additions and 4 deletions.
3 changes: 3 additions & 0 deletions demo/src/app/components/datepicker/datepicker.component.ts
Expand Up @@ -12,6 +12,9 @@ import {DEMO_SNIPPETS} from './demos';
<ngbd-example-box demoTitle="Basic datepicker" [htmlSnippet]="snippets.basic.markup" [tsSnippet]="snippets.basic.code">
<ngbd-datepicker-basic></ngbd-datepicker-basic>
</ngbd-example-box>
<ngbd-example-box demoTitle="Datepicker in a popup" [htmlSnippet]="snippets.popup.markup" [tsSnippet]="snippets.popup.code">
<ngbd-datepicker-popup></ngbd-datepicker-popup>
</ngbd-example-box>
<ngbd-example-box demoTitle="Disabled datepicker" [htmlSnippet]="snippets.disabled.markup" [tsSnippet]="snippets.disabled.code">
<ngbd-datepicker-disabled></ngbd-datepicker-disabled>
</ngbd-example-box>
Expand Down
7 changes: 6 additions & 1 deletion demo/src/app/components/datepicker/demos/index.ts
Expand Up @@ -2,14 +2,19 @@ import {NgbdDatepickerBasic} from './basic/datepicker-basic';
import {NgbdDatepickerConfig} from './config/datepicker-config';
import {NgbdDatepickerI18n} from './i18n/datepicker-i18n';
import {NgbdDatepickerDisabled} from './disabled/datepicker-disabled';
import {NgbdDatepickerPopup} from './popup/datepicker-popup';

export const DEMO_DIRECTIVES = [NgbdDatepickerBasic, NgbdDatepickerDisabled, NgbdDatepickerI18n, NgbdDatepickerConfig];
export const DEMO_DIRECTIVES = [NgbdDatepickerBasic, NgbdDatepickerPopup, NgbdDatepickerDisabled, NgbdDatepickerI18n, NgbdDatepickerConfig];

export const DEMO_SNIPPETS = {
basic: {
code: require('!!prismjs?lang=typescript!./basic/datepicker-basic'),
markup: require('!!prismjs?lang=markup!./basic/datepicker-basic.html')
},
popup: {
code: require('!!prismjs?lang=typescript!./popup/datepicker-popup'),
markup: require('!!prismjs?lang=markup!./popup/datepicker-popup.html')
},
disabled: {
code: require('!!prismjs?lang=typescript!./disabled/datepicker-disabled'),
markup: require('!!prismjs?lang=markup!./disabled/datepicker-disabled.html')
Expand Down
@@ -0,0 +1,14 @@
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input class="form-control" placeholder="yyyy-mm-dd"
name="dp" [(ngModel)]="model" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-addon" (click)="d.toggle()" >
<img src="img/calendar-icon.svg" style="width: 1.2rem; cursor: pointer;"/>
</div>
</div>
</div>
</form>

<hr/>
<pre>Model: {{ model | json }}</pre>
@@ -0,0 +1,9 @@
import {Component} from '@angular/core';

@Component({
selector: 'ngbd-datepicker-popup',
templateUrl: './datepicker-popup.html'
})
export class NgbdDatepickerPopup {
model;
}
16 changes: 16 additions & 0 deletions demo/src/public/img/calendar-icon.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
228 changes: 228 additions & 0 deletions src/datepicker/datepicker-input.spec.ts
@@ -0,0 +1,228 @@
import {TestBed, ComponentFixture, fakeAsync, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {createGenericTestComponent} from '../test/common';

import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

import {NgbDatepickerModule} from './datepicker.module';
import {NgbInputDatepicker} from './datepicker-input';
import {NgbDatepicker} from './datepicker';

const createTestCmpt = (html: string) =>
createGenericTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;

describe('NgbInputDatepicker', () => {

beforeEach(() => {
TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbDatepickerModule, FormsModule]});
});

describe('open, close and toggle', () => {

it('should allow controlling datepicker popup from outside', () => {
const fixture = createTestCmpt(`
<input ngbDatepicker #d="ngbDatepicker">
<button (click)="open(d)">Open</button>
<button (click)="close(d)">Close</button>
<button (click)="toggle(d)">Toggle</button>`);

const buttons = fixture.nativeElement.querySelectorAll('button');

buttons[0].click(); // open
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

buttons[1].click(); // close
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();

buttons[2].click(); // toggle
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();

buttons[2].click(); // toggle
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('ngb-datepicker')).toBeNull();
});
});

describe('ngModel interactions', () => {

it('should format bound date as ISO (by default) in the input field', fakeAsync(() => {
const fixture = createTestCmpt(`<input ngbDatepicker [ngModel]="date">`);
const input = fixture.nativeElement.querySelector('input');

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

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

it('should parse user-entered date as ISO (by default)', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [(ngModel)]="date">`);
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});
});

it('should propagate null to model when a user enters invalid date', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [(ngModel)]="date">`);
const inputDebugEl = fixture.debugElement.query(By.css('input'));

inputDebugEl.triggerEventHandler('change', {target: {value: 'aaa'}});
expect(fixture.componentInstance.date).toBeNull();
});

it('should propagate disabled state', fakeAsync(() => {
const fixture = createTestCmpt(`
<input ngbDatepicker [(ngModel)]="date" #d="ngbDatepicker" [disabled]="isDisabled">
<button (click)="open(d)">Open</button>`);
fixture.componentInstance.isDisabled = true;
fixture.detectChanges();

const button = fixture.nativeElement.querySelector('button');
const input = fixture.nativeElement.querySelector('input');

button.click(); // open
tick();
fixture.detectChanges();
const buttonInDatePicker = fixture.nativeElement.querySelector('ngb-datepicker button');

expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();
expect(input.disabled).toBeTruthy();
expect(buttonInDatePicker.disabled).toBeTruthy();

fixture.componentInstance.isDisabled = false;
fixture.detectChanges();
tick();
fixture.detectChanges();

expect(fixture.nativeElement.querySelector('ngb-datepicker')).not.toBeNull();
expect(input.disabled).toBeFalsy();
expect(buttonInDatePicker.disabled).toBeFalsy();
}));
});

describe('options', () => {

it('should propagate the "dayTemplate" option', () => {
const fixture = createTestCmpt(`<template #t></template><input ngbDatepicker [dayTemplate]="t">`);
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.dayTemplate).toBeDefined();
});

it('should propagate the "firstDayOfWeek" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [firstDayOfWeek]="5">`);
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.firstDayOfWeek).toBe(5);
});

it('should propagate the "markDisabled" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [markDisabled]="noop">`);
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.markDisabled).toBeDefined();
});

it('should propagate the "minDate" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [minDate]="{year: 2016, month: 9, day: 13}">`);
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.minDate).toEqual({year: 2016, month: 9, day: 13});
});

it('should propagate the "maxDate" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [maxDate]="{year: 2016, month: 9, day: 13}">`);
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.maxDate).toEqual({year: 2016, month: 9, day: 13});
});

it('should propagate the "showNavigation" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [showNavigation]="true">`);
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.showNavigation).toBeTruthy();
});

it('should propagate the "showWeekdays" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [showWeekdays]="true">`);
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.showWeekdays).toBeTruthy();
});

it('should propagate the "showWeekNumbers" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [showWeekNumbers]="true">`);
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.showWeekNumbers).toBeTruthy();
});

it('should propagate the "startDate" option', () => {
const fixture = createTestCmpt(`<input ngbDatepicker [startDate]="{year: 2016, month: 9, day: 13}">`);
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.startDate).toEqual({year: 2016, month: 9, day: 13});
});
});
});

@Component({selector: 'test-cmp', template: ''})
class TestComponent {
date;
isDisabled;

open(d: NgbInputDatepicker) { d.open(); }

close(d: NgbInputDatepicker) { d.close(); }

toggle(d: NgbInputDatepicker) { d.toggle(); }

noop() {}
}

0 comments on commit 390935b

Please sign in to comment.