[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
This commit is contained in:
Vito 2018-02-09 22:31:53 +00:00 committed by Eugenio Romano
parent f2dd70a455
commit 3d19341c0d
13 changed files with 766 additions and 38 deletions

View File

@ -37,6 +37,7 @@ export class FormFieldTypes {
static DATE: string = 'date'; static DATE: string = 'date';
static AMOUNT: string = 'amount'; static AMOUNT: string = 'amount';
static DOCUMENT: string = 'document'; static DOCUMENT: string = 'document';
static DATETIME: string = 'datetime';
static READONLY_TYPES: string[] = [ static READONLY_TYPES: string[] = [
FormFieldTypes.HYPERLINK, FormFieldTypes.HYPERLINK,

View File

@ -26,7 +26,9 @@ import {
MinValueFieldValidator, MinValueFieldValidator,
NumberFieldValidator, NumberFieldValidator,
RegExFieldValidator, RegExFieldValidator,
RequiredFieldValidator RequiredFieldValidator,
MaxDateTimeFieldValidator,
MinDateTimeFieldValidator
} from './form-field-validator'; } from './form-field-validator';
import { FormFieldModel } from './form-field.model'; import { FormFieldModel } from './form-field.model';
import { FormModel } from './form.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();
});
});
}); });

View File

