From 31344394e9bf8b1ccefe770dc1b84c759b411cac Mon Sep 17 00:00:00 2001 From: IAfanasov Date: Sun, 7 Jul 2019 10:06:43 +0200 Subject: [PATCH 1/2] feat(datepicker): input datepicker global config Closes #3273 --- .../demos/config/datepicker-config.html | 14 ++++- .../demos/config/datepicker-config.ts | 25 ++++++--- .../datepicker-input-config.spec.ts | 12 +++++ src/datepicker/datepicker-input-config.ts | 17 ++++++ src/datepicker/datepicker-input.spec.ts | 53 +++++++++++++++++++ src/datepicker/datepicker-input.ts | 8 +-- src/datepicker/datepicker.module.ts | 1 + src/index.ts | 1 + 8 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 src/datepicker/datepicker-input-config.spec.ts create mode 100644 src/datepicker/datepicker-input-config.ts diff --git a/demo/src/app/components/datepicker/demos/config/datepicker-config.html b/demo/src/app/components/datepicker/demos/config/datepicker-config.html index 83d2f3064e..7c550c3532 100644 --- a/demo/src/app/components/datepicker/demos/config/datepicker-config.html +++ b/demo/src/app/components/datepicker/demos/config/datepicker-config.html @@ -1,3 +1,13 @@ -

This datepicker uses customized default values.

+

This datepicker input uses customized default values.

