[APPS-2108] migrate date widget to Angular date picker (#8975)

* migrate date widget to Angular date picker

* [ci:force] fix tests

* [ci:force] fix tests

* [ci:force] test fixes

* [ci:force] missing label attributes

* added type for value
This commit is contained in:
Denys Vuika
2023-10-09 10:28:13 +01:00
committed by GitHub
parent e42e0869ba
commit 3a374ad2a4
6 changed files with 72 additions and 84 deletions

View File

@@ -45,8 +45,6 @@ import { CoreTestingModule } from '../../testing';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { FormRenderingService } from '../services/form-rendering.service'; import { FormRenderingService } from '../services/form-rendering.service';
import { TextWidgetComponent } from './widgets'; import { TextWidgetComponent } from './widgets';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { FormRulesManager } from '../models/form-rules.model'; import { FormRulesManager } from '../models/form-rules.model';
const typeIntoInput = (targetInput: HTMLInputElement, message: string) => { const typeIntoInput = (targetInput: HTMLInputElement, message: string) => {
@@ -55,11 +53,6 @@ const typeIntoInput = (targetInput: HTMLInputElement, message: string) => {
targetInput.dispatchEvent(new Event('input')); targetInput.dispatchEvent(new Event('input'));
}; };
const typeIntoDate = (targetInput: DebugElement, date: { srcElement: { value: string } }) => {
expect(targetInput).toBeTruthy('Expected input to set to be valid and not null');
targetInput.triggerEventHandler('change', date);
};
const expectElementToBeHidden = (targetElement: HTMLElement): void => { const expectElementToBeHidden = (targetElement: HTMLElement): void => {
expect(targetElement).toBeTruthy(); expect(targetElement).toBeTruthy();
expect(targetElement.style.visibility).toBe('hidden', `${targetElement.id} should be hidden but it is not`); expect(targetElement.style.visibility).toBe('hidden', `${targetElement.id} should be hidden but it is not`);
@@ -113,11 +106,13 @@ describe('Form Renderer Component', () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const inputDateTestOne = fixture.debugElement.query(By.css('#Date0hwq20')); const inputDateTestOne = fixture.nativeElement.querySelector('#Date0hwq20') as HTMLInputElement;
let displayTextElementContainer: HTMLInputElement = fixture.nativeElement.querySelector('#field-Text0pqd1u-container'); let displayTextElementContainer: HTMLInputElement = fixture.nativeElement.querySelector('#field-Text0pqd1u-container');
expectElementToBeHidden(displayTextElementContainer); expectElementToBeHidden(displayTextElementContainer);
typeIntoDate(inputDateTestOne, { srcElement: { value: '2019-11-19' } }); inputDateTestOne.value = '2019-11-19';
inputDateTestOne.dispatchEvent(new Event('change'));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
@@ -130,11 +125,14 @@ describe('Form Renderer Component', () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const inputDateTestOne = fixture.debugElement.query(By.css('#Date0hwq20')); const inputDateTestOne = fixture.nativeElement.querySelector('#Date0hwq20') as HTMLInputElement;
let displayTextElementContainer: HTMLDivElement = fixture.nativeElement.querySelector('#field-Text0uyqd3-container'); let displayTextElementContainer: HTMLDivElement = fixture.nativeElement.querySelector('#field-Text0uyqd3-container');
expectElementToBeVisible(displayTextElementContainer); expectElementToBeVisible(displayTextElementContainer);
typeIntoDate(inputDateTestOne, { srcElement: { value: '2019-11-19' } }); inputDateTestOne.value = '2019-11-19';
inputDateTestOne.dispatchEvent(new Event('change'));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();

View File

@@ -1,26 +1,25 @@
<div class="{{field.className}}" id="data-widget" [class.adf-invalid]="!field.isValid && isTouched()"> <div class="{{field.className}}" id="data-widget" [class.adf-invalid]="!field.isValid && isTouched()">
<mat-form-field class="adf-date-widget" [hideRequiredMarker]="true"> <mat-form-field [floatLabel]="'always'" class="adf-date-widget">
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }} ({{field.dateDisplayFormat}})<span class="adf-asterisk" <mat-label class="adf-label"
*ngIf="isRequired()">*</span></label> [id]="field.id + '-label'"
<input matInput [attr.for]="field.id"
[id]="field.id" >{{field.name | translate }} ({{field.dateDisplayFormat}})</mat-label>
[value]="field.value" <input matInput [matDatepicker]="datePicker"
[required]="isRequired()" [id]="field.id"
[disabled]="field.readOnly" [(ngModel)]="value"
(change)="onDateChanged($any($event).srcElement.value)" [required]="field.required"
[placeholder]="field.placeholder" [placeholder]="field.placeholder"
(blur)="markAsTouched()"> [min]="minDate"
<mat-datepicker-toggle matSuffix [for]="datePicker" [disabled]="field.readOnly" ></mat-datepicker-toggle> [max]="maxDate"
[disabled]="field.readOnly"
(dateChange)="onDateChange($event)"
(blur)="markAsTouched()">
<mat-datepicker-toggle matSuffix [for]="datePicker" [disabled]="field.readOnly"></mat-datepicker-toggle>
<mat-datepicker #datePicker
[startAt]="startAt"
[disabled]="field.readOnly">
</mat-datepicker>
</mat-form-field> </mat-form-field>
<error-widget [error]="field.validationSummary"></error-widget> <error-widget [error]="field.validationSummary"></error-widget>
<error-widget *ngIf="isInvalidFieldRequired() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget> <error-widget *ngIf="isInvalidFieldRequired() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
<mat-datepicker #datePicker [touchUi]="true" [startAt]="field.value | adfMomentDate: field.dateDisplayFormat" [disabled]="field.readOnly"></mat-datepicker>
<input
type="hidden"
[matDatepicker]="datePicker"
[value]="field.value | adfMomentDate: field.dateDisplayFormat"
[min]="minDate"
[max]="maxDate"
[disabled]="field.readOnly"
(dateInput)="onDateChanged($any($event).targetElement.value)">
</div> </div>

View File

@@ -1,11 +0,0 @@
.adf {
&-date-widget {
.mat-form-field-suffix {
top: 26px;
}
.mat-form-field-label-wrapper {
top: 20px;
}
}
}

View File

@@ -25,7 +25,6 @@ import { TranslateModule } from '@ngx-translate/core';
import { FormFieldTypes } from '../core/form-field-types'; import { FormFieldTypes } from '../core/form-field-types';
describe('DateWidgetComponent', () => { describe('DateWidgetComponent', () => {
let widget: DateWidgetComponent; let widget: DateWidgetComponent;
let fixture: ComponentFixture<DateWidgetComponent>; let fixture: ComponentFixture<DateWidgetComponent>;
let element: HTMLElement; let element: HTMLElement;
@@ -101,7 +100,9 @@ describe('DateWidgetComponent', () => {
readOnly: 'false' readOnly: 'false'
}); });
widget.field = field; widget.field = field;
widget.onDateChanged({ value: moment('12/12/2012', widget.field.dateDisplayFormat) }); widget.onDateChange({
value: moment('12/12/2012', widget.field.dateDisplayFormat)
} as any);
expect(widget.onFieldChanged).toHaveBeenCalledWith(field); expect(widget.onFieldChanged).toHaveBeenCalledWith(field);
}); });
@@ -126,13 +127,6 @@ describe('DateWidgetComponent', () => {
expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeTruthy(); expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeTruthy();
}); });
it('should be able to display label with asterix', () => {
const asterisk: HTMLElement = element.querySelector('.adf-asterisk');
expect(asterisk).toBeTruthy();
expect(asterisk.textContent).toEqual('*');
});
}); });
describe('template check', () => { describe('template check', () => {
@@ -142,7 +136,7 @@ describe('DateWidgetComponent', () => {
TestBed.resetTestingModule(); TestBed.resetTestingModule();
}); });
it('should show visible date widget', () => { it('should show visible date widget', async () => {
widget.field = new FormFieldModel(new FormModel(), { widget.field = new FormFieldModel(new FormModel(), {
id: 'date-field-id', id: 'date-field-id',
name: 'date-name', name: 'date-name',
@@ -153,15 +147,15 @@ describe('DateWidgetComponent', () => {
widget.field.isVisible = true; widget.field.isVisible = true;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(element.querySelector('#date-field-id')).toBeDefined(); const dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
expect(element.querySelector('#date-field-id')).not.toBeNull(); expect(dateElement).not.toBeNull();
const dateElement: any = element.querySelector('#date-field-id'); expect(dateElement?.value).toContain('9-9-9999');
expect(dateElement.value).toContain('9-9-9999');
}); });
it('[C310335] - Should be able to change display format for Date widget', () => { it('[C310335] - Should be able to change display format for Date widget', async () => {
widget.field = new FormFieldModel(new FormModel(), { widget.field = new FormFieldModel(new FormModel(), {
id: 'date-field-id', id: 'date-field-id',
name: 'date-name', name: 'date-name',
@@ -173,25 +167,20 @@ describe('DateWidgetComponent', () => {
widget.field.dateDisplayFormat = 'MM-DD-YYYY'; widget.field.dateDisplayFormat = 'MM-DD-YYYY';
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
let dateElement: any = element.querySelector('#date-field-id'); let dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
expect(dateElement.value).toContain('12-30-9999'); expect(dateElement?.value).toContain('12-30-9999');
widget.field.value = '5-6-2019 00:00';
widget.field.dateDisplayFormat = 'D-M-YYYY HH:mm';
fixture.detectChanges();
dateElement = element.querySelector('#date-field-id');
expect(dateElement.value).toContain('5-6-2019 00:00');
widget.field.value = '05.06.2019'; widget.field.value = '05.06.2019';
widget.field.dateDisplayFormat = 'DD.MM.YYYY'; widget.field.dateDisplayFormat = 'DD.MM.YYYY';
fixture.componentInstance.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
dateElement = element.querySelector('#date-field-id'); dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
expect(dateElement.value).toContain('05.06.2019'); expect(dateElement?.value).toContain('05.06.2019');
}); });
it('should disable date button when is readonly', () => { it('should disable date button when is readonly', () => {
@@ -233,7 +222,7 @@ describe('DateWidgetComponent', () => {
}); });
}); });
it('should display always the json value', () => { it('should display always the json value', async () => {
const field = new FormFieldModel(new FormModel(), { const field = new FormFieldModel(new FormModel(), {
id: 'date-field-id', id: 'date-field-id',
name: 'date-name', name: 'date-name',
@@ -241,20 +230,23 @@ describe('DateWidgetComponent', () => {
type: 'date', type: 'date',
readOnly: 'false' readOnly: 'false'
}); });
field.isVisible = true; field.isVisible = true;
field.dateDisplayFormat = 'MM-DD-YYYY'; field.dateDisplayFormat = 'MM-DD-YYYY';
widget.field = field; widget.field = field;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(element.querySelector('#date-field-id')).toBeDefined(); const dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
expect(element.querySelector('#date-field-id')).not.toBeNull(); expect(dateElement?.value).toContain('12-30-9999');
const dateElement: any = element.querySelector('#date-field-id');
expect(dateElement.value).toContain('12-30-9999');
widget.field.value = '03-02-2020'; widget.field.value = '03-02-2020';
fixture.componentInstance.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
expect(dateElement.value).toContain('03-02-2020'); await fixture.whenStable();
expect(dateElement?.value).toContain('03-02-2020');
}); });
}); });

View File

@@ -20,13 +20,14 @@
import { UserPreferencesService, UserPreferenceValues } from '../../../../common/services/user-preferences.service'; import { UserPreferencesService, UserPreferenceValues } from '../../../../common/services/user-preferences.service';
import { MomentDateAdapter } from '../../../../common/utils/moment-date-adapter'; import { MomentDateAdapter } from '../../../../common/utils/moment-date-adapter';
import { MOMENT_DATE_FORMATS } from '../../../../common/utils/moment-date-formats.model'; import { MOMENT_DATE_FORMATS } from '../../../../common/utils/moment-date-formats.model';
import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core'; import { Component, OnInit, ViewEncapsulation, OnDestroy, Input } from '@angular/core';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import moment, { Moment } from 'moment'; import moment, { Moment } from 'moment';
import { FormService } from '../../../services/form.service'; import { FormService } from '../../../services/form.service';
import { WidgetComponent } from '../widget.component'; import { WidgetComponent } from '../widget.component';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
@Component({ @Component({
selector: 'date-widget', selector: 'date-widget',
@@ -34,7 +35,6 @@ import { takeUntil } from 'rxjs/operators';
{ provide: DateAdapter, useClass: MomentDateAdapter }, { provide: DateAdapter, useClass: MomentDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }], { provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }],
templateUrl: './date.widget.html', templateUrl: './date.widget.html',
styleUrls: ['./date.widget.scss'],
host: { host: {
'(click)': 'event($event)', '(click)': 'event($event)',
'(blur)': 'event($event)', '(blur)': 'event($event)',
@@ -54,6 +54,10 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe
minDate: Moment; minDate: Moment;
maxDate: Moment; maxDate: Moment;
startAt: Moment;
@Input()
value: any = null;
private onDestroy$ = new Subject<boolean>(); private onDestroy$ = new Subject<boolean>();
@@ -80,6 +84,9 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe
if (this.field.maxValue) { if (this.field.maxValue) {
this.maxDate = moment(this.field.maxValue, this.DATE_FORMAT); this.maxDate = moment(this.field.maxValue, this.DATE_FORMAT);
} }
this.startAt = moment(this.field.value, this.field.dateDisplayFormat);
this.value = moment(this.field.value, this.field.dateDisplayFormat);
} }
} }
@@ -88,12 +95,15 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe
this.onDestroy$.complete(); this.onDestroy$.complete();
} }
onDateChanged(newDateValue) { onDateChange(event: MatDatepickerInputEvent<Moment>) {
const date = moment(newDateValue, this.field.dateDisplayFormat, true); const value = event.value;
const input = event.targetElement as HTMLInputElement;
const date = moment(value, this.field.dateDisplayFormat, true);
if (date.isValid()) { if (date.isValid()) {
this.field.value = date.format(this.field.dateDisplayFormat); this.field.value = date.format(this.field.dateDisplayFormat);
} else { } else {
this.field.value = newDateValue; this.field.value = input.value;
} }
this.onFieldChanged(this.field); this.onFieldChanged(this.field);
} }

View File

@@ -267,7 +267,7 @@ describe('StartFormComponent', () => {
const formFields = component.form.getFormFields(); const formFields = component.form.getFormFields();
const labelField = formFields.find((field) => field.id === 'date'); const labelField = formFields.find((field) => field.id === 'date');
const dateWidget = fixture.debugElement.nativeElement.querySelector('date-widget'); const dateWidget = fixture.debugElement.nativeElement.querySelector('date-widget');
const dateLabelElement = fixture.debugElement.nativeElement.querySelector('#data-widget .mat-form-field-infix> .adf-label'); const dateLabelElement = fixture.debugElement.nativeElement.querySelector('#date-label');
expect(dateWidget).toBeTruthy(); expect(dateWidget).toBeTruthy();
expect(labelField.type).toBe('date'); expect(labelField.type).toBe('date');
@@ -302,7 +302,7 @@ describe('StartFormComponent', () => {
const inputElement = fixture.debugElement.nativeElement.querySelector('.adf-input'); const inputElement = fixture.debugElement.nativeElement.querySelector('.adf-input');
const inputLabelElement = fixture.debugElement.nativeElement.querySelector('.mat-form-field-infix > .adf-label'); const inputLabelElement = fixture.debugElement.nativeElement.querySelector('.mat-form-field-infix > .adf-label');
const dateElement = fixture.debugElement.nativeElement.querySelector('#billdate'); const dateElement = fixture.debugElement.nativeElement.querySelector('#billdate');
const dateLabelElement = fixture.debugElement.nativeElement.querySelector('#data-widget .mat-form-field-infix> .adf-label'); const dateLabelElement = fixture.debugElement.nativeElement.querySelector('#billdate-label');
const selectElement = fixture.debugElement.nativeElement.querySelector('#claimtype'); const selectElement = fixture.debugElement.nativeElement.querySelector('#claimtype');
const selectLabelElement = fixture.debugElement.nativeElement.querySelector('.adf-dropdown-widget > .adf-label'); const selectLabelElement = fixture.debugElement.nativeElement.querySelector('.adf-dropdown-widget > .adf-label');