@ -42,7 +42,8 @@ export class RequiredFieldValidator implements FormFieldValidator {
FormFieldTypes.UPLOAD, FormFieldTypes.UPLOAD,
FormFieldTypes.AMOUNT, FormFieldTypes.AMOUNT,
FormFieldTypes.DYNAMIC_TABLE, FormFieldTypes.DYNAMIC_TABLE,
FormFieldTypes.DATE FormFieldTypes.DATE,
FormFieldTypes.DATETIME
]; ];
isSupported(field: FormFieldModel): boolean { isSupported(field: FormFieldModel): boolean {
@ -159,8 +160,6 @@ export class DateFieldValidator implements FormFieldValidator {
export class MinDateFieldValidator implements FormFieldValidator { export class MinDateFieldValidator implements FormFieldValidator {
MIN_DATE_FORMAT = 'DD-MM-YYYY';
private supportedTypes = [ private supportedTypes = [
FormFieldTypes.DATE FormFieldTypes.DATE
]; ];
@ -171,30 +170,38 @@ export class MinDateFieldValidator implements FormFieldValidator {
} }
validate(field: FormFieldModel): boolean { validate(field: FormFieldModel): boolean {
let isValid = true;
if (this.isSupported(field) && field.value) { if (this.isSupported(field) && field.value) {
const dateFormat = field.dateDisplayFormat; const dateFormat = field.dateDisplayFormat;
if (!DateFieldValidator.isValidDate(field.value, dateFormat)) { if (!DateFieldValidator.isValidDate(field.value, dateFormat)) {
field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE';
return false; isValid = false;
}
// remove time and timezone info
let d;
if (typeof field.value === 'string') {
d = moment(field.value.split('T')[0], dateFormat);
} else { } else {
d = field.value; isValid = this.checkDate(field, dateFormat);
}
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;
} }
} }
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 { export class MinLengthFieldValidator implements FormFieldValidator {
private supportedTypes = [ private supportedTypes = [
@ -428,5 +529,7 @@ export const FORM_FIELD_VALIDATORS = [
new DateFieldValidator(), new DateFieldValidator(),
new MinDateFieldValidator(), new MinDateFieldValidator(),
new MaxDateFieldValidator(), new MaxDateFieldValidator(),
new FixedValueFieldValidator() new FixedValueFieldValidator(),
new MinDateTimeFieldValidator(),
new MaxDateTimeFieldValidator()
]; ];

View File

@ -36,6 +36,7 @@ export class FormFieldModel extends FormWidgetModel {
private _required: boolean = false; private _required: boolean = false;
readonly defaultDateFormat: string = 'D-M-YYYY'; readonly defaultDateFormat: string = 'D-M-YYYY';
readonly deafultDateTimeFormat: string = 'D-M-YYYY hh:mm A';
// model members // model members
fieldType: string; fieldType: string;
@ -164,7 +165,7 @@ export class FormFieldModel extends FormWidgetModel {
this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition; this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition;
this.enableFractions = <boolean> json.enableFractions; this.enableFractions = <boolean> json.enableFractions;
this.currency = json.currency; this.currency = json.currency;
this.dateDisplayFormat = json.dateDisplayFormat || this.defaultDateFormat; this.dateDisplayFormat = json.dateDisplayFormat || this.getDefaultDateFormat(json);
this._value = this.parseValue(json); this._value = this.parseValue(json);
this.validationSummary = new ErrorMessageModel(); this.validationSummary = new ErrorMessageModel();
@ -200,6 +201,16 @@ export class FormFieldModel extends FormWidgetModel {
this.updateForm(); 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 { private isTypeaHeadFieldType(type: string): boolean {
return type === 'typeahead' ? true : false; 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 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) 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) { if (value) {
let dateValue; let dateValue;
if (NumberFieldValidator.isNumber(value)) { if (NumberFieldValidator.isNumber(value)) {
dateValue = moment(value); dateValue = moment(value);
} else { } 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()) { if (dateValue && dateValue.isValid()) {
value = dateValue.format(this.dateDisplayFormat); value = dateValue.format(this.dateDisplayFormat);
@ -382,6 +393,15 @@ export class FormFieldModel extends FormWidgetModel {
this._value = this.value; this._value = this.value;
} }
break; 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: case FormFieldTypes.NUMBER:
this.form.values[this.id] = parseInt(this.value, 10); this.form.values[this.id] = parseInt(this.value, 10);
break; break;
@ -424,4 +444,12 @@ export class FormFieldModel extends FormWidgetModel {
json.params.field.type === FormFieldTypes.DATE ) || json.params.field.type === FormFieldTypes.DATE ) ||
json.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;
}
} }

View File

@ -0,0 +1,21 @@
<div class="{{field.className}}" *ngIf="field?.isVisible"
id="data-time-widget" [class.adf-invalid]="!field.isValid || field.validationSummary.message">
<mat-form-field class="adf-date-time-widget">
<label class="adf-label" [attr.for]="field.id">{{field.name}} ({{field.dateDisplayFormat}})<span *ngIf="isRequired()">*</span></label>
<input matInput
[matDatetimepicker]="datetimePicker"
[id]="field.id"
[(ngModel)]="displayDate"
[required]="isRequired()"
[disabled]="field.readOnly"
[min]="minDate"
[max]="maxDate"
(focusout)="onDateChanged($event.srcElement.value)"
(dateChange)="onDateChanged($event)"
placeholder="{{field.placeholder}}">
<mat-datetimepicker-toggle matSuffix [for]="datetimePicker" [disabled]="field.readOnly"></mat-datetimepicker-toggle>
</mat-form-field>
<error-widget [error]="field.validationSummary"></error-widget>
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
<mat-datetimepicker #datetimePicker type="datetime" openOnFocus="true" timeInterval="5"></mat-datetimepicker>
</div>

View File

@ -0,0 +1,17 @@
@import '../form';
.adf {
&-date-time-widget {
.mat-input-suffix {
text-align: right;
position: absolute;
margin-top: 30px;
width: 100%;
}
}
}

View File

@ -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<DateTimeWidgetComponent>;
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 = <HTMLButtonElement> element.querySelector('button');
expect(dateButton.disabled).toBeFalsy();
widget.field.readOnly = true;
fixture.detectChanges();
dateButton = <HTMLButtonElement> element.querySelector('button');
expect(dateButton.disabled).toBeTruthy();
}));
});
});

View File

@ -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<Moment>,
private preferences: UserPreferencesService) {
super(formService);
}
ngOnInit() {
this.preferences.locale$.subscribe((locale) => {
this.dateAdapter.setLocale(locale);
});
let momentDateAdapter = <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);
}
}

View File