- +
+
+
+ +
+ +
+
+
+
diff --git a/demo/src/app/components/datepicker/demos/config/datepicker-config.ts b/demo/src/app/components/datepicker/demos/config/datepicker-config.ts index d03851b3ea..d3e71225ee 100644 --- a/demo/src/app/components/datepicker/demos/config/datepicker-config.ts +++ b/demo/src/app/components/datepicker/demos/config/datepicker-config.ts @@ -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', @@ -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']; } } diff --git a/src/datepicker/datepicker-input-config.spec.ts b/src/datepicker/datepicker-input-config.spec.ts new file mode 100644 index 0000000000..7df4346ada --- /dev/null +++ b/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']); + }); +}); diff --git a/src/datepicker/datepicker-input-config.ts b/src/datepicker/datepicker-input-config.ts new file mode 100644 index 0000000000..4366b7389e --- /dev/null +++ b/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']; +} diff --git a/src/datepicker/datepicker-input.spec.ts b/src/datepicker/datepicker-input.spec.ts index 910bd1d55f..437b87986c 100644 --- a/src/datepicker/datepicker-input.spec.ts +++ b/src/datepicker/datepicker-input.spec.ts @@ -11,6 +11,7 @@ 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; @@ -18,12 +19,64 @@ const createTestCmpt = (html: string) => const createTestNativeCmpt = (html: string) => createGenericTestComponent(html, TestNativeComponent) as ComponentFixture; +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(``); + + 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: ''}}); + 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(``); + + 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', () => { diff --git a/src/datepicker/datepicker-input.ts b/src/datepicker/datepicker-input.ts index 2cabfdabce..be9366e618 100644 --- a/src/datepicker/datepicker-input.ts +++ b/src/datepicker/datepicker-input.ts @@ -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, @@ -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. @@ -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. @@ -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, @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()); } diff --git a/src/datepicker/datepicker.module.ts b/src/datepicker/datepicker.module.ts index 850f9ee9b3..8feb350a8c 100644 --- a/src/datepicker/datepicker.module.ts +++ b/src/datepicker/datepicker.module.ts @@ -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'; diff --git a/src/index.ts b/src/index.ts index ddc59a1f97..af0d0c98af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -58,6 +58,7 @@ export { NgbDateParserFormatter, NgbDatepicker, NgbDatepickerConfig, + NgbInputDatepickerConfig, NgbDatepickerI18n, NgbDatepickerI18nHebrew, NgbDatepickerKeyboardService, From fbd6e8d720922e35fe4e6769ba36933c468e9ec6 Mon Sep 17 00:00:00 2001 From: Max Okorokov Date: Fri, 15 Nov 2019 16:33:34 +0100 Subject: [PATCH 2/2] refactor(datepicker): inherit 'NgbInputDatepickerConfig' from 'NgbDatepickerConfig' --- .../demos/config/datepicker-config.ts | 18 ++++++++---------- src/datepicker/datepicker-input-config.ts | 3 ++- src/datepicker/datepicker-input.ts | 6 +++++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/demo/src/app/components/datepicker/demos/config/datepicker-config.ts b/demo/src/app/components/datepicker/demos/config/datepicker-config.ts index d3e71225ee..44cdf850e4 100644 --- a/demo/src/app/components/datepicker/demos/config/datepicker-config.ts +++ b/demo/src/app/components/datepicker/demos/config/datepicker-config.ts @@ -1,6 +1,5 @@ import {Component} from '@angular/core'; import { - NgbDatepickerConfig, NgbCalendar, NgbDate, NgbDateStruct, @@ -10,28 +9,27 @@ import { @Component({ selector: 'ngbd-datepicker-config', templateUrl: './datepicker-config.html', - providers: [NgbDatepickerConfig] // add NgbDatepickerConfig to the component providers + providers: [NgbInputDatepickerConfig] // add config to the component providers }) export class NgbdDatepickerConfig { model: NgbDateStruct; - constructor( - datepickerConfig: NgbDatepickerConfig, inputDatepickerConfig: NgbInputDatepickerConfig, calendar: NgbCalendar) { + constructor(config: NgbInputDatepickerConfig, calendar: NgbCalendar) { // customize default values of datepickers used by this component tree - datepickerConfig.minDate = {year: 1900, month: 1, day: 1}; - datepickerConfig.maxDate = {year: 2099, month: 12, day: 31}; + 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 - datepickerConfig.outsideDays = 'hidden'; + config.outsideDays = 'hidden'; // weekends are disabled - datepickerConfig.markDisabled = (date: NgbDate) => calendar.getWeekday(date) >= 6; + config.markDisabled = (date: NgbDate) => calendar.getWeekday(date) >= 6; // setting datepicker popup to close only on click outside - inputDatepickerConfig.autoClose = 'outside'; + config.autoClose = 'outside'; // setting datepicker popup to open above the input - inputDatepickerConfig.placement = ['top-left', 'top-right']; + config.placement = ['top-left', 'top-right']; } } diff --git a/src/datepicker/datepicker-input-config.ts b/src/datepicker/datepicker-input-config.ts index 4366b7389e..3ed85c6b31 100644 --- a/src/datepicker/datepicker-input-config.ts +++ b/src/datepicker/datepicker-input-config.ts @@ -1,5 +1,6 @@ import {Injectable} from '@angular/core'; +import {NgbDatepickerConfig} from './datepicker-config'; import {PlacementArray} from '../util/positioning'; /** @@ -9,7 +10,7 @@ import {PlacementArray} from '../util/positioning'; * order to provide default values for all the datepicker inputs used in the application. */ @Injectable({providedIn: 'root'}) -export class NgbInputDatepickerConfig { +export class NgbInputDatepickerConfig extends NgbDatepickerConfig { autoClose: boolean | 'inside' | 'outside' = true; container: null | 'body'; positionTarget: string | HTMLElement; diff --git a/src/datepicker/datepicker-input.ts b/src/datepicker/datepicker-input.ts index be9366e618..d558b017cc 100644 --- a/src/datepicker/datepicker-input.ts +++ b/src/datepicker/datepicker-input.ts @@ -33,6 +33,7 @@ import {NgbDate} from './ngb-date'; import {NgbDateParserFormatter} from './ngb-date-parser-formatter'; import {NgbDateStruct} from './ngb-date-struct'; import {NgbInputDatepickerConfig} from './datepicker-input-config'; +import {NgbDatepickerConfig} from './datepicker-config'; const NGB_DATEPICKER_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, @@ -61,7 +62,10 @@ const NGB_DATEPICKER_VALIDATOR = { '(blur)': 'onBlur()', '[disabled]': 'disabled' }, - providers: [NGB_DATEPICKER_VALUE_ACCESSOR, NGB_DATEPICKER_VALIDATOR, NgbDatepickerService] + providers: [ + NGB_DATEPICKER_VALUE_ACCESSOR, NGB_DATEPICKER_VALIDATOR, NgbDatepickerService, + {provide: NgbDatepickerConfig, useExisting: NgbInputDatepickerConfig} + ], }) export class NgbInputDatepicker implements OnChanges, OnDestroy, ControlValueAccessor, Validator {