From 3d19341c0debac25ee694d18a1084f68419b07df Mon Sep 17 00:00:00 2001 From: Vito Date: Fri, 9 Feb 2018 22:31:53 +0000 Subject: [PATCH] [ADF-1925] created datetime widget (#2929) * [ADF-2225] created datetime widget * [ADF-2225] start fixing max and min date validation * [ADF-2225] fixed validator for date time * [ADF-2225] start adding test * [ADF-2225] removed time wrong workaround and added test to the widget * [ADF-2225] removed fdescribe * [1925] renamed variable --- .../widgets/core/form-field-types.ts | 1 + .../widgets/core/form-field-validator.spec.ts | 192 ++++++++++++- .../widgets/core/form-field-validator.ts | 143 +++++++-- .../widgets/core/form-field.model.ts | 34 ++- .../widgets/date-time/date-time.widget.html | 21 ++ .../widgets/date-time/date-time.widget.scss | 17 ++ .../date-time/date-time.widget.spec.ts | 272 ++++++++++++++++++ .../widgets/date-time/date-time.widget.ts | 86 ++++++ .../widgets/date/date.widget.spec.ts | 18 +- lib/core/form/components/widgets/index.ts | 5 +- lib/core/form/form.module.ts | 5 +- .../form/services/form-rendering.service.ts | 6 +- lib/core/material.module.ts | 4 +- 13 files changed, 766 insertions(+), 38 deletions(-) create mode 100644 lib/core/form/components/widgets/date-time/date-time.widget.html create mode 100644 lib/core/form/components/widgets/date-time/date-time.widget.scss create mode 100644 lib/core/form/components/widgets/date-time/date-time.widget.spec.ts create mode 100644 lib/core/form/components/widgets/date-time/date-time.widget.ts diff --git a/lib/core/form/components/widgets/core/form-field-types.ts b/lib/core/form/components/widgets/core/form-field-types.ts index 2049d114fe..71e010bbb9 100644 --- a/lib/core/form/components/widgets/core/form-field-types.ts +++ b/lib/core/form/components/widgets/core/form-field-types.ts @@ -37,6 +37,7 @@ export class FormFieldTypes { static DATE: string = 'date'; static AMOUNT: string = 'amount'; static DOCUMENT: string = 'document'; + static DATETIME: string = 'datetime'; static READONLY_TYPES: string[] = [ FormFieldTypes.HYPERLINK, diff --git a/lib/core/form/components/widgets/core/form-field-validator.spec.ts b/lib/core/form/components/widgets/core/form-field-validator.spec.ts index cf517aa816..03a601054c 100644 --- a/lib/core/form/components/widgets/core/form-field-validator.spec.ts +++ b/lib/core/form/components/widgets/core/form-field-validator.spec.ts @@ -26,7 +26,9 @@ import { MinValueFieldValidator, NumberFieldValidator, RegExFieldValidator, - RequiredFieldValidator + RequiredFieldValidator, + MaxDateTimeFieldValidator, + MinDateTimeFieldValidator } from './form-field-validator'; import { FormFieldModel } from './form-field.model'; import { FormModel } from './form.model'; @@ -588,4 +590,192 @@ describe('FormFieldValidator', () => { }); }); + + describe('MaxDateTimeFieldValidator', () => { + + let validator: MaxDateTimeFieldValidator; + + beforeEach(() => { + validator = new MaxDateTimeFieldValidator(); + }); + + it('should require maxValue defined', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME + }); + expect(validator.isSupported(field)).toBeFalsy(); + + field.maxValue = '9999-02-08 10:10 AM'; + expect(validator.isSupported(field)).toBeTruthy(); + }); + + it('should support date time widgets only', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + maxValue: '9999-02-08 10:10 AM' + }); + + expect(validator.isSupported(field)).toBeTruthy(); + + field.type = FormFieldTypes.TEXT; + expect(validator.isSupported(field)).toBeFalsy(); + }); + + it('should allow empty values', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: null, + maxValue: '9999-02-08 10:10 AM' + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should succeed for unsupported types', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.TEXT + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should succeed validating value checking the time', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '08-02-9999 09:10 AM', + maxValue: '9999-02-08 10:10 AM' + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should fail validating value checking the time', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '08-02-9999 11:10 AM', + maxValue: '9999-02-08 10:10 AM' + }); + + field.validationSummary = new ErrorMessageModel(); + expect(validator.validate(field)).toBeFalsy(); + expect(field.validationSummary).not.toBeNull(); + }); + + it('should succeed validating value checking the date', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '08-02-9999 09:10 AM', + maxValue: '9999-02-08 10:10 AM' + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should fail validating value checking the date', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '08-02-9999 12:10 AM', + maxValue: '9999-02-07 10:10 AM' + }); + + field.validationSummary = new ErrorMessageModel(); + expect(validator.validate(field)).toBeFalsy(); + expect(field.validationSummary).not.toBeNull(); + }); + + }); + + describe('MinDateTimeFieldValidator', () => { + + let validator: MinDateTimeFieldValidator; + + beforeEach(() => { + validator = new MinDateTimeFieldValidator(); + }); + + it('should require minValue defined', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME + }); + expect(validator.isSupported(field)).toBeFalsy(); + + field.minValue = '9999-02-08 09:10 AM'; + expect(validator.isSupported(field)).toBeTruthy(); + }); + + it('should support date time widgets only', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + minValue: '9999-02-08 09:10 AM' + }); + + expect(validator.isSupported(field)).toBeTruthy(); + + field.type = FormFieldTypes.TEXT; + expect(validator.isSupported(field)).toBeFalsy(); + }); + + it('should allow empty values', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: null, + minValue: '9999-02-08 09:10 AM' + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should succeed for unsupported types', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.TEXT + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should succeed validating value by time', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '08-02-9999 09:10 AM', + minValue: '9999-02-08 09:00 AM' + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should succeed validating value by date', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '09-02-9999 09:10 AM', + minValue: '9999-02-08 09:10 AM' + }); + + expect(validator.validate(field)).toBeTruthy(); + }); + + it('should fail validating value by time', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '08-02-9999 09:00 AM', + minValue: '9999-02-08 09:10 AM' + }); + + field.validationSummary = new ErrorMessageModel(); + expect(validator.validate(field)).toBeFalsy(); + expect(field.validationSummary).not.toBeNull(); + }); + + it('should fail validating value by date', () => { + let field = new FormFieldModel(new FormModel(), { + type: FormFieldTypes.DATETIME, + value: '07-02-9999 09:10 AM', + minValue: '9999-02-08 09:10 AM' + }); + + field.validationSummary = new ErrorMessageModel(); + expect(validator.validate(field)).toBeFalsy(); + expect(field.validationSummary).not.toBeNull(); + }); + + }); }); diff --git a/lib/core/form/components/widgets/core/form-field-validator.ts b/lib/core/form/components/widgets/core/form-field-validator.ts index b9a68dee3f..93a251143f 100644 --- a/lib/core/form/components/widgets/core/form-field-validator.ts +++ b/lib/core/form/components/widgets/core/form-field-validator.ts @@ -42,7 +42,8 @@ export class RequiredFieldValidator implements FormFieldValidator { FormFieldTypes.UPLOAD, FormFieldTypes.AMOUNT, FormFieldTypes.DYNAMIC_TABLE, - FormFieldTypes.DATE + FormFieldTypes.DATE, + FormFieldTypes.DATETIME ]; isSupported(field: FormFieldModel): boolean { @@ -159,8 +160,6 @@ export class DateFieldValidator implements FormFieldValidator { export class MinDateFieldValidator implements FormFieldValidator { - MIN_DATE_FORMAT = 'DD-MM-YYYY'; - private supportedTypes = [ FormFieldTypes.DATE ]; @@ -171,30 +170,38 @@ export class MinDateFieldValidator implements FormFieldValidator { } validate(field: FormFieldModel): boolean { + let isValid = true; if (this.isSupported(field) && field.value) { const dateFormat = field.dateDisplayFormat; if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; - return false; - } - - // remove time and timezone info - let d; - if (typeof field.value === 'string') { - d = moment(field.value.split('T')[0], dateFormat); + isValid = false; } else { - d = field.value; - } - let min = moment(field.minValue, this.MIN_DATE_FORMAT); - - if (d.isBefore(min)) { - field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; - field.validationSummary.attributes.set('minValue', field.minValue.toLocaleString()); - return false; + isValid = this.checkDate(field, dateFormat); } } - return true; + return isValid; + } + + private checkDate(field: FormFieldModel, dateFormat: string): boolean { + const MIN_DATE_FORMAT = 'DD-MM-YYYY'; + let isValid = true; + // remove time and timezone info + let fieldValueData; + if (typeof field.value === 'string') { + fieldValueData = moment(field.value.split('T')[0], dateFormat); + } else { + fieldValueData = field.value; + } + let min = moment(field.minValue, MIN_DATE_FORMAT); + + if (fieldValueData.isBefore(min)) { + field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; + field.validationSummary.attributes.set('minValue', field.minValue.toLocaleString()); + isValid = false; + } + return isValid; } } @@ -239,6 +246,100 @@ export class MaxDateFieldValidator implements FormFieldValidator { } } +export class MinDateTimeFieldValidator implements FormFieldValidator { + + private supportedTypes = [ + FormFieldTypes.DATETIME + ]; + + isSupported(field: FormFieldModel): boolean { + return field && + this.supportedTypes.indexOf(field.type) > -1 && !!field.minValue; + } + + validate(field: FormFieldModel): boolean { + let isValid = true; + if (this.isSupported(field) && field.value) { + const dateFormat = field.dateDisplayFormat; + + if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { + field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; + isValid = false; + } else { + isValid = this.checkDateTime(field, dateFormat); + } + } + return isValid; + } + + private checkDateTime(field: FormFieldModel, dateFormat: string): boolean { + const MIN_DATE_FORMAT = 'YYYY-MM-DD hh:mm A'; + + let isValid = true; + let fieldValueDate; + if (typeof field.value === 'string') { + fieldValueDate = moment(field.value, dateFormat); + } else { + fieldValueDate = field.value; + } + let min = moment(field.minValue, MIN_DATE_FORMAT); + + if (fieldValueDate.isBefore(min)) { + field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; + field.validationSummary.attributes.set('minValue', min.format('D-M-YYYY hh-mm A')); + isValid = false; + } + return isValid; + } +} + +export class MaxDateTimeFieldValidator implements FormFieldValidator { + + private supportedTypes = [ + FormFieldTypes.DATETIME + ]; + + isSupported(field: FormFieldModel): boolean { + return field && + this.supportedTypes.indexOf(field.type) > -1 && !!field.maxValue; + } + + validate(field: FormFieldModel): boolean { + let isValid = true; + if (this.isSupported(field) && field.value) { + const dateFormat = field.dateDisplayFormat; + + if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { + field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; + isValid = false; + } else { + isValid = this.checkDateTime(field, dateFormat); + } + } + return isValid; + } + + private checkDateTime(field: FormFieldModel, dateFormat: string): boolean { + const MAX_DATE_FORMAT = 'YYYY-MM-DD hh:mm A'; + let isValid = true; + let fieldValueDate; + + if (typeof field.value === 'string') { + fieldValueDate = moment(field.value, dateFormat); + } else { + fieldValueDate = field.value; + } + let max = moment(field.maxValue, MAX_DATE_FORMAT); + + if (fieldValueDate.isAfter(max)) { + field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_GREATER_THAN`; + field.validationSummary.attributes.set('maxValue', max.format('D-M-YYYY hh-mm A')); + isValid = false; + } + return isValid; + } +} + export class MinLengthFieldValidator implements FormFieldValidator { private supportedTypes = [ @@ -428,5 +529,7 @@ export const FORM_FIELD_VALIDATORS = [ new DateFieldValidator(), new MinDateFieldValidator(), new MaxDateFieldValidator(), - new FixedValueFieldValidator() + new FixedValueFieldValidator(), + new MinDateTimeFieldValidator(), + new MaxDateTimeFieldValidator() ]; diff --git a/lib/core/form/components/widgets/core/form-field.model.ts b/lib/core/form/components/widgets/core/form-field.model.ts index 166a0fda8e..5e13e96842 100644 --- a/lib/core/form/components/widgets/core/form-field.model.ts +++ b/lib/core/form/components/widgets/core/form-field.model.ts @@ -36,6 +36,7 @@ export class FormFieldModel extends FormWidgetModel { private _required: boolean = false; readonly defaultDateFormat: string = 'D-M-YYYY'; + readonly deafultDateTimeFormat: string = 'D-M-YYYY hh:mm A'; // model members fieldType: string; @@ -164,7 +165,7 @@ export class FormFieldModel extends FormWidgetModel { this.visibilityCondition = json.visibilityCondition; this.enableFractions = json.enableFractions; this.currency = json.currency; - this.dateDisplayFormat = json.dateDisplayFormat || this.defaultDateFormat; + this.dateDisplayFormat = json.dateDisplayFormat || this.getDefaultDateFormat(json); this._value = this.parseValue(json); this.validationSummary = new ErrorMessageModel(); @@ -200,6 +201,16 @@ export class FormFieldModel extends FormWidgetModel { this.updateForm(); } + private getDefaultDateFormat(jsonField: any): string { + let originalType = jsonField.type; + if (FormFieldTypes.isReadOnlyType(jsonField.type) && + jsonField.params && + jsonField.params.field) { + originalType = jsonField.params.field.type; + } + return originalType === FormFieldTypes.DATETIME ? this.deafultDateTimeFormat : this.defaultDateFormat; + } + private isTypeaHeadFieldType(type: string): boolean { return type === 'typeahead' ? true : false; } @@ -311,13 +322,13 @@ export class FormFieldModel extends FormWidgetModel { This is needed due to Activiti displaying/editing dates in d-M-YYYY format but storing on server in ISO8601 format (i.e. 2013-02-04T22:44:30.652Z) */ - if (this.isDateField(json)) { + if (this.isDateField(json) || this.isDateTimeField(json)) { if (value) { let dateValue; if (NumberFieldValidator.isNumber(value)) { dateValue = moment(value); } else { - dateValue = moment(value.split('T')[0], 'YYYY-M-D'); + dateValue = this.isDateTimeField(json) ? moment(value, 'YYYY-MM-DD hh:mm A') : moment(value.split('T')[0], 'YYYY-M-D'); } if (dateValue && dateValue.isValid()) { value = dateValue.format(this.dateDisplayFormat); @@ -382,6 +393,15 @@ export class FormFieldModel extends FormWidgetModel { this._value = this.value; } break; + case FormFieldTypes.DATETIME: + const dateTimeValue = moment(this.value, this.dateDisplayFormat, true); + if (dateTimeValue && dateTimeValue.isValid()) { + this.form.values[this.id] = dateTimeValue.format('YYYY-MM-DDTHH:mm:ssZ'); + } else { + this.form.values[this.id] = null; + this._value = this.value; + } + break; case FormFieldTypes.NUMBER: this.form.values[this.id] = parseInt(this.value, 10); break; @@ -424,4 +444,12 @@ export class FormFieldModel extends FormWidgetModel { json.params.field.type === FormFieldTypes.DATE ) || json.type === FormFieldTypes.DATE; } + + private isDateTimeField(json: any): boolean { + return (json.params && + json.params.field && + json.params.field.type === FormFieldTypes.DATETIME) || + json.type === FormFieldTypes.DATETIME; + } + } diff --git a/lib/core/form/components/widgets/date-time/date-time.widget.html b/lib/core/form/components/widgets/date-time/date-time.widget.html new file mode 100644 index 0000000000..b0fa611d39 --- /dev/null +++ b/lib/core/form/components/widgets/date-time/date-time.widget.html @@ -0,0 +1,21 @@ +
+ + + + + + + + +
diff --git a/lib/core/form/components/widgets/date-time/date-time.widget.scss b/lib/core/form/components/widgets/date-time/date-time.widget.scss new file mode 100644 index 0000000000..cc5b941b96 --- /dev/null +++ b/lib/core/form/components/widgets/date-time/date-time.widget.scss @@ -0,0 +1,17 @@ +@import '../form'; + +.adf { + + &-date-time-widget { + + .mat-input-suffix { + text-align: right; + position: absolute; + margin-top: 30px; + width: 100%; + } + + } + +} + diff --git a/lib/core/form/components/widgets/date-time/date-time.widget.spec.ts b/lib/core/form/components/widgets/date-time/date-time.widget.spec.ts new file mode 100644 index 0000000000..b68995f8b6 --- /dev/null +++ b/lib/core/form/components/widgets/date-time/date-time.widget.spec.ts @@ -0,0 +1,272 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import moment from 'moment-es6'; +import { ActivitiContentService } from '../../../services/activiti-alfresco.service'; +import { MaterialModule } from '../../../../material.module'; +import { ErrorWidgetComponent } from '../error/error.component'; +import { EcmModelService } from './../../../services/ecm-model.service'; +import { FormService } from './../../../services/form.service'; +import { FormFieldModel } from './../core/form-field.model'; +import { FormModel } from './../core/form.model'; +import { DateTimeWidgetComponent } from './date-time.widget'; +import { UserPreferencesService } from '../../../../services/user-preferences.service'; + +describe('DateTimeWidgetComponent', () => { + + let widget: DateTimeWidgetComponent; + let fixture: ComponentFixture; + let element: HTMLElement; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + MaterialModule + ], + declarations: [ + DateTimeWidgetComponent, + ErrorWidgetComponent + ], + providers: [ + FormService, + UserPreferencesService, + EcmModelService, + ActivitiContentService + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DateTimeWidgetComponent); + + element = fixture.nativeElement; + widget = fixture.componentInstance; + }); + + afterEach(() => { + fixture.destroy(); + TestBed.resetTestingModule(); + }); + + it('should setup min value for date picker', () => { + let minValue = '1982-03-13T10:00Z'; + widget.field = new FormFieldModel(null, { + id: 'date-id', + name: 'date-name', + type: 'datetime', + minValue: minValue + }); + + fixture.detectChanges(); + + let expected = moment(minValue, 'YYYY-MM-DDTHH:mm:ssZ'); + expect(widget.minDate.isSame(expected)).toBeTruthy(); + }); + + it('should date field be present', () => { + widget.field = new FormFieldModel(null, { + id: 'date-id', + name: 'date-name', + type: 'datetime' + }); + fixture.detectChanges(); + + expect(element.querySelector('#data-time-widget')).toBeDefined(); + expect(element.querySelector('#data-time-widget')).not.toBeNull(); + }); + + it('should setup max value for date picker', () => { + let maxValue = '1982-03-13T10:00Z'; + widget.field = new FormFieldModel(null, { + maxValue: maxValue + }); + fixture.detectChanges(); + + let expected = moment(maxValue, 'YYYY-MM-DDTHH:mm:ssZ'); + expect(widget.maxDate.isSame(expected)).toBeTruthy(); + }); + + it('should eval visibility on date changed', () => { + spyOn(widget, 'checkVisibility').and.callThrough(); + + let field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '9-12-9999 10:00 AM', + type: 'datetime', + readOnly: 'false' + }); + + widget.field = field; + + widget.onDateChanged({ value: moment('13-03-1982 10:00 AM') }); + expect(widget.checkVisibility).toHaveBeenCalledWith(field); + }); + + describe('template check', () => { + + it('should show visible date widget', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '30-11-9999 10:30 AM', + type: 'datetime', + readOnly: 'false' + }); + fixture.detectChanges(); + fixture.whenStable() + .then(() => { + expect(element.querySelector('#date-field-id')).toBeDefined(); + expect(element.querySelector('#date-field-id')).not.toBeNull(); + let dateElement: any = element.querySelector('#date-field-id'); + expect(dateElement.value).toBe('30-11-9999 10:30 AM'); + }); + })); + + it('should check correctly the min value with different formats', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '11-29-9999 10:30 AM', + dateDisplayFormat: 'MM-DD-YYYY HH:mm A', + type: 'datetime', + readOnly: 'false', + minValue: '9999-11-30T10:30Z' + }); + fixture.detectChanges(); + widget.field.validate(); + fixture.detectChanges(); + fixture.whenStable() + .then(() => { + expect(element.querySelector('#date-field-id')).toBeDefined(); + expect(element.querySelector('#date-field-id')).not.toBeNull(); + let dateElement: any = element.querySelector('#date-field-id'); + expect(dateElement.value).toContain('11-29-9999 10:30 AM'); + expect(element.querySelector('.adf-error-text').textContent).toBe('FORM.FIELD.VALIDATOR.NOT_LESS_THAN'); + }); + })); + + it('should show the correct format type', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '12-30-9999 10:30 AM', + dateDisplayFormat: 'MM-DD-YYYY HH:mm A', + type: 'datetime', + readOnly: 'false' + }); + fixture.detectChanges(); + fixture.whenStable() + .then(() => { + expect(element.querySelector('#date-field-id')).toBeDefined(); + expect(element.querySelector('#date-field-id')).not.toBeNull(); + let dateElement: any = element.querySelector('#date-field-id'); + expect(dateElement.value).toContain('12-30-9999 10:30 AM'); + }); + })); + + it('should hide not visible date widget', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '12-30-9999 10:30 AM', + dateDisplayFormat: 'MM-DD-YYYY HH:mm A', + type: 'datetime', + readOnly: 'false' + }); + fixture.detectChanges(); + expect(element.querySelector('#data-time-widget')).not.toBeNull(); + widget.field.isVisible = false; + fixture.detectChanges(); + fixture.whenStable() + .then(() => { + fixture.detectChanges(); + expect(element.querySelector('#data-time-widget')).toBeNull(); + }); + })); + + it('should become visibile if the visibility change to true', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '12-30-9999 10:30 AM', + dateDisplayFormat: 'MM-DD-YYYY HH:mm A', + type: 'datetime', + readOnly: 'false' + }); + widget.field.isVisible = false; + fixture.detectChanges(); + expect(element.querySelector('#data-time-widget')).toBeNull(); + widget.fieldChanged.subscribe((field) => { + field.isVisible = true; + fixture.detectChanges(); + fixture.whenStable() + .then(() => { + expect(element.querySelector('#data-time-widget')).toBeDefined(); + expect(element.querySelector('#data-time-widget')).not.toBeNull(); + let dateElement: any = element.querySelector('#date-field-id'); + expect(dateElement.value).toContain('12-30-9999 10:30 AM'); + }); + }); + widget.checkVisibility(widget.field); + })); + + it('should be hided if the visibility change to false', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '12-30-9999 10:30 AM', + dateDisplayFormat: 'MM-DD-YYYY HH:mm A', + type: 'datetime', + readOnly: 'false' + }); + fixture.detectChanges(); + expect(element.querySelector('#data-time-widget')).not.toBeNull(); + widget.fieldChanged.subscribe((field) => { + field.isVisible = false; + fixture.detectChanges(); + fixture.whenStable() + .then(() => { + expect(element.querySelector('#data-time-widget')).toBeNull(); + }); + }); + widget.checkVisibility(widget.field); + })); + + it('should disable date button when is readonly', async(() => { + widget.field = new FormFieldModel(new FormModel(), { + id: 'date-field-id', + name: 'date-name', + value: '12-30-9999 10:30 AM', + dateDisplayFormat: 'MM-DD-YYYY HH:mm A', + type: 'datetime', + readOnly: 'false' + }); + fixture.detectChanges(); + + let dateButton = element.querySelector('button'); + expect(dateButton.disabled).toBeFalsy(); + + widget.field.readOnly = true; + fixture.detectChanges(); + + dateButton = element.querySelector('button'); + expect(dateButton.disabled).toBeTruthy(); + })); + }); +}); diff --git a/lib/core/form/components/widgets/date-time/date-time.widget.ts b/lib/core/form/components/widgets/date-time/date-time.widget.ts new file mode 100644 index 0000000000..1c867fe396 --- /dev/null +++ b/lib/core/form/components/widgets/date-time/date-time.widget.ts @@ -0,0 +1,86 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* tslint:disable:component-selector */ + +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material'; +import { DatetimeAdapter, MAT_DATETIME_FORMATS } from '@mat-datetimepicker/core'; +import { MomentDatetimeAdapter, MAT_MOMENT_DATETIME_FORMATS } from '@mat-datetimepicker/moment'; +import moment from 'moment-es6'; +import { Moment } from 'moment'; +import { UserPreferencesService } from '../../../../services/user-preferences.service'; +import { MomentDateAdapter } from '../../../../utils/momentDateAdapter'; +import { MOMENT_DATE_FORMATS } from '../../../../utils/moment-date-formats.model'; +import { FormService } from './../../../services/form.service'; +import { WidgetComponent } from './../widget.component'; + +@Component({ + providers: [ + { provide: DateAdapter, useClass: MomentDateAdapter }, + { provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }, + { provide: DatetimeAdapter, useClass: MomentDatetimeAdapter }, + { provide: MAT_DATETIME_FORMATS, useValue: MAT_MOMENT_DATETIME_FORMATS } + ], + selector: 'date-time-widget', + templateUrl: './date-time.widget.html', + styleUrls: ['./date-time.widget.scss'], + encapsulation: ViewEncapsulation.None +}) +export class DateTimeWidgetComponent extends WidgetComponent implements OnInit { + + minDate: Moment; + maxDate: Moment; + displayDate: Moment; + + constructor(public formService: FormService, + private dateAdapter: DateAdapter, + private preferences: UserPreferencesService) { + super(formService); + } + + ngOnInit() { + this.preferences.locale$.subscribe((locale) => { + this.dateAdapter.setLocale(locale); + }); + let momentDateAdapter = this.dateAdapter; + momentDateAdapter.overrideDisplyaFormat = this.field.dateDisplayFormat; + + if (this.field) { + if (this.field.minValue) { + this.minDate = moment(this.field.minValue, 'YYYY-MM-DDTHH:mm:ssZ'); + } + + if (this.field.maxValue) { + this.maxDate = moment(this.field.maxValue, 'YYYY-MM-DDTHH:mm:ssZ'); + } + } + this.displayDate = moment(this.field.value, this.field.dateDisplayFormat); + } + + onDateChanged(newDateValue) { + if (newDateValue && newDateValue.value) { + this.field.value = newDateValue.value.format(this.field.dateDisplayFormat); + } else if (newDateValue) { + this.field.value = newDateValue; + } else { + this.field.value = null; + } + this.checkVisibility(this.field); + } + +} diff --git a/lib/core/form/components/widgets/date/date.widget.spec.ts b/lib/core/form/components/widgets/date/date.widget.spec.ts index 9e7a9d8679..c37d3e461a 100644 --- a/lib/core/form/components/widgets/date/date.widget.spec.ts +++ b/lib/core/form/components/widgets/date/date.widget.spec.ts @@ -76,9 +76,10 @@ describe('DateWidgetComponent', () => { minValue: minValue }); - widget.ngOnInit(); + fixture.detectChanges(); - expect(element.querySelector('#dropdown-id')).toBeDefined(); + expect(element.querySelector('#data-widget')).toBeDefined(); + expect(element.querySelector('#data-widget')).not.toBeNull(); }); it('should setup max value for date picker', () => { @@ -129,13 +130,12 @@ describe('DateWidgetComponent', () => { }); it('should show visible date widget', async(() => { - fixture.whenStable() - .then(() => { - expect(element.querySelector('#date-field-id')).toBeDefined(); - expect(element.querySelector('#date-field-id')).not.toBeNull(); - let dateElement: any = element.querySelector('#date-field-id'); - expect(dateElement.value).toContain('9-9-9999'); - }); + fixture.whenStable().then(() => { + expect(element.querySelector('#date-field-id')).toBeDefined(); + expect(element.querySelector('#date-field-id')).not.toBeNull(); + let dateElement: any = element.querySelector('#date-field-id'); + expect(dateElement.value).toContain('9-9-9999'); + }); })); it('should check correctly the min value with different formats', async(() => { diff --git a/lib/core/form/components/widgets/index.ts b/lib/core/form/components/widgets/index.ts index 3fa1231187..5bbdd97eba 100644 --- a/lib/core/form/components/widgets/index.ts +++ b/lib/core/form/components/widgets/index.ts @@ -42,6 +42,7 @@ import { InputMaskDirective } from './text/text-mask.component'; import { TextWidgetComponent } from './text/text.widget'; import { TypeaheadWidgetComponent } from './typeahead/typeahead.widget'; import { UploadWidgetComponent } from './upload/upload.widget'; +import { DateTimeWidgetComponent } from './date-time/date-time.widget'; // core export * from './widget.component'; @@ -70,6 +71,7 @@ export * from './amount/amount.widget'; export * from './dynamic-table/dynamic-table.widget'; export * from './error/error.component'; export { DocumentWidgetComponent } from './document/document.widget'; +export * from './date-time/date-time.widget'; // editors (dynamic table) export * from './dynamic-table/dynamic-table.widget.model'; @@ -105,7 +107,8 @@ export const WIDGET_DIRECTIVES: any[] = [ TextEditorComponent, RowEditorComponent, ErrorWidgetComponent, - DocumentWidgetComponent + DocumentWidgetComponent, + DateTimeWidgetComponent ]; export const MASK_DIRECTIVE: any[] = [ diff --git a/lib/core/form/form.module.ts b/lib/core/form/form.module.ts index 137aec44dd..ad2d84e42a 100644 --- a/lib/core/form/form.module.ts +++ b/lib/core/form/form.module.ts @@ -44,6 +44,7 @@ import { FormService } from './services/form.service'; import { NodeService } from './services/node.service'; import { ProcessContentService } from './services/process-content.service'; import { WidgetVisibilityService } from './services/widget-visibility.service'; +import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core'; @NgModule({ imports: [ @@ -55,7 +56,9 @@ import { WidgetVisibilityService } from './services/widget-visibility.service'; FormsModule, ReactiveFormsModule, DataColumnModule, - PipeModule + PipeModule, + MatDatetimepickerModule, + MatNativeDatetimeModule ], declarations: [ ContentWidgetComponent, diff --git a/lib/core/form/services/form-rendering.service.ts b/lib/core/form/services/form-rendering.service.ts index 6ad1082e19..dd6ef74216 100644 --- a/lib/core/form/services/form-rendering.service.ts +++ b/lib/core/form/services/form-rendering.service.ts @@ -36,7 +36,8 @@ import { TextWidgetComponent, TypeaheadWidgetComponent, UnknownWidgetComponent, - UploadWidgetComponent + UploadWidgetComponent, + DateTimeWidgetComponent } from './../components/widgets/index'; @Injectable() @@ -62,7 +63,8 @@ export class FormRenderingService extends DynamicComponentMapper { 'container': DynamicComponentResolver.fromType(ContainerWidgetComponent), 'group': DynamicComponentResolver.fromType(ContainerWidgetComponent), 'document': DynamicComponentResolver.fromType(DocumentWidgetComponent), - 'upload': DynamicComponentResolver.fromType(UploadWidgetComponent) + 'upload': DynamicComponentResolver.fromType(UploadWidgetComponent), + 'datetime': DynamicComponentResolver.fromType(DateTimeWidgetComponent) }; constructor() { diff --git a/lib/core/material.module.ts b/lib/core/material.module.ts index e3400e5c4c..b0c6978acb 100644 --- a/lib/core/material.module.ts +++ b/lib/core/material.module.ts @@ -16,6 +16,8 @@ */ import { NgModule } from '@angular/core'; +import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core'; + import { MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatGridListModule, MatIconModule, @@ -32,7 +34,7 @@ export function modules() { MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule, - MatTooltipModule + MatTooltipModule, MatDatetimepickerModule, MatNativeDatetimeModule ]; }