@ -76,9 +76,10 @@ describe('DateWidgetComponent', () => {
minValue: minValue 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', () => { it('should setup max value for date picker', () => {
@ -129,13 +130,12 @@ describe('DateWidgetComponent', () => {
}); });
it('should show visible date widget', async(() => { it('should show visible date widget', async(() => {
fixture.whenStable() fixture.whenStable().then(() => {
.then(() => { expect(element.querySelector('#date-field-id')).toBeDefined();
expect(element.querySelector('#date-field-id')).toBeDefined(); expect(element.querySelector('#date-field-id')).not.toBeNull();
expect(element.querySelector('#date-field-id')).not.toBeNull(); let dateElement: any = element.querySelector('#date-field-id');
let dateElement: any = element.querySelector('#date-field-id'); expect(dateElement.value).toContain('9-9-9999');
expect(dateElement.value).toContain('9-9-9999'); });
});
})); }));
it('should check correctly the min value with different formats', async(() => { it('should check correctly the min value with different formats', async(() => {

View File

@ -42,6 +42,7 @@ import { InputMaskDirective } from './text/text-mask.component';
import { TextWidgetComponent } from './text/text.widget'; import { TextWidgetComponent } from './text/text.widget';
import { TypeaheadWidgetComponent } from './typeahead/typeahead.widget'; import { TypeaheadWidgetComponent } from './typeahead/typeahead.widget';
import { UploadWidgetComponent } from './upload/upload.widget'; import { UploadWidgetComponent } from './upload/upload.widget';
import { DateTimeWidgetComponent } from './date-time/date-time.widget';
// core // core
export * from './widget.component'; export * from './widget.component';
@ -70,6 +71,7 @@ export * from './amount/amount.widget';
export * from './dynamic-table/dynamic-table.widget'; export * from './dynamic-table/dynamic-table.widget';
export * from './error/error.component'; export * from './error/error.component';
export { DocumentWidgetComponent } from './document/document.widget'; export { DocumentWidgetComponent } from './document/document.widget';
export * from './date-time/date-time.widget';
// editors (dynamic table) // editors (dynamic table)
export * from './dynamic-table/dynamic-table.widget.model'; export * from './dynamic-table/dynamic-table.widget.model';
@ -105,7 +107,8 @@ export const WIDGET_DIRECTIVES: any[] = [
TextEditorComponent, TextEditorComponent,
RowEditorComponent, RowEditorComponent,
ErrorWidgetComponent, ErrorWidgetComponent,
DocumentWidgetComponent DocumentWidgetComponent,
DateTimeWidgetComponent
]; ];
export const MASK_DIRECTIVE: any[] = [ export const MASK_DIRECTIVE: any[] = [

View File

@ -44,6 +44,7 @@ import { FormService } from './services/form.service';
import { NodeService } from './services/node.service'; import { NodeService } from './services/node.service';
import { ProcessContentService } from './services/process-content.service'; import { ProcessContentService } from './services/process-content.service';
import { WidgetVisibilityService } from './services/widget-visibility.service'; import { WidgetVisibilityService } from './services/widget-visibility.service';
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
@NgModule({ @NgModule({
imports: [ imports: [
@ -55,7 +56,9 @@ import { WidgetVisibilityService } from './services/widget-visibility.service';
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
DataColumnModule, DataColumnModule,
PipeModule PipeModule,
MatDatetimepickerModule,
MatNativeDatetimeModule
], ],
declarations: [ declarations: [
ContentWidgetComponent, ContentWidgetComponent,

View File

@ -36,7 +36,8 @@ import {
TextWidgetComponent, TextWidgetComponent,
TypeaheadWidgetComponent, TypeaheadWidgetComponent,
UnknownWidgetComponent, UnknownWidgetComponent,
UploadWidgetComponent UploadWidgetComponent,
DateTimeWidgetComponent
} from './../components/widgets/index'; } from './../components/widgets/index';
@Injectable() @Injectable()
@ -62,7 +63,8 @@ export class FormRenderingService extends DynamicComponentMapper {
'container': DynamicComponentResolver.fromType(ContainerWidgetComponent), 'container': DynamicComponentResolver.fromType(ContainerWidgetComponent),
'group': DynamicComponentResolver.fromType(ContainerWidgetComponent), 'group': DynamicComponentResolver.fromType(ContainerWidgetComponent),
'document': DynamicComponentResolver.fromType(DocumentWidgetComponent), 'document': DynamicComponentResolver.fromType(DocumentWidgetComponent),
'upload': DynamicComponentResolver.fromType(UploadWidgetComponent) 'upload': DynamicComponentResolver.fromType(UploadWidgetComponent),
'datetime': DynamicComponentResolver.fromType(DateTimeWidgetComponent)
}; };
constructor() { constructor() {

View File

@ -16,6 +16,8 @@
*/ */
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
import { import {
MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule, MatAutocompleteModule, MatButtonModule, MatCardModule, MatCheckboxModule,
MatChipsModule, MatDatepickerModule, MatDialogModule, MatGridListModule, MatIconModule, MatChipsModule, MatDatepickerModule, MatDialogModule, MatGridListModule, MatIconModule,
@ -32,7 +34,7 @@ export function modules() {
MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule, MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule,
MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule,
MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule, MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule,
MatTooltipModule MatTooltipModule, MatDatetimepickerModule, MatNativeDatetimeModule
]; ];
} }