Skip to content

Commit

Permalink
feat(datepicker): input datepicker global config
Browse files Browse the repository at this point in the history
Closes #3273
  • Loading branch information
IAfanasov authored and maxokorokov committed Nov 15, 2019
1 parent b35f759 commit a9ce83e
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 11 deletions.
@@ -1,3 +1,13 @@
<p>This datepicker uses customized default values.</p>
<p>This datepicker input uses customized default values.</p>

<ngb-datepicker [(ngModel)]="model"></ngb-datepicker>
<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-append">
<button class="btn btn-outline-secondary calendar" (click)="d.toggle()" type="button"></button>
</div>
</div>
</div>
</form>
@@ -1,5 +1,11 @@
import {Component} from '@angular/core';
import {NgbDatepickerConfig, NgbCalendar, NgbDate, NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';
import {
NgbDatepickerConfig,
NgbCalendar,
NgbDate,
NgbDateStruct,
NgbInputDatepickerConfig
} from '@ng-bootstrap/ng-bootstrap';

@Component({
selector: 'ngbd-datepicker-config',
Expand All @@ -10,15 +16,22 @@ export class NgbdDatepickerConfig {

model: NgbDateStruct;

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

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

// weekends are disabled
config.markDisabled = (date: NgbDate) => calendar.getWeekday(date) >= 6;
datepickerConfig.markDisabled = (date: NgbDate) => calendar.getWeekday(date) >= 6;

// setting datepicker popup to close only on click outside
inputDatepickerConfig.autoClose = 'outside';

// setting datepicker popup to open above the input
inputDatepickerConfig.placement = ['top-left', 'top-right'];
}
}
12 changes: 12 additions & 0 deletions src/datepicker/datepicker-input-config.spec.ts
@@ -0,0 +1,12 @@
import {NgbInputDatepickerConfig} from './datepicker-input-config';

describe('NgbInputDatepickerConfig', () => {
it('should have sensible default values', () => {
const config = new NgbInputDatepickerConfig();

expect(config.autoClose).toBe(true);
expect(config.container).toBeUndefined();
expect(config.positionTarget).toBeUndefined();
expect(config.placement).toEqual(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
});
});
17 changes: 17 additions & 0 deletions src/datepicker/datepicker-input-config.ts
@@ -0,0 +1,17 @@
import {Injectable} from '@angular/core';

import {PlacementArray} from '../util/positioning';

/**
* A configuration service for the [`NgbDatepickerInput`](#/components/datepicker/api#NgbDatepicker) component.
*
* You can inject this service, typically in your root component, and customize the values of its properties in
* order to provide default values for all the datepicker inputs used in the application.
*/
@Injectable({providedIn: 'root'})
export class NgbInputDatepickerConfig {
autoClose: boolean | 'inside' | 'outside' = true;
container: null | 'body';
positionTarget: string | HTMLElement;
placement: PlacementArray = ['bottom-left', 'bottom-right', 'top-left', 'top-right'];
}
53 changes: 53 additions & 0 deletions src/datepicker/datepicker-input.spec.ts
Expand Up @@ -11,19 +11,72 @@ import {NgbDatepicker} from './datepicker';
import {NgbDateStruct} from './ngb-date-struct';
import {NgbDate} from './ngb-date';
import * as positioning from 'src/util/positioning';
import {NgbInputDatepickerConfig} from './datepicker-input-config';

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

const createTestNativeCmpt = (html: string) =>
createGenericTestComponent(html, TestNativeComponent) as ComponentFixture<TestNativeComponent>;

function expectSameValues(inputDatepicker: NgbInputDatepicker, config: NgbInputDatepickerConfig) {
['autoClose', 'container', 'positionTarget', 'placement'].forEach(
field => expect(inputDatepicker[field]).toEqual(config[field], field));
}

function customizeConfig(config: NgbInputDatepickerConfig) {
config.autoClose = 'outside';
config.container = 'body';
config.positionTarget = 'positionTarget';
config.placement = ['bottom-left', 'top-right'];
}

describe('NgbInputDatepicker', () => {

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

it('should initialize inputs with provided datepicker config', () => {
const defaultConfig = new NgbInputDatepickerConfig();
const fixture = createTestCmpt(`<input ngbDatepicker>`);

const inputDatepicker =
fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);
expectSameValues(inputDatepicker, defaultConfig);
});

it('should initialize inputs with provided config', () => {
// overrideComponent should happen before any injections, so createTestCmpt will fail here
TestBed.overrideComponent(TestComponent, {set: {template: '<input ngbDatepicker>'}});
const config = TestBed.get(NgbInputDatepickerConfig);
customizeConfig(config);
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();

const inputDatepicker =
fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);
expectSameValues(inputDatepicker, config);
});

describe('Custom config as provider', () => {
const config = new NgbInputDatepickerConfig();
customizeConfig(config);

beforeEach(() => {
TestBed.configureTestingModule(
{imports: [NgbDatepickerModule], providers: [{provide: NgbInputDatepickerConfig, useValue: config}]});
});

it('should initialize inputs with provided config as provider', () => {
const fixture = createTestCmpt(`<input ngbDatepicker>`);

const inputDatepicker =
fixture.debugElement.query(By.directive(NgbInputDatepicker)).injector.get(NgbInputDatepicker);
expectSameValues(inputDatepicker, config);
});
});

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

it('should allow controlling datepicker popup from outside', () => {
Expand Down
8 changes: 5 additions & 3 deletions src/datepicker/datepicker-input.ts
Expand Up @@ -32,6 +32,7 @@ import {NgbCalendar} from './ngb-calendar';
import {NgbDate} from './ngb-date';
import {NgbDateParserFormatter} from './ngb-date-parser-formatter';
import {NgbDateStruct} from './ngb-date-struct';
import {NgbInputDatepickerConfig} from './datepicker-input-config';

const NGB_DATEPICKER_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -81,7 +82,7 @@ export class NgbInputDatepicker implements OnChanges,
*
* @since 3.0.0
*/
@Input() autoClose: boolean | 'inside' | 'outside' = true;
@Input() autoClose: boolean | 'inside' | 'outside';

/**
* The reference to a custom template for the day.
Expand Down Expand Up @@ -177,7 +178,7 @@ export class NgbInputDatepicker implements OnChanges,
*
* Please see the [positioning overview](#/positioning) for more details.
*/
@Input() placement: PlacementArray = ['bottom-left', 'bottom-right', 'top-left', 'top-right'];
@Input() placement: PlacementArray;

/**
* If `true`, weekdays will be displayed.
Expand Down Expand Up @@ -260,7 +261,8 @@ export class NgbInputDatepicker implements OnChanges,
private _vcRef: ViewContainerRef, private _renderer: Renderer2, private _cfr: ComponentFactoryResolver,
private _ngZone: NgZone, private _service: NgbDatepickerService, private _calendar: NgbCalendar,
private _dateAdapter: NgbDateAdapter<any>, @Inject(DOCUMENT) private _document: any,
private _changeDetector: ChangeDetectorRef) {
private _changeDetector: ChangeDetectorRef, config: NgbInputDatepickerConfig) {
['autoClose', 'container', 'positionTarget', 'placement'].forEach(input => this[input] = config[input]);
this._zoneSubscription = _ngZone.onStable.subscribe(() => this._updatePopupPosition());
}

Expand Down
1 change: 1 addition & 0 deletions src/datepicker/datepicker.module.ts
Expand Up @@ -21,6 +21,7 @@ export {NgbDatepickerDayView} from './datepicker-day-view';
export {NgbDatepickerNavigation} from './datepicker-navigation';
export {NgbDatepickerNavigationSelect} from './datepicker-navigation-select';
export {NgbDatepickerConfig} from './datepicker-config';
export {NgbInputDatepickerConfig} from './datepicker-input-config';
export {NgbDatepickerI18n} from './datepicker-i18n';
export {NgbDateStruct} from './ngb-date-struct';
export {NgbDate} from './ngb-date';
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -58,6 +58,7 @@ export {
NgbDateParserFormatter,
NgbDatepicker,
NgbDatepickerConfig,
NgbInputDatepickerConfig,
NgbDatepickerI18n,
NgbDatepickerI18nHebrew,
NgbDatepickerKeyboardService,
Expand Down

0 comments on commit a9ce83e

Please sign in to comment.