Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(datepicker): input datepicker global config #3275

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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,16 +1,21 @@
import {Component} from '@angular/core';
import {NgbDatepickerConfig, NgbCalendar, NgbDate, NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';
import {
NgbCalendar,
NgbDate,
NgbDateStruct,
NgbInputDatepickerConfig
} from '@ng-bootstrap/ng-bootstrap';

@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(config: NgbDatepickerConfig, calendar: NgbCalendar) {
constructor(config: 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};
Expand All @@ -20,5 +25,11 @@ export class NgbdDatepickerConfig {

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

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

// setting datepicker popup to open above the input
config.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']);
});
});
18 changes: 18 additions & 0 deletions src/datepicker/datepicker-input-config.ts
@@ -0,0 +1,18 @@
import {Injectable} from '@angular/core';

import {NgbDatepickerConfig} from './datepicker-config';
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 extends NgbDatepickerConfig {
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
14 changes: 10 additions & 4 deletions src/datepicker/datepicker-input.ts
Expand Up @@ -32,6 +32,8 @@ 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';
import {NgbDatepickerConfig} from './datepicker-config';

const NGB_DATEPICKER_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
Expand Down Expand Up @@ -60,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 {
Expand All @@ -81,7 +86,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 +182,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 +265,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