From 27502f5595b15e02a02c80404d046b1a71ee9beb Mon Sep 17 00:00:00 2001
From: mdelez <60604010+mdelez@users.noreply.github.com>
Date: Mon, 11 Jan 2021 14:31:22 +0100
Subject: [PATCH] fix(Date Value Validators): Propagate valueRequiredValidator
to child component (DSP-1188) (#251)
* refactor (date-value-comp): propagate valueRequiredValidator to child component
* tests (date value & input): added more tests
* fix (date-input): fixes error message logic
* test(date-input): adds additional form validity check
---
.../date-input/date-input.component.html | 8 +-
.../date-input/date-input.component.spec.ts | 79 ++++++++++++++++++-
.../date-input/date-input.component.ts | 34 +++++---
.../date-value/date-value.component.html | 2 +-
.../date-value/date-value.component.spec.ts | 46 +++++++++++
.../int-value/int-value.component.spec.ts | 4 +-
6 files changed, 153 insertions(+), 20 deletions(-)
diff --git a/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.html b/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.html
index 29f24c58f..57f2c59ac 100644
--- a/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.html
+++ b/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.html
@@ -38,12 +38,12 @@
Start date is required
-
- In a period, start and end date must use the same
+
+ In a period, start and end dates are required and must use the same
calendar
-
- In a period, start must be before end
+
+ In a period, start must be before end
diff --git a/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.spec.ts b/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.spec.ts
index 262b280f8..5e6c215a4 100644
--- a/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.spec.ts
+++ b/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.spec.ts
@@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DateInputComponent } from './date-input.component';
import { Component, OnInit, ViewChild } from '@angular/core';
-import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { KnoraDate, KnoraPeriod } from '@dasch-swiss/dsp-js';
import { JDNDatepickerDirective } from '../../jdn-datepicker-directive/jdndatepicker.directive';
import { MatFormFieldModule } from '@angular/material/form-field';
@@ -46,6 +46,35 @@ class TestHostComponent implements OnInit {
}
}
+/**
+ * Test host component to simulate parent component.
+ */
+@Component({
+ template: `
+
+
+
+
+
`
+})
+class NoValueRequiredTestHostComponent implements OnInit {
+
+ @ViewChild('dateInput') dateInputComponent: DateInputComponent;
+
+ form: FormGroup;
+
+ constructor(private _fb: FormBuilder) {
+ }
+
+ ngOnInit(): void {
+
+ this.form = this._fb.group({
+ date: new FormControl(null)
+ });
+
+ }
+}
+
describe('DateInputComponent', () => {
let testHostComponent: TestHostComponent;
let testHostFixture: ComponentFixture;
@@ -261,4 +290,52 @@ describe('DateInputComponent', () => {
});
+ it('should mark the form\'s validity correctly', () => {
+ expect(testHostComponent.dateInputComponent.valueRequiredValidator).toBe(true);
+ expect(testHostComponent.dateInputComponent.form.valid).toBe(true);
+
+ testHostComponent.dateInputComponent.startDateControl.setValue(null);
+
+ testHostComponent.dateInputComponent._handleInput();
+
+ expect(testHostComponent.dateInputComponent.form.valid).toBe(false);
+ });
+
+});
+
+describe('NoValueRequiredTestHostComponent', () => {
+ let testHostComponent: NoValueRequiredTestHostComponent;
+ let testHostFixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ReactiveFormsModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatDatepickerModule,
+ MatCheckboxModule,
+ MatJDNConvertibleCalendarDateAdapterModule,
+ BrowserAnimationsModule
+ ],
+ declarations: [DateInputComponent, NoValueRequiredTestHostComponent, JDNDatepickerDirective]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ testHostFixture = TestBed.createComponent(NoValueRequiredTestHostComponent);
+ testHostComponent = testHostFixture.componentInstance;
+ testHostFixture.detectChanges();
+
+ expect(testHostComponent).toBeTruthy();
+ });
+
+ it('should recieve the propagated valueRequiredValidator from the parent component', () => {
+ expect(testHostComponent.dateInputComponent.valueRequiredValidator).toBe(false);
+ });
+
+ it('should mark the form\'s validity correctly', () => {
+ expect(testHostComponent.dateInputComponent.form.valid).toBe(true);
+ });
});
diff --git a/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.ts b/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.ts
index 16c6c1f5a..b07f43ed5 100644
--- a/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.ts
+++ b/projects/dsp-ui/src/lib/viewer/values/date-value/date-input/date-input.component.ts
@@ -1,6 +1,6 @@
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
-import { Component, DoCheck, ElementRef, HostBinding, Input, OnDestroy, Optional, Self } from '@angular/core';
+import { Component, DoCheck, ElementRef, HostBinding, Input, OnDestroy, OnInit, Optional, Self } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
@@ -93,7 +93,7 @@ const _MatInputMixinBase: CanUpdateErrorStateCtor & typeof MatInputBase =
styleUrls: ['./date-input.component.scss'],
providers: [{ provide: MatFormFieldControl, useExisting: DateInputComponent }]
})
-export class DateInputComponent extends _MatInputMixinBase implements ControlValueAccessor, MatFormFieldControl, DoCheck, CanUpdateErrorState, OnDestroy {
+export class DateInputComponent extends _MatInputMixinBase implements ControlValueAccessor, MatFormFieldControl, DoCheck, CanUpdateErrorState, OnDestroy, OnInit {
static nextId = 0;
@@ -110,6 +110,8 @@ export class DateInputComponent extends _MatInputMixinBase implements ControlVal
endDateControl: FormControl;
isPeriodControl: FormControl;
+ @Input() valueRequiredValidator = true;
+
onChange = (_: any) => {
};
onTouched = () => {
@@ -267,15 +269,7 @@ export class DateInputComponent extends _MatInputMixinBase implements ControlVal
this.endDateControl = new FormControl(null);
this.isPeriodControl = new FormControl(null);
- this.startDateControl
- = new FormControl(
- null,
- [
- Validators.required,
- sameCalendarValidator(this.isPeriodControl, this.endDateControl),
- periodStartEndValidator(this.isPeriodControl, this.endDateControl)
- ]
- );
+ this.startDateControl = new FormControl(null);
this.form = fb.group({
dateStart: this.startDateControl,
@@ -283,7 +277,6 @@ export class DateInputComponent extends _MatInputMixinBase implements ControlVal
isPeriod: this.isPeriodControl
});
-
_fm.monitor(_elRef.nativeElement, true).subscribe(origin => {
this.focused = !!origin;
this.stateChanges.next();
@@ -294,6 +287,23 @@ export class DateInputComponent extends _MatInputMixinBase implements ControlVal
}
}
+ ngOnInit() {
+ if (this.valueRequiredValidator) {
+ this.startDateControl.setValidators([
+ Validators.required,
+ sameCalendarValidator(this.isPeriodControl, this.endDateControl),
+ periodStartEndValidator(this.isPeriodControl, this.endDateControl)
+ ]);
+ } else {
+ this.startDateControl.setValidators([
+ sameCalendarValidator(this.isPeriodControl, this.endDateControl),
+ periodStartEndValidator(this.isPeriodControl, this.endDateControl)
+ ]);
+ }
+
+ this.startDateControl.updateValueAndValidity();
+ }
+
ngDoCheck() {
if (this.ngControl) {
this.updateErrorState();
diff --git a/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.html b/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.html
index 6ac9f8e35..dcd96c734 100644
--- a/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.html
+++ b/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.html
@@ -34,7 +34,7 @@
-
+
New value must be different than the current value.
diff --git a/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.spec.ts b/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.spec.ts
index 642b4881f..adad2580c 100644
--- a/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.spec.ts
+++ b/projects/dsp-ui/src/lib/viewer/values/date-value/date-value.component.spec.ts
@@ -36,6 +36,7 @@ class TestDateInputComponent implements ControlValueAccessor, MatFormFieldContro
@Input() required: boolean;
@Input() shouldLabelFloat: boolean;
@Input() errorStateMatcher: ErrorStateMatcher;
+ @Input() valueRequiredValidator = true;
errorState = false;
focused = false;
@@ -116,6 +117,26 @@ class TestHostCreateValueComponent implements OnInit {
}
}
+/**
+ * Test host component to simulate parent component.
+ */
+@Component({
+ template: `
+ `
+ })
+ class TestHostCreateValueNoValueRequiredComponent implements OnInit {
+
+ @ViewChild('inputVal') inputValueComponent: DateValueComponent;
+
+ mode: 'read' | 'update' | 'create' | 'search';
+
+ ngOnInit() {
+
+ this.mode = 'create';
+
+ }
+ }
+
describe('DateValueComponent', () => {
beforeEach(async(() => {
@@ -130,6 +151,7 @@ describe('DateValueComponent', () => {
TestDateInputComponent,
TestHostDisplayValueComponent,
TestHostCreateValueComponent,
+ TestHostCreateValueNoValueRequiredComponent,
KnoraDatePipe
]
})
@@ -611,5 +633,29 @@ describe('DateValueComponent', () => {
});
+ describe('create a date value no required value', () => {
+
+ let testHostComponent: TestHostCreateValueNoValueRequiredComponent;
+ let testHostFixture: ComponentFixture;
+
+ beforeEach(() => {
+ testHostFixture = TestBed.createComponent(TestHostCreateValueNoValueRequiredComponent);
+ testHostComponent = testHostFixture.componentInstance;
+ testHostFixture.detectChanges();
+
+ expect(testHostComponent).toBeTruthy();
+ expect(testHostComponent.inputValueComponent).toBeTruthy();
+ });
+
+ it('should not create an empty value', () => {
+ expect(testHostComponent.inputValueComponent.getNewValue()).toBe(false);
+ expect(testHostComponent.inputValueComponent.form.valid).toBe(true);
+ });
+
+ it('should propagate valueRequiredValidator to child component', () => {
+ expect(testHostComponent.inputValueComponent.valueRequiredValidator).toBe(false);
+ });
+
+ });
});
diff --git a/projects/dsp-ui/src/lib/viewer/values/int-value/int-value.component.spec.ts b/projects/dsp-ui/src/lib/viewer/values/int-value/int-value.component.spec.ts
index bd9813062..b4e6a9376 100644
--- a/projects/dsp-ui/src/lib/viewer/values/int-value/int-value.component.spec.ts
+++ b/projects/dsp-ui/src/lib/viewer/values/int-value/int-value.component.spec.ts
@@ -335,8 +335,8 @@ describe('IntValueComponent', () => {
});
describe('create value no required value', () => {
- let testHostComponent: TestHostCreateValueComponent;
- let testHostFixture: ComponentFixture;
+ let testHostComponent: TestHostCreateValueNoValueRequiredComponent;
+ let testHostFixture: ComponentFixture;
beforeEach(async () => {
testHostFixture = TestBed.createComponent(TestHostCreateValueNoValueRequiredComponent);