diff --git a/packages/stark-ui/src/modules/date-range-picker/components/_date-range-picker.component.scss b/packages/stark-ui/src/modules/date-range-picker/components/_date-range-picker.component.scss
index 5b01e5a749..09801c64ee 100644
--- a/packages/stark-ui/src/modules/date-range-picker/components/_date-range-picker.component.scss
+++ b/packages/stark-ui/src/modules/date-range-picker/components/_date-range-picker.component.scss
@@ -3,7 +3,7 @@
/* ============================================================================== */
/* stark-ui: src/modules/date-range-picker/components/date-range-picker/_date-range-picker.component.scss */
-.stark-date-range-picker stark-date-picker:first-child {
+.stark-date-range-picker mat-form-field {
margin-right: 16px;
}
diff --git a/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.html b/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.html
index 6462b964e1..f0b979ab4d 100644
--- a/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.html
+++ b/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.html
@@ -3,30 +3,37 @@
#startPicker
[pickerId]="rangePickerId + '-start'"
[pickerName]="rangePickerName + '-start'"
- [value]="startDate"
- [dateFilter]="dateFilter"
- [disabled]="isDisabled"
+ [formControl]="startDateFormControl"
[placeholder]="startDateLabel | translate"
+ [dateFilter]="dateFilter"
+ [dateMask]="dateMask"
[min]="startMinDate"
[max]="startMaxDate"
- [dateMask]="dateMask"
- (dateChange)="onDateStartChanged($event)"
+ [required]="required"
>
+
+
+
+
+
+
+
+
+
diff --git a/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.spec.ts b/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.spec.ts
index 7b92d1c9cb..f4d5d671d1 100644
--- a/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.spec.ts
+++ b/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.spec.ts
@@ -1,7 +1,8 @@
+/* tslint:disable:no-null-keyword completed-docs max-inline-declarations no-big-function */
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
-import { EventEmitter } from "@angular/core";
-import { async, ComponentFixture, TestBed } from "@angular/core/testing";
-import { ReactiveFormsModule } from "@angular/forms";
+import { Component, EventEmitter, ViewChild } from "@angular/core";
+import { async, ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
+import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, ValidationErrors } from "@angular/forms";
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from "@angular/material/core";
import { MAT_MOMENT_DATE_FORMATS, MomentDateAdapter } from "@angular/material-moment-adapter";
import { MatDatepickerModule } from "@angular/material/datepicker";
@@ -16,160 +17,324 @@ import { StarkTimestampMaskDirective } from "../../input-mask-directives";
import moment from "moment";
describe("DateRangePickerComponent", () => {
- let fixture: ComponentFixture;
- let component: StarkDateRangePickerComponent;
-
- beforeEach(async(() => {
- return TestBed.configureTestingModule({
- declarations: [StarkTimestampMaskDirective, StarkDatePickerComponent, StarkDateRangePickerComponent],
- imports: [NoopAnimationsModule, MatDatepickerModule, MatFormFieldModule, TranslateModule.forRoot(), ReactiveFormsModule],
- providers: [
- { provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
- { provide: STARK_ROUTING_SERVICE, useClass: MockStarkRoutingService },
- { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
- { provide: MAT_DATE_LOCALE, useValue: "en-us" },
- { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }
- ]
- }).compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(StarkDateRangePickerComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
+ describe("uncontrolled", () => {
+ let fixture: ComponentFixture;
+ let component: StarkDateRangePickerComponent;
- describe("on initialization", () => {
- it("should set internal component properties", () => {
- expect(fixture).toBeDefined();
- expect(component).toBeDefined();
+ beforeEach(async(() => {
+ return TestBed.configureTestingModule({
+ declarations: [StarkTimestampMaskDirective, StarkDatePickerComponent, StarkDateRangePickerComponent],
+ imports: [
+ NoopAnimationsModule,
+ MatDatepickerModule,
+ MatFormFieldModule,
+ FormsModule,
+ ReactiveFormsModule,
+ TranslateModule.forRoot()
+ ],
+ providers: [
+ { provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
+ { provide: STARK_ROUTING_SERVICE, useClass: MockStarkRoutingService },
+ { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
+ { provide: MAT_DATE_LOCALE, useValue: "en-us" },
+ { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }
+ ]
+ }).compileComponents();
+ }));
- expect(component.logger).not.toBeNull();
- expect(component.logger).toBeDefined();
+ beforeEach(() => {
+ fixture = TestBed.createComponent(StarkDateRangePickerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
});
- it("should NOT have any inputs set", () => {
- expect(component.dateFilter).toBeUndefined();
- expect(component.dateMask).toBeUndefined();
- expect(component.endDate).toBeUndefined();
- expect(component.endDateLabel).toBeDefined();
- expect(component.endDateLabel).toEqual("STARK.DATE_RANGE_PICKER.TO");
- expect(component.endMaxDate).toBeUndefined();
- expect(component.endMinDate).toBeUndefined();
- expect(component.isDisabled).toBeUndefined();
- expect(component.rangePickerId).toBeDefined();
- expect(component.rangePickerId).toEqual("");
- expect(component.rangePickerName).toBeDefined();
- expect(component.rangePickerName).toEqual("");
- expect(component.startDate).toBeUndefined();
- expect(component.startDateLabel).toBeDefined();
- expect(component.startDateLabel).toEqual("STARK.DATE_RANGE_PICKER.FROM");
- expect(component.startMaxDate).toBeUndefined();
- expect(component.startMinDate).toBeUndefined();
- expect(component.dateRangeChanged).toBeDefined();
- expect(component.dateRangeChanged).toEqual(new EventEmitter());
- });
- });
+ describe("on initialization", () => {
+ it("should set internal component properties", () => {
+ expect(fixture).toBeDefined();
+ expect(component).toBeDefined();
- describe("datepickers properties binding", () => {
- it("the ids of the datepickers should be set correctly", () => {
- component.rangePickerId = "test-id";
- fixture.detectChanges();
- let input: HTMLElement = fixture.nativeElement.querySelector("#test-id-start-input");
- expect(input).not.toBeNull();
- input = fixture.nativeElement.querySelector("#test-id-end-input");
- expect(input).not.toBeNull();
- let picker: HTMLElement = fixture.nativeElement.querySelector("#test-id-start");
- expect(picker).not.toBeNull();
- picker = fixture.nativeElement.querySelector("#test-id-end");
- expect(picker).not.toBeNull();
- });
+ expect(component.logger).not.toBeNull();
+ expect(component.logger).toBeDefined();
+ });
- it("the names of the datepickers should be set correctly", () => {
- component.rangePickerName = "test-name";
- fixture.detectChanges();
- let input: HTMLElement = fixture.nativeElement.querySelector('[name="test-name-start"]');
- expect(input).not.toBeNull();
- input = fixture.nativeElement.querySelector('[name="test-name-end"]');
- expect(input).not.toBeNull();
+ it("should NOT have any inputs set", () => {
+ expect(component.dateFilter).toBeUndefined();
+ expect(component.dateMask).toBeUndefined();
+ expect(component.endDate).toBeUndefined();
+ expect(component.endDateLabel).toBeDefined();
+ expect(component.endDateLabel).toEqual("STARK.DATE_RANGE_PICKER.TO");
+ expect(component.endMaxDate).toBeUndefined();
+ expect(component.endMinDate).toBeUndefined();
+ expect(component.rangePickerId).toBeDefined();
+ expect(component.rangePickerId).toEqual("");
+ expect(component.rangePickerName).toBeDefined();
+ expect(component.rangePickerName).toEqual("");
+ expect(component.startDate).toBeUndefined();
+ expect(component.startDateLabel).toBeDefined();
+ expect(component.startDateLabel).toEqual("STARK.DATE_RANGE_PICKER.FROM");
+ expect(component.startMaxDate).toBeUndefined();
+ expect(component.startMinDate).toBeUndefined();
+ expect(component.dateRangeChanged).toBeDefined();
+ expect(component.dateRangeChanged).toEqual(new EventEmitter());
+ });
});
- it("the datepickers should be disabled when isDisabled is true", () => {
- component.isDisabled = true;
- fixture.detectChanges();
- expect(component.startPicker.pickerInput.disabled).toBe(true);
- expect(component.endPicker.pickerInput.disabled).toBe(true);
- });
+ describe("datepickers properties binding", () => {
+ it("the ids of the datepickers should be set correctly", () => {
+ component.rangePickerId = "test-id";
+ fixture.detectChanges();
+ let input: HTMLElement = fixture.nativeElement.querySelector("#test-id-start-input");
+ expect(input).not.toBeNull();
+ input = fixture.nativeElement.querySelector("#test-id-end-input");
+ expect(input).not.toBeNull();
+ let picker: HTMLElement = fixture.nativeElement.querySelector("#test-id-start");
+ expect(picker).not.toBeNull();
+ picker = fixture.nativeElement.querySelector("#test-id-end");
+ expect(picker).not.toBeNull();
+ });
- it("the placeholders of the datepickers should be set correctly", () => {
- component.startDateLabel = "startDateLabel";
- component.endDateLabel = "endDateLabel";
- fixture.detectChanges();
- let input: HTMLElement = fixture.nativeElement.querySelector('[ng-reflect-placeholder="startDateLabel"]');
- expect(input).not.toBeNull();
- input = fixture.nativeElement.querySelector('[ng-reflect-placeholder="endDateLabel"]');
- expect(input).not.toBeNull();
+ it("the names of the datepickers should be set correctly", () => {
+ component.rangePickerName = "test-name";
+ fixture.detectChanges();
+ let input: HTMLElement = fixture.nativeElement.querySelector('[name="test-name-start"]');
+ expect(input).not.toBeNull();
+ input = fixture.nativeElement.querySelector('[name="test-name-end"]');
+ expect(input).not.toBeNull();
+ });
+
+ it("the datepickers should be disabled when isDisabled is true", () => {
+ component.disabled = true;
+ fixture.detectChanges();
+ expect(component.startPicker.pickerInput.disabled).toBe(true);
+ expect(component.endPicker.pickerInput.disabled).toBe(true);
+ });
+
+ it("the placeholders of the datepickers should be set correctly", () => {
+ component.startDateLabel = "startDateLabel";
+ component.endDateLabel = "endDateLabel";
+ fixture.detectChanges();
+ let input: HTMLElement = fixture.nativeElement.querySelector('[ng-reflect-placeholder="startDateLabel"]');
+ expect(input).not.toBeNull();
+ input = fixture.nativeElement.querySelector('[ng-reflect-placeholder="endDateLabel"]');
+ expect(input).not.toBeNull();
+ });
+
+ it("the datepickers min date should be set correctly", () => {
+ const minDate = new Date(2018, 6, 1);
+ component.startMinDate = minDate;
+ component.endMinDate = minDate;
+ fixture.detectChanges();
+ expect(component.startPicker.pickerInput.min).not.toBeNull();
+ expect((component.startPicker.pickerInput.min).toDate()).toEqual(minDate);
+ expect(component.endPicker.pickerInput.min).not.toBeNull();
+ expect((component.endPicker.pickerInput.min).toDate()).toEqual(minDate);
+ });
+
+ it("the datepickers max date should be set correctly", () => {
+ const maxDate = new Date(2018, 6, 2);
+ component.startMaxDate = maxDate;
+ component.endMaxDate = maxDate;
+ fixture.detectChanges();
+ expect(component.startPicker.pickerInput.max).not.toBeNull();
+ expect((component.startPicker.pickerInput.max).toDate()).toEqual(maxDate);
+ expect(component.endPicker.pickerInput.max).not.toBeNull();
+ expect((component.endPicker.pickerInput.max).toDate()).toEqual(maxDate);
+ });
+
+ it("the datepickers value should be set correctly", fakeAsync(() => {
+ const date = new Date(2018, 6, 3);
+ component.startDate = date;
+ component.endDate = date;
+ fixture.detectChanges();
+ tick();
+
+ expect(component.startPicker.value).not.toBeNull("The value of the startDate date-picker should be set.");
+ expect(component.startPicker.value).toEqual(date);
+ expect(component.endPicker.value).not.toBeNull("The value of the endDate date-picker should be set.");
+ expect(component.endPicker.value).toEqual(date);
+ }));
});
- it("the datepickers min date should be set correctly", () => {
- const minDate = new Date(2018, 6, 1);
- component.startMinDate = minDate;
- component.endMinDate = minDate;
- fixture.detectChanges();
- expect(component.startPicker.pickerInput.min).not.toBeNull();
- expect((component.startPicker.pickerInput.min).toDate()).toEqual(minDate);
- expect(component.endPicker.pickerInput.min).not.toBeNull();
- expect((component.endPicker.pickerInput.min).toDate()).toEqual(minDate);
+ describe("dates selection", () => {
+ it("the end date should be invalid if before startdate", () => {
+ component.startDate = new Date(2018, 6, 5);
+ component.endDate = new Date(2018, 6, 4);
+ fixture.detectChanges();
+
+ expect(component.endDateFormControl.status).toBe("INVALID");
+ });
+
+ it("the end date should be correctly set if after the start date", () => {
+ const endDate = new Date(2018, 6, 7);
+ component.startDate = new Date(2018, 6, 6);
+ component.endDate = endDate;
+ fixture.detectChanges();
+
+ expect(component.endDate).toEqual(endDate);
+ });
+
+ it("the end date should be correctly set if after the start date is undefined", () => {
+ const endDate = new Date(2018, 6, 8);
+ component.startDate = undefined;
+ component.endDate = endDate;
+ fixture.detectChanges();
+
+ expect(component.endDate).toEqual(endDate);
+ });
});
+ });
- it("the datepickers max date should be set correctly", () => {
- const maxDate = new Date(2018, 6, 2);
- component.startMaxDate = maxDate;
- component.endMaxDate = maxDate;
- fixture.detectChanges();
- expect(component.startPicker.pickerInput.max).not.toBeNull();
- expect((component.startPicker.pickerInput.max).toDate()).toEqual(maxDate);
- expect(component.endPicker.pickerInput.max).not.toBeNull();
- expect((component.endPicker.pickerInput.max).toDate()).toEqual(maxDate);
+ @Component({
+ selector: "test-model",
+ template: `
+
+ `
+ })
+ class TestModelComponent {
+ @ViewChild(StarkDateRangePickerComponent)
+ public dateRangePicker!: StarkDateRangePickerComponent;
+
+ public dateRange = {};
+ }
+
+ describe("with ngModel", () => {
+ let fixture: ComponentFixture;
+ let hostComponent: TestModelComponent;
+ let component: StarkDateRangePickerComponent;
+
+ beforeEach(async(() =>
+ TestBed.configureTestingModule({
+ declarations: [StarkTimestampMaskDirective, StarkDatePickerComponent, StarkDateRangePickerComponent, TestModelComponent],
+ imports: [
+ NoopAnimationsModule,
+ MatDatepickerModule,
+ MatFormFieldModule,
+ FormsModule,
+ ReactiveFormsModule,
+ TranslateModule.forRoot()
+ ],
+ providers: [
+ { provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
+ { provide: STARK_ROUTING_SERVICE, useClass: MockStarkRoutingService },
+ { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
+ { provide: MAT_DATE_LOCALE, useValue: "en-us" },
+ { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }
+ ]
+ }).compileComponents()));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestModelComponent);
+ hostComponent = fixture.componentInstance;
+ component = hostComponent.dateRangePicker;
});
- it("the datepickers value should be set correctly", () => {
- const date = new Date(2018, 6, 3);
- component.startDate = date;
- component.endDate = date;
+ it("should update when model is updated", fakeAsync(() => {
+ const expected = { startDate: new Date(2019, 0, 1), endDate: new Date(2019, 0, 2) };
+
+ hostComponent.dateRange = expected;
fixture.detectChanges();
- expect(component.startPicker.pickerInput.value).not.toBeNull();
- expect((component.startPicker.pickerInput.value).toDate()).toEqual(date);
- expect(component.endPicker.pickerInput.value).not.toBeNull();
- expect((component.endPicker.pickerInput.value).toDate()).toEqual(date);
- });
+
+ tick();
+
+ expect(component.startDate).toBeDefined();
+ expect(component.endDate).toBeDefined();
+
+ expect(component.startDate).toEqual(expected.startDate);
+ expect(component.endDate).toEqual(expected.endDate);
+ }));
});
- describe("dates selection", () => {
- it("the end date should be undefined before the start date", () => {
- component.startDate = new Date(2018, 6, 5);
- component.endDate = new Date(2018, 6, 4);
- fixture.detectChanges();
- component.checkDates();
- expect(component.endDate).toBeUndefined();
+ @Component({
+ selector: "test-form-group",
+ template: `
+
+ START-ERROR
+ END-ERROR
+
+ `
+ })
+ class TestFormGroupComponent {
+ @ViewChild(StarkDateRangePickerComponent)
+ public dateRangePicker!: StarkDateRangePickerComponent;
+
+ public formGroup = new FormGroup({
+ startDate: new FormControl(),
+ endDate: new FormControl()
});
+ }
+
+ describe("with formGroup", () => {
+ let fixture: ComponentFixture;
+ let hostComponent: TestFormGroupComponent;
+ let component: StarkDateRangePickerComponent;
- it("the end date should be correctly set if after the start date", () => {
- const endDate = new Date(2018, 6, 7);
- component.startDate = new Date(2018, 6, 6);
- component.endDate = endDate;
+ beforeEach(async(() =>
+ TestBed.configureTestingModule({
+ declarations: [
+ StarkTimestampMaskDirective,
+ StarkDatePickerComponent,
+ StarkDateRangePickerComponent,
+ TestFormGroupComponent
+ ],
+ imports: [
+ NoopAnimationsModule,
+ MatDatepickerModule,
+ MatFormFieldModule,
+ FormsModule,
+ ReactiveFormsModule,
+ TranslateModule.forRoot()
+ ],
+ providers: [
+ { provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() },
+ { provide: STARK_ROUTING_SERVICE, useClass: MockStarkRoutingService },
+ { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS },
+ { provide: MAT_DATE_LOCALE, useValue: "en-us" },
+ { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }
+ ]
+ }).compileComponents()));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestFormGroupComponent);
+ hostComponent = fixture.componentInstance;
+ component = hostComponent.dateRangePicker;
+ });
+
+ it("should update when form group is updated", () => {
+ const expected = { startDate: new Date(2019, 0, 1), endDate: new Date(2019, 0, 2) };
+
+ hostComponent.formGroup.setValue(expected);
fixture.detectChanges();
- component.checkDates();
- expect(component.endDate).toEqual(endDate);
+
+ expect(component.startDate).toBeDefined();
+ expect(component.endDate).toBeDefined();
+
+ expect(component.startDate).toEqual(expected.startDate);
+ expect(component.endDate).toEqual(expected.endDate);
});
- it("the end date should be correctly set if after the start date is undefined", () => {
- const endDate = new Date(2018, 6, 8);
- component.startDate = undefined;
- component.endDate = endDate;
+ it("should show errors at the correct input", () => {
+ const { startDate: startDateFC, endDate: endDateFC } = hostComponent.formGroup.controls;
+ const alwaysFail = (): ValidationErrors => ({ alwaysFail: "error" });
+
+ startDateFC.setValidators(alwaysFail);
+ startDateFC.setValue(new Date());
+ startDateFC.markAsTouched();
+ startDateFC.markAsDirty();
+
+ endDateFC.setValidators(alwaysFail);
+ endDateFC.setValue(new Date());
+ endDateFC.markAsTouched();
+ endDateFC.markAsDirty();
+
fixture.detectChanges();
- component.checkDates();
- expect(component.endDate).toEqual(endDate);
+
+ const startDateError = fixture.nativeElement.querySelectorAll("mat-form-field mat-error").item(0);
+ expect(startDateError).not.toBeNull();
+ expect(startDateError.textContent).toEqual("START-ERROR");
+
+ const endDateError = fixture.nativeElement.querySelectorAll("mat-form-field mat-error").item(1);
+ expect(endDateError).not.toBeNull();
+ expect(endDateError.textContent).toEqual("END-ERROR");
});
});
});
diff --git a/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.ts b/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.ts
index 5a191d7572..3062410773 100644
--- a/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.ts
+++ b/packages/stark-ui/src/modules/date-range-picker/components/date-range-picker.component.ts
@@ -1,12 +1,27 @@
-import { Component, ElementRef, EventEmitter, Inject, Input, OnInit, Output, Renderer2, ViewChild, ViewEncapsulation } from "@angular/core";
-import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
-import moment from "moment";
+/* tslint:disable:no-null-keyword */
import {
- StarkDatePickerComponent,
- StarkDatePickerFilter,
- StarkDatePickerMaskConfig
-} from "../../date-picker/components/date-picker.component";
+ AfterViewInit,
+ Component,
+ ElementRef,
+ EventEmitter,
+ Inject,
+ Injector,
+ Input,
+ OnDestroy,
+ OnInit,
+ Output,
+ Renderer2,
+ Type,
+ ViewChild,
+ ViewEncapsulation
+} from "@angular/core";
+import { ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR, NgControl, ValidatorFn, Validators } from "@angular/forms";
+import { Subscription } from "rxjs";
+import noop from "lodash-es/noop";
+import get from "lodash-es/get";
+import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
import { AbstractStarkUiComponent } from "../../../common/classes/abstract-component";
+import { StarkDatePickerComponent, StarkDatePickerFilter, StarkDatePickerMaskConfig } from "../../date-picker";
import { StarkDateRangePickerEvent } from "./date-range-picker-event.intf";
/**
@@ -24,91 +39,197 @@ const componentName = "stark-date-range-picker";
// We need to use host instead of @HostBinding: https://github.com/NationalBankBelgium/stark/issues/664
host: {
class: componentName
- }
+ },
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ multi: true,
+ useExisting: StarkDateRangePickerComponent
+ }
+ ]
})
-export class StarkDateRangePickerComponent extends AbstractStarkUiComponent implements OnInit {
+export class StarkDateRangePickerComponent extends AbstractStarkUiComponent
+ implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy {
/**
- * Filter function or a string
- * Will be applied to both date-picker
+ * ControlValueAccessor listener
+ * @ignore
*/
- @Input()
- public dateFilter?: StarkDatePickerFilter;
+ private _onTouched: () => void = noop;
/**
- * Timestamp Mask Configuration to apply on the start/end date-picker.
- * If `true` is passed, the default mask config is applied: {DEFAULT_DATE_MASK_CONFIG|DEFAULT_DATE_MASK_CONFIG}
- * If `false` is passed or if `dateMask` is not present, the directive is disabled.
- * If a `StarkTimestampMaskConfig` is passed, it is set as the date mask config.
+ * ControlValueAccessor listener
+ * @ignore
*/
- @Input()
- public dateMask?: StarkDatePickerMaskConfig;
+ private _onChange: (_dateRange: StarkDateRangePickerEvent) => void = noop;
/**
- * Source Date to be bound to the end datepicker model
+ * Subscriptions to be removed at end of component lifecycle
+ * @ignore
+ */
+ private subs: Subscription[] = [];
+
+ /*--- VIEW CHILDREN ---*/
+ /**
+ * Reference to the start datepicker embedded in this component
+ */
+ @ViewChild("startPicker")
+ public startPicker!: StarkDatePickerComponent;
+
+ /**
+ * Reference to the end datepicker embedded in this component
+ */
+ @ViewChild("endPicker")
+ public endPicker!: StarkDatePickerComponent;
+
+ /*--- START-DATE CONFIGURATIONS ---*/
+
+ /**
+ * @ignore
+ * @internal
+ */
+ private _startBeforeEndValidator: ValidatorFn = ({ value }) => {
+ return value instanceof Date && this.endDate instanceof Date && value.getTime() > this.endDate.getTime()
+ ? { startBeforeEnd: true }
+ : null;
+ };
+
+ /**
+ * Source Date to be bound to the start datepicker model
*/
@Input()
- public endDate?: Date;
+ public get startDate(): Date | undefined {
+ return this._startDate.value || undefined;
+ }
+
+ public set startDate(value: Date | undefined) {
+ this._startDate.setValue(value);
+ }
+
+ /**
+ * The internal formControl used to manage the state of the starkDatePicker for the start date.
+ * This can be used to retrieve internal errors.
+ *
+ * @example
+ *
+ *
+ *
+ * {{starkDateRangePicker.startDateFormControl.hasError('required') ? "Start date is required" : null }}
+ *
+ *
+ */
+ public get startDateFormControl(): FormControl {
+ return this._startDate;
+ }
+
+ /**
+ * @ignore
+ */
+ private _startDate = new FormControl();
/**
* Label to be displayed in the end datepicker
*/
@Input()
- public endDateLabel = "STARK.DATE_RANGE_PICKER.TO";
+ public startDateLabel = "STARK.DATE_RANGE_PICKER.FROM";
/**
- * Maximum date of the end date picker
+ * Minimum date of the start date picker
*/
@Input()
- public endMaxDate?: Date;
+ public startMinDate?: Date;
/**
- * Minimum date of the end date picker
+ * Maximum date of the start date picker
*/
@Input()
- public endMinDate?: Date;
+ public startMaxDate?: Date;
+
+ /*--- END-DATE CONFIGURATIONS ---*/
/**
- * Whether the datepickers are disabled
+ * @ignore
+ * @internal
*/
- @Input()
- public isDisabled?: boolean;
+ private _endAfterStartValidator: ValidatorFn = ({ value }) => {
+ return value instanceof Date && this.startDate instanceof Date && value.getTime() < this.startDate.getTime()
+ ? { endAfterStart: true }
+ : null;
+ };
/**
- * HTML "name" attribute of the element.
+ * Source Date to be bound to the end datepicker model
*/
@Input()
- public rangePickerId = "";
+ public get endDate(): Date | undefined {
+ return this._endDate.value || undefined;
+ }
+
+ public set endDate(value: Date | undefined) {
+ this._endDate.setValue(value || null);
+ }
/**
- * HTML "name" attribute of the element.
+ * The internal formControl used to manage the state of the starkDatePicker for the start date.
+ * This can be used to retrieve internal errors.
+ *
+ * @example
+ *
+ *
+ *
+ * {{starkDateRangePicker.endDateFormControl.hasError('required') ? "End date is required" : null }}
+ *
+ *
*/
- @Input()
- public rangePickerName = "";
+ public get endDateFormControl(): FormControl {
+ return this._endDate;
+ }
/**
- * Source Date to be bound to the start datepicker model
+ * @ignore
*/
+ private _endDate = new FormControl();
+ /**
+ * Label to be displayed in the end datepicker
+ */
@Input()
- public startDate?: Date;
+ public endDateLabel = "STARK.DATE_RANGE_PICKER.TO";
/**
- * Label to be displayed in the start datepicker
+ * Minimum date of the end date picker
*/
@Input()
- public startDateLabel = "STARK.DATE_RANGE_PICKER.FROM";
+ public endMinDate?: Date;
/**
- * Maximum date of the start date picker
+ * Maximum date of the end date picker
*/
@Input()
- public startMaxDate?: Date;
+ public endMaxDate?: Date;
+
+ /*--- SHARED CONFIGURATIONS ---*/
/**
- * Minimum date of the start date picker
+ * Input to manage both start date and end date.
*/
@Input()
- public startMinDate?: Date;
+ public set rangeFormGroup(val: FormGroup) {
+ const { startDate, endDate } = val.controls;
+ if (!(startDate instanceof FormControl && endDate instanceof FormControl)) {
+ this.logger.error(`[${componentName}]: "formGroup" requires a FormControl for startDate and endDate`);
+ return;
+ }
+
+ this._formGroup = val;
+ // overwrite internal formControls
+ this._startDate = startDate;
+ this._endDate = endDate;
+ }
+
+ /**
+ * @ignore
+ */
+ private _formGroup?: FormGroup;
/**
* Output that will emit a specific date whenever the selection has changed
@@ -117,25 +238,75 @@ export class StarkDateRangePickerComponent extends AbstractStarkUiComponent impl
public readonly dateRangeChanged = new EventEmitter();
/**
- * Reference to the start datepicker embedded in this component
+ * Filter function or a string
+ * Will be applied to both date-picker
*/
- @ViewChild("startPicker")
- public startPicker!: StarkDatePickerComponent;
+ @Input()
+ public dateFilter?: StarkDatePickerFilter;
/**
- * Reference to the end datepicker embedded in this component
+ * Timestamp Mask Configuration to apply on the start/end date-picker.
+ * If `true` is passed, the default mask config is applied: {DEFAULT_DATE_MASK_CONFIG|DEFAULT_DATE_MASK_CONFIG}
+ * If `false` is passed or if `dateMask` is not present, the directive is disabled.
+ * If a `StarkTimestampMaskConfig` is passed, it is set as the date mask config.
*/
- @ViewChild("endPicker")
- public endPicker!: StarkDatePickerComponent;
+ @Input()
+ public dateMask?: StarkDatePickerMaskConfig;
+
+ /**
+ * Whether the datepickers are disabled
+ */
+ @Input()
+ public set disabled(val: boolean) {
+ if (this._formGroup) {
+ this.logger.warn(`[${componentName}]:
+ It looks like you're using the "disabled" attribute with a "formGroup". We recommend using following approach.
+
+ Example:
+ dateRangeFormGroup = new FormGroup({
+ startDate: new FormControl({value: null, disabled: true}),
+ endDate: new FormControl({value: null, disabled: true})
+ });
+ `);
+ }
+
+ if (val) {
+ this.startDateFormControl.disable();
+ this.endDateFormControl.disable();
+ } else {
+ this.startDateFormControl.enable();
+ this.endDateFormControl.enable();
+ }
+ }
+
+ /**
+ * Whether the datepickers are required
+ */
+ @Input()
+ public required = false;
+
+ /**
+ * HTML "name" attribute of the element.
+ */
+ @Input()
+ public rangePickerId = "";
+
+ /**
+ * HTML "name" attribute of the element.
+ */
+ @Input()
+ public rangePickerName = "";
/**
* Class constructor
* @param logger - The logger of the application
+ * @param injector - The Injector of the application
* @param renderer - Angular Renderer wrapper for DOM manipulations.
* @param elementRef - Reference to the DOM element where this directive is applied to.
*/
public constructor(
@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService,
+ private injector: Injector,
protected renderer: Renderer2,
protected elementRef: ElementRef
) {
@@ -143,41 +314,115 @@ export class StarkDateRangePickerComponent extends AbstractStarkUiComponent impl
}
/**
- * Component lifecycle hook
+ * Angular lifecycle method
*/
public ngOnInit(): void {
this.logger.debug(componentName + ": component initialized");
+ this._setupFormControls();
+ }
+
+ /**
+ * Angular lifecycle method
+ */
+ public ngAfterViewInit(): void {
+ this._setupNgControl();
}
/**
- * Handle the date changed on the start datepicker
+ * @ignore
+ * @internal
*/
- public onDateStartChanged(date: Date): void {
- this.startDate = date;
- this.checkDates();
+ private _setupFormControls(): void {
+ // Merge the original validators with ones from the start/end date form controls
+ this.startDateFormControl.setValidators(Validators.compose([this.startDateFormControl.validator, this._startBeforeEndValidator]));
+ this.endDateFormControl.setValidators(Validators.compose([this.endDateFormControl.validator, this._endAfterStartValidator]));
+
+ for (const subscription of this.subs) {
+ subscription.unsubscribe();
+ }
+ this.subs.push(
+ this.startDateFormControl.valueChanges.subscribe(() => {
+ this.endDateFormControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
+ this.onDateChanged();
+ }),
+ this.endDateFormControl.valueChanges.subscribe(() => {
+ this.startDateFormControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
+ this.onDateChanged();
+ })
+ );
}
/**
- * Handle the date changed on the end datepicker
+ * Link the passed control (if one is available) to the internal formControls.
+ * @ignore
+ * @internal
*/
- public onDateEndChanged(date: Date): void {
- this.endDate = date;
- this.checkDates();
+ private _setupNgControl(): void {
+ // Get the ngControl from the injector
+ const ngControl = this.injector.get(>NgControl, null);
+ if (!ngControl) {
+ return;
+ }
+
+ ngControl.valueAccessor = this;
+
+ if (typeof get(ngControl, "control.validator") === "function") {
+ this.logger.warn(
+ `[${componentName}]: validators set on the control will not be used, use the "formGroup" attribute to manage your own validations.`
+ );
+ }
}
/**
- * Validate the dates and emit the dateRangeChanged event
+ * Angular lifecycle method
*/
- public checkDates(): void {
- if (moment.isDate(this.startDate) && moment.isDate(this.endDate) && moment(this.endDate).isBefore(this.startDate)) {
- this.logger.error("StarkDateRangePicker: Start Date cannot be lower than End Date. End Date will be cleared");
- this.endDate = undefined;
- // tslint:disable-next-line:no-null-keyword
- this.endPicker.pickerInput.value = null;
+ public ngOnDestroy(): void {
+ for (const subscription of this.subs) {
+ subscription.unsubscribe();
}
- this.dateRangeChanged.emit({
- startDate: this.startDate,
- endDate: this.endDate
- });
+ }
+
+ /**
+ * Handle the date changed on the start and end datepicker
+ */
+ public onDateChanged(): void {
+ this._onTouched();
+
+ const dateRange: StarkDateRangePickerEvent = { startDate: this.startDate, endDate: this.endDate };
+
+ this._onChange(dateRange);
+ this.dateRangeChanged.emit(dateRange);
+ }
+
+ /*--- Control Value Accessor methods---*/
+
+ /**
+ * @ignore
+ */
+ public registerOnChange(fn: (_dateRange: StarkDateRangePickerEvent) => void): void {
+ this._onChange = fn;
+ }
+
+ /**
+ * @ignore
+ */
+ public registerOnTouched(fn: () => void): void {
+ this._onTouched = fn;
+ }
+
+ /**
+ * @ignore
+ */
+ public setDisabledState(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ /**
+ * @ignore
+ */
+ public writeValue(dateRange: StarkDateRangePickerEvent): void {
+ dateRange = dateRange || {};
+ this.startDateFormControl.setValue(dateRange.startDate, { emitEvent: false });
+ this.endDateFormControl.setValue(dateRange.endDate, { emitEvent: false });
}
}
diff --git a/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.html b/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.html
index c7cce95a21..1b4b1b7b35 100644
--- a/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.html
+++ b/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.html
@@ -1,80 +1,76 @@
SHOWCASE.DEMO.DATE_RANGE_PICKER.TITLE
SHOWCASE.DEMO.SHARED.EXAMPLE_VIEWER_LIST
-
-
-
-
-
- isDisabled
+
+
+
+
+ {{ error | translate }}
+
+
+
+
+ {{ error | translate }}
+
+
+
+
+
+ {{ (modelDisabled ? "SHOWCASE.COMMON.DISABLED" : "SHOWCASE.COMMON.ENABLED") | translate }}
+
+
+
+
+
+
+ {{ error | translate }}
+
+
+
+
+ {{ error | translate }}
+
+
+
+
+
+ {{ (dateRangeFormGroup.disabled ? "SHOWCASE.COMMON.DISABLED" : "SHOWCASE.COMMON.ENABLED") | translate }}
+
-
-
-
-
- isDisabled
+
-
-
-
-
- isDisabled
+
diff --git a/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.ts b/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.ts
index 6eb975edb2..952df3c7ef 100644
--- a/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.ts
+++ b/showcase/src/app/demo-ui/pages/date-range-picker/demo-date-range-picker-page.component.ts
@@ -1,30 +1,68 @@
-import { Component, Inject } from "@angular/core";
+/* tslint:disable:no-null-keyword trackBy-function */
+import { Component, Inject, OnDestroy } from "@angular/core";
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
-import { StarkDatePickerFilter, StarkDateRangePickerEvent, StarkTimestampMaskConfig } from "@nationalbankbelgium/stark-ui";
+import { AbstractControl, FormControl, FormGroup, ValidationErrors, Validators } from "@angular/forms";
+import { MatCheckboxChange } from "@angular/material/checkbox";
+import { Subscription } from "rxjs";
import { ReferenceLink } from "../../../shared/components";
+import { StarkDateRangePickerEvent } from "@nationalbankbelgium/stark-ui";
+import map from "lodash-es/map";
-const DAY_IN_MILLISECONDS = 86400000;
+const MONTH_IN_MILLI = 2592000000;
@Component({
selector: "demo-date-range-picker",
templateUrl: "./demo-date-range-picker-page.component.html"
})
-export class DemoDateRangePickerPageComponent {
- public startDate?: Date;
- public endDate?: Date;
+export class DemoDateRangePickerPageComponent implements OnDestroy {
+ public static noFebruaryValidator(control: AbstractControl): ValidationErrors | null {
+ const { value } = control;
+ return value instanceof Date && value.getMonth() === 1 ? { inFebruary: true } : null; // date counts months from 0
+ }
+
+ public today = new Date();
+ public inOneMonth = new Date(this.today.getTime() + MONTH_IN_MILLI);
- public minDate = new Date();
- public maxDate = new Date(Date.now() + 30 * DAY_IN_MILLISECONDS);
+ public dateRangeModel = { startDate: this.today, endDate: this.inOneMonth };
+ public modelDisabled = false;
- public customDateFilter: StarkDatePickerFilter = (date: Date): boolean => date.getDay() !== 0;
+ public dateRangeFormGroup = new FormGroup({
+ startDate: new FormControl(null, Validators.compose([DemoDateRangePickerPageComponent.noFebruaryValidator])),
+ endDate: new FormControl(null, Validators.compose([DemoDateRangePickerPageComponent.noFebruaryValidator]))
+ });
- public customDateMask: StarkTimestampMaskConfig = {
- format: "DD-MM-YYYY"
- };
+ public getErrorMessages: (control: AbstractControl) => string[] = () => [];
- public disabled = false;
- public isDisabledDateMask = false;
- public isDisabledCustomDateMask = false;
+ private _activateGetErrorMessages(): void {
+ this.getErrorMessages = (control: AbstractControl): string[] =>
+ map(
+ control.errors || {},
+ (_value: any, key: string): string => {
+ switch (key) {
+ case "required":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.REQUIRED";
+ case "matDatepickerMin":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.MIN_TODAY";
+ case "matDatepickerMax":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.MAX_MONTH";
+ case "matDatepickerFilter":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.WEEKDAY";
+ case "startBeforeEnd":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.START_BEFORE_END";
+ case "endAfterStart":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.END_AFTER_START";
+ case "inFebruary":
+ return "SHOWCASE.DEMO.DATE_RANGE_PICKER.ERROR_MESSAGES.IN_FEBRUARY";
+ default:
+ return "";
+ }
+ }
+ );
+ }
+ /**
+ * List of subscriptions to be unsubscribed when component is destroyed
+ */
+ private _subs: Subscription[] = [];
public referenceList: ReferenceLink[] = [
{
@@ -33,12 +71,33 @@ export class DemoDateRangePickerPageComponent {
}
];
- public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {}
+ public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {
+ this._subs.push(this.dateRangeFormGroup.valueChanges.subscribe((v: any) => this.logger.debug("formGroup:", v)));
+
+ // FIXME: For some reason validation is run on the internal formControls before the value is set.
+ // this results in a ExpressionChangedAfterItHasBeenCheckedError on the usage of getErrorMessages.
+ setTimeout(() => this._activateGetErrorMessages());
+ }
+
+ public onDateModelChange(): void {
+ this.logger.debug("ngModel", this.dateRangeModel);
+ }
+
+ public onDateRangeFormGroupDisableCheckboxChange(event: MatCheckboxChange): void {
+ if (event.checked) {
+ this.dateRangeFormGroup.disable();
+ } else {
+ this.dateRangeFormGroup.enable();
+ }
+ }
- public onDateChanged(event: StarkDateRangePickerEvent): void {
- this.logger.debug(event);
+ public onDateChange(dateRange: StarkDateRangePickerEvent): void {
+ this.logger.debug("onChange:", dateRange);
+ }
- this.startDate = event.startDate;
- this.endDate = event.endDate;
+ public ngOnDestroy(): void {
+ for (const subscription of this._subs) {
+ subscription.unsubscribe()
+ }
}
}
diff --git a/showcase/src/assets/examples/date-range-picker/custom-date-mask.html b/showcase/src/assets/examples/date-range-picker/custom-date-mask.html
index 26443be59a..9d1118a8ec 100644
--- a/showcase/src/assets/examples/date-range-picker/custom-date-mask.html
+++ b/showcase/src/assets/examples/date-range-picker/custom-date-mask.html
@@ -1,20 +1 @@
-
-
-
-
-isDisabled
+
diff --git a/showcase/src/assets/examples/date-range-picker/custom-date-mask.ts b/showcase/src/assets/examples/date-range-picker/custom-date-mask.ts
index f462aa9319..120f9866fa 100644
--- a/showcase/src/assets/examples/date-range-picker/custom-date-mask.ts
+++ b/showcase/src/assets/examples/date-range-picker/custom-date-mask.ts
@@ -1,34 +1,15 @@
-import { Component, Inject, OnInit } from "@angular/core";
+import { Component, Inject } from "@angular/core";
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
-import { StarkDatePickerFilter, StarkDateRangePickerEvent, StarkTimestampMaskConfig } from "@nationalbankbelgium/stark-ui";
-
-const DAY_IN_MILLISECONDS = 86400000;
+import { StarkDateRangePickerEvent } from "@nationalbankbelgium/stark-ui";
@Component({
selector: "demo-date-range-picker",
templateUrl: "./demo-date-range-picker.component.html"
})
-export class DemoDateRangePickerComponent implements OnInit {
- public startDate?: Date;
- public endDate?: Date;
-
- public minDate = new Date();
- public maxDate = new Date(Date.now() + 30 * DAY_IN_MILLISECONDS);
-
- public customDateFilter: StarkDatePickerFilter = (date: Date): boolean => date.getDay() !== 0;
-
- public customDateMask: StarkTimestampMaskConfig = {
- format: "DD-MM-YYYY"
- };
-
- public disabled = false;
-
+export class DemoDateRangePickerComponent {
public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {}
- public onDateChanged(event: StarkDateRangePickerEvent): void {
- this.logger.debug(event);
-
- this.startDate = event.startDate;
- this.endDate = event.endDate;
+ public onDateChange(dateRange: StarkDateRangePickerEvent): void {
+ this.logger.debug("onChange:", dateRange);
}
}
diff --git a/showcase/src/assets/examples/date-range-picker/date-mask.html b/showcase/src/assets/examples/date-range-picker/date-mask.html
index d648945bad..22ed205613 100644
--- a/showcase/src/assets/examples/date-range-picker/date-mask.html
+++ b/showcase/src/assets/examples/date-range-picker/date-mask.html
@@ -1,20 +1 @@
-
-
-
- isDisabled
-
+
diff --git a/showcase/src/assets/examples/date-range-picker/date-mask.ts b/showcase/src/assets/examples/date-range-picker/date-mask.ts
index e6924415f0..120f9866fa 100644
--- a/showcase/src/assets/examples/date-range-picker/date-mask.ts
+++ b/showcase/src/assets/examples/date-range-picker/date-mask.ts
@@ -1,30 +1,15 @@
-import { Component, Inject, OnInit } from "@angular/core";
+import { Component, Inject } from "@angular/core";
import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
-import { StarkDatePickerFilter, StarkDateRangePickerEvent, StarkTimestampMaskConfig } from "@nationalbankbelgium/stark-ui";
-
-const DAY_IN_MILLISECONDS = 86400000;
+import { StarkDateRangePickerEvent } from "@nationalbankbelgium/stark-ui";
@Component({
selector: "demo-date-range-picker",
templateUrl: "./demo-date-range-picker.component.html"
})
-export class DemoDateRangePickerComponent implements OnInit {
- public startDate?: Date;
- public endDate?: Date;
-
- public minDate = new Date();
- public maxDate = new Date(Date.now() + 30 * DAY_IN_MILLISECONDS);
-
- public customDateFilter: StarkDatePickerFilter = (date: Date): boolean => date.getDay() !== 0;
-
- public disabled = false;
-
+export class DemoDateRangePickerComponent {
public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {}
- public onDateChanged(event: StarkDateRangePickerEvent): void {
- this.logger.debug(event);
-
- this.startDate = event.startDate;
- this.endDate = event.endDate;
+ public onDateChange(dateRange: StarkDateRangePickerEvent): void {
+ this.logger.debug("onChange:", dateRange);
}
}
diff --git a/showcase/src/assets/examples/date-range-picker/form-group.html b/showcase/src/assets/examples/date-range-picker/form-group.html
new file mode 100644
index 0000000000..a176567650
--- /dev/null
+++ b/showcase/src/assets/examples/date-range-picker/form-group.html
@@ -0,0 +1,15 @@
+
+
+ {{ error }}
+
+
+
+
+ {{ error }}
+
+
+
+
+
+ {{ dateRangeFormGroup.disabled ? "Disabled" : "Enabled" }}
+
diff --git a/showcase/src/assets/examples/date-range-picker/form-group.ts b/showcase/src/assets/examples/date-range-picker/form-group.ts
new file mode 100644
index 0000000000..c174adf6d3
--- /dev/null
+++ b/showcase/src/assets/examples/date-range-picker/form-group.ts
@@ -0,0 +1,66 @@
+/* tslint:disable:no-null-keyword */
+import { Component, Inject, OnDestroy } from "@angular/core";
+import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
+import { AbstractControl, FormControl, FormGroup, ValidationErrors, Validators } from "@angular/forms";
+import { MatCheckboxChange } from "@angular/material/checkbox";
+import { Subscription } from "rxjs";
+import map from "lodash-es/map";
+
+@Component({
+ selector: "demo-date-range-picker",
+ templateUrl: "./demo-date-range-picker.component.html"
+})
+export class DemoDateRangePickerComponent implements OnDestroy {
+ public static noFebruaryValidator(control: AbstractControl): ValidationErrors | null {
+ const { value } = control;
+ return value instanceof Date && value.getMonth() === 1 ? { inFebruary: true } : null; // date counts months from 0
+ }
+
+ public dateRangeFormGroup = new FormGroup({
+ startDate: new FormControl(null, Validators.compose([DemoDateRangePickerComponent.noFebruaryValidator])),
+ endDate: new FormControl(null, Validators.compose([DemoDateRangePickerComponent.noFebruaryValidator]))
+ });
+
+ /**
+ * List of subscriptions to be unsubscribed when component is destroyed
+ */
+ private _subs: Subscription[] = [];
+
+ public getErrorMessages(control: AbstractControl): string[] {
+ return map(
+ control.errors || [],
+ (_value: any, key: string): string => {
+ switch (key) {
+ case "required":
+ return "Date is required";
+ case "startBeforeEnd":
+ return "Start date should be before end date";
+ case "endAfterStart":
+ return "End date should be after start date";
+ case "inFebruary":
+ return "Date should not be in February";
+ default:
+ return "";
+ }
+ }
+ );
+ }
+
+ public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {
+ this._subs.push(this.dateRangeFormGroup.valueChanges.subscribe((v: any) => this.logger.debug("formGroup:", v)));
+ }
+
+ public onDateRangeFormGroupDisableCheckboxChange(event: MatCheckboxChange): void {
+ if (event.checked) {
+ this.dateRangeFormGroup.disable();
+ } else {
+ this.dateRangeFormGroup.enable();
+ }
+ }
+
+ public ngOnDestroy(): void {
+ for (const subscription of this._subs) {
+ subscription.unsubscribe()
+ }
+ }
+}
diff --git a/showcase/src/assets/examples/date-range-picker/full.html b/showcase/src/assets/examples/date-range-picker/full.html
deleted file mode 100644
index 1bbb69e1eb..0000000000
--- a/showcase/src/assets/examples/date-range-picker/full.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-isDisabled
diff --git a/showcase/src/assets/examples/date-range-picker/full.ts b/showcase/src/assets/examples/date-range-picker/full.ts
deleted file mode 100644
index e279da8ba6..0000000000
--- a/showcase/src/assets/examples/date-range-picker/full.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Component, Inject } from "@angular/core";
-import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
-import { StarkDatePickerFilter, StarkDateRangePickerEvent } from "@nationalbankbelgium/stark-ui";
-
-const DAY_IN_MILLISECONDS = 86400000;
-
-@Component({
- selector: "demo-date-range-picker",
- templateUrl: "./demo-date-range-picker.component.html"
-})
-export class DemoDateRangePickerComponent {
- public startDate?: Date;
- public endDate?: Date;
-
- public minDate = new Date();
- public maxDate = new Date(Date.now() + 30 * DAY_IN_MILLISECONDS);
-
- public customDateFilter: StarkDatePickerFilter = (date: Date): boolean => date.getDay() !== 0;
-
- public disabled = false;
-
- public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {}
-
- public onDateChanged(event: StarkDateRangePickerEvent): void {
- this.logger.debug(event);
-
- this.startDate = event.startDate;
- this.endDate = event.endDate;
- }
-}
diff --git a/showcase/src/assets/examples/date-range-picker/model.html b/showcase/src/assets/examples/date-range-picker/model.html
new file mode 100644
index 0000000000..fb42c720bc
--- /dev/null
+++ b/showcase/src/assets/examples/date-range-picker/model.html
@@ -0,0 +1,23 @@
+
+
+ {{ error }}
+
+
+
+
+ {{ error }}
+
+
+
+
+{{ modelDisabled ? "Disabled" : "Enabled" }}
diff --git a/showcase/src/assets/examples/date-range-picker/model.ts b/showcase/src/assets/examples/date-range-picker/model.ts
new file mode 100644
index 0000000000..cf38f1804d
--- /dev/null
+++ b/showcase/src/assets/examples/date-range-picker/model.ts
@@ -0,0 +1,56 @@
+/* tslint:disable:no-null-keyword */
+import { Component, Inject, OnDestroy } from "@angular/core";
+import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
+import { AbstractControl } from "@angular/forms";
+import map from "lodash-es/map";
+
+const MONTH_IN_MILLI = 2592000000;
+
+@Component({
+ selector: "demo-date-range-picker",
+ templateUrl: "./demo-date-range-picker.component.html"
+})
+export class DemoDateRangePickerComponent implements OnDestroy {
+ public today = new Date();
+ public inOneMonth = new Date(this.today.getTime() + MONTH_IN_MILLI);
+
+ public dateRangeModel = { startDate: this.today, endDate: this.inOneMonth };
+ public modelDisabled = false;
+
+ public getErrorMessages: (control: AbstractControl) => string[] = () => [];
+
+ private _activateGetErrorMessages(): void {
+ this.getErrorMessages = (control: AbstractControl): string[] =>
+ map(
+ control.errors || {},
+ (_value: any, key: string): string => {
+ switch (key) {
+ case "required":
+ return "Date is required";
+ case "matDatepickerMin":
+ return "Date should be after today";
+ case "matDatepickerMax":
+ return "Date should be in less than 1 month";
+ case "matDatepickerFilter":
+ return "Date should be a weekday";
+ case "startBeforeEnd":
+ return "Start date should be before end date";
+ case "endAfterStart":
+ return "End date should be after start date";
+ default:
+ return "";
+ }
+ }
+ );
+ }
+
+ public constructor(@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService) {
+ // FIXME: For some reason validation is run on the internal formControls before the value is set.
+ // this results in a ExpressionChangedAfterItHasBeenCheckedError on the usage of getErrorMessages.
+ setTimeout(() => this._activateGetErrorMessages());
+ }
+
+ public onDateModelChange(): void {
+ this.logger.debug("ngModel", this.dateRangeModel);
+ }
+}
diff --git a/showcase/src/assets/translations/en.json b/showcase/src/assets/translations/en.json
index 6f40d4dcdf..27ad49b24a 100644
--- a/showcase/src/assets/translations/en.json
+++ b/showcase/src/assets/translations/en.json
@@ -52,9 +52,20 @@
"TITLE": "Date picker"
},
"DATE_RANGE_PICKER": {
- "CUSTOM_MASK": "Date range picker - Custom date mask ({{ mask }})",
- "DEFAULT_MASK": "Date range picker - Default date mask",
- "TITLE": "Date range picker"
+ "TITLE": "Date range picker",
+ "WITH_MODEL": "ngModel",
+ "WITH_FORM_GROUP": "Form group",
+ "WITH_MASK": "Default date mask",
+ "WITH_CUSTOM_MASK": "Custom date mask (DD-MM-YYYY)",
+ "ERROR_MESSAGES": {
+ "MIN_TODAY": "Date should be after today",
+ "MAX_MONTH": "Date should be in less than 1 month",
+ "WEEKDAY": "Date should be a weekday",
+ "START_BEFORE_END": "Start date should be before end date",
+ "END_AFTER_START": "End date should be after start date",
+ "IN_FEBRUARY": "Date should not be in February",
+ "REQUIRED": "Date is required"
+ }
},
"DIALOGS": {
"ALERT": {
diff --git a/showcase/src/assets/translations/fr.json b/showcase/src/assets/translations/fr.json
index dd526312d4..454466b63a 100644
--- a/showcase/src/assets/translations/fr.json
+++ b/showcase/src/assets/translations/fr.json
@@ -52,9 +52,20 @@
"TITLE": "Date picker"
},
"DATE_RANGE_PICKER": {
- "CUSTOM_MASK": "Date range picker - Masque de date personnalisé ({{ mask }})",
- "DEFAULT_MASK": "Date range picker - Masque de date par défaut",
- "TITLE": "Date range picker"
+ "TITLE": "Date range picker",
+ "WITH_MODEL": "ngModel",
+ "WITH_FORM_GROUP": "Form group",
+ "WITH_MASK": "Masque date par défaut",
+ "WITH_CUSTOM_MASK": "Masque date personnalisé (DD-MM-YYYY)",
+ "ERROR_MESSAGES": {
+ "MIN_TODAY": "La date devrait être postérieure à aujourd'hui",
+ "MAX_MONTH": "La date devrait être dans moins d'un mois",
+ "WEEKDAY": "La date doit être un jour de semaine",
+ "START_BEFORE_END": "La date de début doit être antérieure à la date de fin",
+ "END_AFTER_START": "La date de fin doit être postérieure à la date de début",
+ "IN_FEBRUARY": "La date ne devrait pas être en février",
+ "REQUIRED": "La date est requise"
+ }
},
"DIALOGS": {
"ALERT": {
diff --git a/showcase/src/assets/translations/nl.json b/showcase/src/assets/translations/nl.json
index 22495df35b..b249d4a60f 100644
--- a/showcase/src/assets/translations/nl.json
+++ b/showcase/src/assets/translations/nl.json
@@ -52,9 +52,20 @@
"TITLE": "Date picker"
},
"DATE_RANGE_PICKER": {
- "CUSTOM_MASK": "Date range picker - Aangepast datummasker ({{ mask }})",
- "DEFAULT_MASK": "Date range picker - Standaard datummasker",
- "TITLE": "Date range picker"
+ "TITLE": "Date range picker",
+ "WITH_MODEL": "ngModel",
+ "WITH_FORM_GROUP": "Form group",
+ "WITH_MASK": "Standaard datummasker",
+ "WITH_CUSTOM_MASK": "Aangepast datummasker (DD-MM-YYYY)",
+ "ERROR_MESSAGES": {
+ "MIN_TODAY": "Datum moet na vandaag zijn",
+ "MAX_MONTH": "Datum moet binnen minder dan 1 maand zijn",
+ "WEEKDAY": "Datum moet een weekdag zijn",
+ "START_BEFORE_END": "Startdatum moet voor einddatum zijn",
+ "END_AFTER_START": "Einddatum moet na startdatum zijn",
+ "IN_FEBRUARY": "Datum mag niet in februari zijn",
+ "REQUIRED": "Datum is verplicht"
+ }
},
"DIALOGS": {
"ALERT": {