mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[APPS-2108] date-fns adapter for datetime pickers, many datetime parsing and validation fixes (#8992)
* migrate cloud date widget to date-fns, fix test bugs * [ci:force] update docs * [ci:force] remove commented out code * [APPS-2232] date cell validator, unit tests * improved moment adapter, code cleanup * datetime adapter, many code fixes * code review fixes * code cleanup * cleanup * fix max datetime validation, update tests * remove e2e already covered by unit tests * fix search date range * remove fake demo shell e2e for search * remove fake demo shell e2e for search page * cleanup e2e * migrate dynamic table to date-fns * fix e2e formatting * migrate protractor to unit tests * cleanup e2e
This commit is contained in:
@@ -79,7 +79,6 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
.select(UserPreferenceValues.Locale)
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(locale => {
|
||||
this.dateAdapter.setLocale(locale);
|
||||
this.property.locale = locale;
|
||||
});
|
||||
|
||||
|
@@ -46,6 +46,23 @@ import { Locale } from 'date-fns';
|
||||
adapter.displayFormat = '<custom date-fns format>';
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Material date formats for Date-fns
|
||||
*/
|
||||
export const ADF_DATE_FORMATS: MatDateFormats = {
|
||||
parse: {
|
||||
dateInput: 'dd-MM-yyyy'
|
||||
},
|
||||
display: {
|
||||
dateInput: 'dd-MM-yyyy',
|
||||
monthLabel: 'LLL',
|
||||
monthYearLabel: 'LLL uuuu',
|
||||
dateA11yLabel: 'PP',
|
||||
monthYearA11yLabel: 'LLLL uuuu'
|
||||
}
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AdfDateFnsAdapter extends DateFnsAdapter {
|
||||
private _displayFormat?: string = null;
|
||||
|
@@ -25,9 +25,14 @@ describe('DateFnsUtils', () => {
|
||||
expect(dateFnsFormat).toBe('yyyy-MM-dd');
|
||||
});
|
||||
|
||||
it('should convert moment datetime format', () => {
|
||||
it('should convert moment datetime format with zone', () => {
|
||||
const dateFnsFormat = DateFnsUtils.convertMomentToDateFnsFormat('YYYY-MM-DDTHH:mm:ssZ');
|
||||
expect(dateFnsFormat).toBe(`yyyy-MM-dd'T'HH:mm:ss'Z'`);
|
||||
expect(dateFnsFormat).toBe(`yyyy-MM-dd'T'HH:mm:ssXXX`);
|
||||
});
|
||||
|
||||
it('should convert moment datetime format with zone hours and mins', () => {
|
||||
const dateFnsFormat = DateFnsUtils.convertMomentToDateFnsFormat('YYYY-MM-DDTHH:mm:ssZZ');
|
||||
expect(dateFnsFormat).toBe(`yyyy-MM-dd'T'HH:mm:ssXX`);
|
||||
});
|
||||
|
||||
it('should convert custom moment datetime format', () => {
|
||||
@@ -81,20 +86,29 @@ describe('DateFnsUtils', () => {
|
||||
expect(result).toEqual(expectedParsedDate);
|
||||
});
|
||||
|
||||
it('should format ISO datetime from date', () => {
|
||||
const result = DateFnsUtils.formatDate(
|
||||
new Date('2023-10-10T18:28:50.082Z'),
|
||||
`yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`
|
||||
it('should parse alternative ISO datetime', () => {
|
||||
const result = DateFnsUtils.parseDate(
|
||||
'1982-03-13T10:00:000Z',
|
||||
`yyyy-MM-dd'T'HH:mm:sssXXX`
|
||||
);
|
||||
expect(result).toBe('2023-10-10T18:28:50.082Z');
|
||||
|
||||
expect(result.toISOString()).toBe('1982-03-13T10:00:00.000Z');
|
||||
});
|
||||
|
||||
it('should format ISO datetime from string', () => {
|
||||
const result = DateFnsUtils.formatDate(
|
||||
'2023-10-10T18:28:50.082Z',
|
||||
`yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`
|
||||
it('should parse the datetime with zone', () => {
|
||||
const result = DateFnsUtils.parseDate(
|
||||
'1982-03-13T10:00:000+01:00',
|
||||
`yyyy-MM-dd'T'HH:mm:sssXXX`
|
||||
);
|
||||
expect(result).toBe('2023-10-10T18:28:50.082Z');
|
||||
expect(result.toISOString()).toBe('1982-03-13T09:00:00.000Z');
|
||||
});
|
||||
|
||||
it('should parse datetime with zone in moment format', () => {
|
||||
const result = DateFnsUtils.parseDate(
|
||||
'1982-03-13T10:00:00+0100',
|
||||
`YYYY-MM-DDTHH:mm:ssZZ`
|
||||
);
|
||||
expect(result.toISOString()).toBe('1982-03-13T09:00:00.000Z');
|
||||
});
|
||||
|
||||
it('should validate datetime with moment format', () => {
|
||||
|
@@ -87,7 +87,8 @@ export class DateFnsUtils {
|
||||
A: 'a',
|
||||
ll: 'PP',
|
||||
T: `'T'`,
|
||||
Z: `'Z'`
|
||||
ZZ: 'XX',
|
||||
Z: `XXX`
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,9 +100,7 @@ export class DateFnsUtils {
|
||||
static convertMomentToDateFnsFormat(dateDisplayFormat: string): string {
|
||||
if (dateDisplayFormat && dateDisplayFormat.trim() !== '') {
|
||||
// normalise the input to support double conversion of the same string
|
||||
dateDisplayFormat = dateDisplayFormat
|
||||
.replace(`'T'`, 'T')
|
||||
.replace(`'Z'`, 'Z');
|
||||
dateDisplayFormat = dateDisplayFormat.replace(`'T'`, 'T');
|
||||
|
||||
for (const [search, replace] of Object.entries(this.momentToDateFnsMap)) {
|
||||
dateDisplayFormat = dateDisplayFormat.replace(new RegExp(search, 'g'), replace);
|
||||
@@ -194,4 +193,12 @@ export class DateFnsUtils {
|
||||
static isAfterDate(source: Date, target: Date): boolean {
|
||||
return isAfter(source, target);
|
||||
}
|
||||
|
||||
static utcToLocal(date: Date): Date {
|
||||
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
|
||||
}
|
||||
|
||||
static localToUtc(date: Date): Date {
|
||||
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
|
||||
}
|
||||
}
|
||||
|
141
lib/core/src/lib/common/utils/datetime-fns-adapter.ts
Normal file
141
lib/core/src/lib/common/utils/datetime-fns-adapter.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { Inject, Injectable, Optional } from '@angular/core';
|
||||
import { DateFnsUtils } from './date-fns-utils';
|
||||
import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimeFormats } from '@mat-datetimepicker/core';
|
||||
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
import { Locale, addHours, addMinutes } from 'date-fns';
|
||||
|
||||
/**
|
||||
* Material date/time formats for Date-fns (mat-datetimepicker)
|
||||
*/
|
||||
export const ADF_DATETIME_FORMATS: MatDatetimeFormats = {
|
||||
parse: {
|
||||
dateInput: 'P', // L
|
||||
monthInput: 'LLLL', // MMMM
|
||||
timeInput: 'p', // LT
|
||||
datetimeInput: 'Pp' // L LT
|
||||
},
|
||||
display: {
|
||||
dateInput: 'P', // L
|
||||
monthInput: 'LLLL', // MMMM
|
||||
datetimeInput: 'Pp', // L LT
|
||||
timeInput: 'p', // LT
|
||||
monthYearLabel: 'LLL uuuu', // MMM YYYY
|
||||
dateA11yLabel: 'PP', // LL
|
||||
monthYearA11yLabel: 'LLLL uuuu', // MMMM YYYY
|
||||
popupHeaderDateLabel: 'ccc, dd MMM' // ddd, DD MMM
|
||||
}
|
||||
};
|
||||
|
||||
/** The default hour names to use if Intl API is not available. */
|
||||
const DEFAULT_HOUR_NAMES = range(24, (i) => String(i));
|
||||
|
||||
/** The default minute names to use if Intl API is not available. */
|
||||
const DEFAULT_MINUTE_NAMES = range(60, (i) => String(i));
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-jsdoc
|
||||
function range<T>(length: number, valueFunction: (index: number) => T): T[] {
|
||||
const valuesArray = Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
valuesArray[i] = valueFunction(i);
|
||||
}
|
||||
return valuesArray;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AdfDateTimeFnsAdapter extends DatetimeAdapter<Date> {
|
||||
private _displayFormat?: string = null;
|
||||
|
||||
get displayFormat(): string | null {
|
||||
return this._displayFormat;
|
||||
}
|
||||
|
||||
set displayFormat(value: string | null) {
|
||||
this._displayFormat = value ? DateFnsUtils.convertMomentToDateFnsFormat(value) : null;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: Locale,
|
||||
@Optional() @Inject(MAT_DATETIME_FORMATS) private formats: MatDatetimeFormats,
|
||||
dateAdapter: DateAdapter<Date, Locale>
|
||||
) {
|
||||
super(dateAdapter);
|
||||
this.setLocale(matDateLocale);
|
||||
}
|
||||
|
||||
getHour(date: Date): number {
|
||||
return date.getHours();
|
||||
}
|
||||
|
||||
getMinute(date: Date): number {
|
||||
return date.getMinutes();
|
||||
}
|
||||
|
||||
getFirstDateOfMonth(date: Date): Date {
|
||||
const result = new Date();
|
||||
result.setFullYear(date.getFullYear(), date.getMonth(), 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
isInNextMonth(startDate: Date, endDate: Date): boolean {
|
||||
const nextMonth = this.getDateInNextMonth(startDate);
|
||||
return this.sameMonthAndYear(nextMonth, endDate);
|
||||
}
|
||||
|
||||
getHourNames(): string[] {
|
||||
return DEFAULT_HOUR_NAMES;
|
||||
}
|
||||
|
||||
getMinuteNames(): string[] {
|
||||
return DEFAULT_MINUTE_NAMES;
|
||||
}
|
||||
|
||||
addCalendarHours(date: Date, hours: number): Date {
|
||||
return addHours(date, hours);
|
||||
}
|
||||
|
||||
addCalendarMinutes(date: Date, minutes: number): Date {
|
||||
return addMinutes(date, minutes);
|
||||
}
|
||||
|
||||
createDatetime(year: number, month: number, date: number, hour: number, minute: number): Date {
|
||||
const result = new Date();
|
||||
result.setFullYear(year, month, date);
|
||||
result.setHours(hour, minute, 0, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
private getDateInNextMonth(date: Date) {
|
||||
return new Date(date.getFullYear(), date.getMonth() + 1, 1, date.getHours(), date.getMinutes());
|
||||
}
|
||||
|
||||
override parse(value: any, parseFormat: any): Date {
|
||||
return this._delegate.parse(value, parseFormat);
|
||||
}
|
||||
|
||||
override format(date: Date, displayFormat: any): string {
|
||||
displayFormat = DateFnsUtils.convertMomentToDateFnsFormat(displayFormat);
|
||||
|
||||
if (this.displayFormat && displayFormat === this.formats?.display?.datetimeInput) {
|
||||
return this._delegate.format(date, this.displayFormat || displayFormat);
|
||||
}
|
||||
|
||||
return this._delegate.format(date, displayFormat);
|
||||
}
|
||||
}
|
@@ -18,6 +18,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DateAdapter } from '@angular/material/core';
|
||||
import moment, { isMoment, Moment } from 'moment';
|
||||
import { UserPreferencesService, UserPreferenceValues } from '../services/user-preferences.service';
|
||||
|
||||
@Injectable()
|
||||
export class MomentDateAdapter extends DateAdapter<Moment> {
|
||||
@@ -25,6 +26,14 @@ export class MomentDateAdapter extends DateAdapter<Moment> {
|
||||
|
||||
overrideDisplayFormat: string;
|
||||
|
||||
constructor(preferences: UserPreferencesService) {
|
||||
super();
|
||||
|
||||
preferences.select(UserPreferenceValues.Locale).subscribe((locale: string) => {
|
||||
this.setLocale(locale);
|
||||
});
|
||||
}
|
||||
|
||||
getYear(date: Moment): number {
|
||||
return date.year();
|
||||
}
|
||||
|
@@ -22,3 +22,4 @@ export * from './moment-date-adapter';
|
||||
export * from './string-utils';
|
||||
export * from './date-fns-utils';
|
||||
export * from './date-fns-adapter';
|
||||
export * from './datetime-fns-adapter';
|
||||
|
@@ -65,6 +65,8 @@ import { AppConfigService } from './app-config/app-config.service';
|
||||
import { StorageService } from './common/services/storage.service';
|
||||
import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-factories/alfresco-api-v2-loader.service';
|
||||
import { AdfDateFnsAdapter } from './common/utils/date-fns-adapter';
|
||||
import { MomentDateAdapter } from './common/utils/moment-date-adapter';
|
||||
import { AdfDateTimeFnsAdapter } from './common/utils/datetime-fns-adapter';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -150,6 +152,8 @@ export class CoreModule {
|
||||
TranslateService,
|
||||
{ provide: TranslateLoader, useClass: TranslateLoaderService },
|
||||
AdfDateFnsAdapter,
|
||||
AdfDateTimeFnsAdapter,
|
||||
MomentDateAdapter,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: loadAppConfig,
|
||||
|
@@ -706,7 +706,7 @@ describe('FormFieldValidator', () => {
|
||||
});
|
||||
|
||||
it('should take into account that max value is in UTC and NOT fail validating value checking the time', () => {
|
||||
const localValidValue = '2018-3-30 11:59 PM';
|
||||
const localValidValue = '2018-03-30T22:59:00.000Z';
|
||||
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
@@ -718,7 +718,7 @@ describe('FormFieldValidator', () => {
|
||||
});
|
||||
|
||||
it('should take into account that max value is in UTC and fail validating value checking the time', () => {
|
||||
const localInvalidValue = '2018-3-31 12:01 AM';
|
||||
const localInvalidValue = '2018-03-30T23:01:00.000Z';
|
||||
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
@@ -735,8 +735,8 @@ describe('FormFieldValidator', () => {
|
||||
it('should succeed validating value checking the time', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: '08-02-9999 09:10 AM',
|
||||
maxValue: '9999-02-08 10:10 AM'
|
||||
value: '9999-02-08T09:10:00.000Z',
|
||||
maxValue: '9999-02-08T10:10:00.000Z'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
@@ -745,8 +745,8 @@ describe('FormFieldValidator', () => {
|
||||
it('should fail validating value checking the time', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: '08-02-9999 11:10 AM',
|
||||
maxValue: '9999-02-08 10:10 AM'
|
||||
value: '9999-02-08T11:10:00.000Z',
|
||||
maxValue: '9999-02-08T10:10:00.000Z'
|
||||
});
|
||||
|
||||
field.validationSummary = new ErrorMessageModel();
|
||||
@@ -757,8 +757,8 @@ describe('FormFieldValidator', () => {
|
||||
it('should succeed validating value checking the date', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: '08-02-9999 09:10 AM',
|
||||
maxValue: '9999-02-08 10:10 AM'
|
||||
value: '9999-02-08T09:10:00.000Z',
|
||||
maxValue: '9999-02-08T10:10:00.000Z'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
@@ -825,12 +825,12 @@ describe('FormFieldValidator', () => {
|
||||
});
|
||||
|
||||
it('should take into account that min value is in UTC and NOT fail validating value checking the time', () => {
|
||||
const localValidValue = '2018-3-02 06:01 AM';
|
||||
const localValidValue = '2018-03-02T06:01:00.000Z';
|
||||
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: localValidValue,
|
||||
minValue: '2018-03-02T06:00:00+00:00'
|
||||
minValue: '2018-03-02T06:00:00.000Z'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
@@ -874,8 +874,8 @@ describe('FormFieldValidator', () => {
|
||||
it('should fail validating value by time', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: '08-02-9999 09:00 AM',
|
||||
minValue: '9999-02-08 09:10 AM'
|
||||
value: '9999-08-02T08:10:00.000Z',
|
||||
minValue: '9999-08-02T08:11:00.000Z'
|
||||
});
|
||||
|
||||
field.validationSummary = new ErrorMessageModel();
|
||||
@@ -886,8 +886,8 @@ describe('FormFieldValidator', () => {
|
||||
it('should fail validating value by date', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: '07-02-9999 09:10 AM',
|
||||
minValue: '9999-02-08 09:10 AM'
|
||||
value: '9999-02-07T09:10:00.000Z',
|
||||
minValue: '9999-02-08T09:10:00.000Z'
|
||||
});
|
||||
|
||||
field.validationSummary = new ErrorMessageModel();
|
||||
@@ -1110,9 +1110,9 @@ describe('FormFieldValidator', () => {
|
||||
it('should not validate dateTime format with default format', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DATETIME,
|
||||
value: '2021-06-09 14:10' // 14:10 does not conform to A
|
||||
value: '2021-06-09 14:10 AM' // 14:10 does not conform to A
|
||||
});
|
||||
expect(field.value).toBe('2021-06-09 14:10');
|
||||
expect(field.value).toBe('2021-06-09 14:10 AM');
|
||||
expect(field.dateDisplayFormat).toBe('D-M-YYYY hh:mm A');
|
||||
expect(validator.validate(field)).toBeFalse();
|
||||
});
|
||||
|
@@ -21,6 +21,7 @@ import { FormFieldTypes } from './form-field-types';
|
||||
import { isNumberValue } from './form-field-utils';
|
||||
import { FormFieldModel } from './form-field.model';
|
||||
import { DateFnsUtils } from '../../../../common/utils/date-fns-utils';
|
||||
import { isValid as isDateValid, isBefore, isAfter } from 'date-fns';
|
||||
|
||||
export interface FormFieldValidator {
|
||||
|
||||
@@ -171,18 +172,18 @@ export class DateTimeFieldValidator implements FormFieldValidator {
|
||||
FormFieldTypes.DATETIME
|
||||
];
|
||||
|
||||
// Validates that the input string is a valid date formatted as <dateFormat> (default D-M-YYYY)
|
||||
static isValidDate(inputDate: string, dateFormat: string = 'YYYY-MM-DD HH:mm'): boolean {
|
||||
return DateFnsUtils.isValidDate(inputDate, dateFormat);
|
||||
}
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field && this.supportedTypes.indexOf(field.type) > -1;
|
||||
}
|
||||
|
||||
static isValidDateTime(input: string): boolean {
|
||||
const date = new Date(input);
|
||||
return isDateValid(date);
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value && field.isVisible) {
|
||||
if (DateFieldValidator.isValidDate(field.value, field.dateDisplayFormat)) {
|
||||
if (DateTimeFieldValidator.isValidDateTime(field.value)) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary.message = field.dateDisplayFormat;
|
||||
@@ -298,24 +299,22 @@ export class MinDateTimeFieldValidator implements FormFieldValidator {
|
||||
validate(field: FormFieldModel): boolean {
|
||||
let isValid = true;
|
||||
if (this.isSupported(field) && field.value && field.isVisible) {
|
||||
const dateFormat = field.dateDisplayFormat;
|
||||
|
||||
if (!DateFieldValidator.isValidDate(field.value, dateFormat)) {
|
||||
if (!DateTimeFieldValidator.isValidDateTime(field.value)) {
|
||||
field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE';
|
||||
isValid = false;
|
||||
} else {
|
||||
isValid = this.checkDateTime(field, dateFormat);
|
||||
isValid = this.checkDateTime(field);
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private checkDateTime(field: FormFieldModel, dateFormat: string): boolean {
|
||||
private checkDateTime(field: FormFieldModel): boolean {
|
||||
let isValid = true;
|
||||
const fieldValueDate = DateFnsUtils.parseDate(field.value, dateFormat);
|
||||
const fieldValueDate = new Date(field.value);
|
||||
const min = new Date(field.minValue);
|
||||
|
||||
if (DateFnsUtils.isBeforeDate(fieldValueDate, min)) {
|
||||
if (isBefore(fieldValueDate, min)) {
|
||||
field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`;
|
||||
field.validationSummary.attributes.set(
|
||||
'minValue',
|
||||
@@ -349,24 +348,22 @@ export class MaxDateTimeFieldValidator implements FormFieldValidator {
|
||||
validate(field: FormFieldModel): boolean {
|
||||
let isValid = true;
|
||||
if (this.isSupported(field) && field.value && field.isVisible) {
|
||||
const dateFormat = field.dateDisplayFormat;
|
||||
|
||||
if (!DateFieldValidator.isValidDate(field.value, dateFormat)) {
|
||||
if (!DateTimeFieldValidator.isValidDateTime(field.value)) {
|
||||
field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE';
|
||||
isValid = false;
|
||||
} else {
|
||||
isValid = this.checkDateTime(field, dateFormat);
|
||||
isValid = this.checkDateTime(field);
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private checkDateTime(field: FormFieldModel, dateFormat: string): boolean {
|
||||
private checkDateTime(field: FormFieldModel): boolean {
|
||||
let isValid = true;
|
||||
const fieldValueDate = DateFnsUtils.parseDate(field.value, dateFormat);
|
||||
const fieldValueDate = new Date(field.value);
|
||||
const max = new Date(field.maxValue);
|
||||
|
||||
if (DateFnsUtils.isAfterDate(fieldValueDate, max)) {
|
||||
if (isAfter(fieldValueDate, max)) {
|
||||
field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_GREATER_THAN`;
|
||||
field.validationSummary.attributes.set(
|
||||
'maxValue',
|
||||
|
@@ -6,29 +6,30 @@
|
||||
<mat-form-field class="adf-date-time-widget" [class.adf-left-label-input-datepicker]="field.leftLabels" [hideRequiredMarker]="true">
|
||||
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">{{field.name | translate }} ({{field.dateDisplayFormat}})<span class="adf-asterisk" *ngIf="isRequired()">*</span></label>
|
||||
<input matInput
|
||||
[id]="field.id"
|
||||
[value]="field.value"
|
||||
[required]="isRequired()"
|
||||
[disabled]="field.readOnly"
|
||||
(change)="onDateChanged($any($event).srcElement.value)"
|
||||
[placeholder]="field.placeholder"
|
||||
[matTooltip]="field.tooltip"
|
||||
(blur)="markAsTouched()"
|
||||
matTooltipPosition="above"
|
||||
matTooltipShowDelay="1000"
|
||||
(focus)="datetimePicker.open()">
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
[id]="field.id"
|
||||
[(ngModel)]="value"
|
||||
[required]="isRequired()"
|
||||
[disabled]="field.readOnly"
|
||||
(change)="onValueChanged($event)"
|
||||
(dateChange)="onDateChanged($event)"
|
||||
[placeholder]="field.placeholder"
|
||||
[matTooltip]="field.tooltip"
|
||||
(blur)="markAsTouched()"
|
||||
matTooltipPosition="above"
|
||||
matTooltipShowDelay="1000"
|
||||
[min]="minDate"
|
||||
[max]="maxDate">
|
||||
<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() && isTouched()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
|
||||
<mat-datetimepicker #datetimePicker type="datetime" [touchUi]="true" [timeInterval]="5" [disabled]="field.readOnly"></mat-datetimepicker>
|
||||
<input
|
||||
type="hidden"
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
[value]="field.value | adfMomentDate: field.dateDisplayFormat"
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[disabled]="field.readOnly"
|
||||
(dateInput)="onDateChanged($any($event).targetElement.value)">
|
||||
<mat-datetimepicker #datetimePicker
|
||||
type="datetime"
|
||||
[touchUi]="true"
|
||||
[openOnFocus]="true"
|
||||
[timeInterval]="5"
|
||||
[disabled]="field.readOnly">
|
||||
</mat-datetimepicker>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import moment from 'moment';
|
||||
import { FormFieldModel } from '../core/form-field.model';
|
||||
import { FormModel } from '../core/form.model';
|
||||
import { DateTimeWidgetComponent } from './date-time.widget';
|
||||
@@ -25,12 +24,14 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { FormFieldTypes } from '../core/form-field-types';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DateFieldValidator, DateTimeFieldValidator } from '../core';
|
||||
|
||||
describe('DateTimeWidgetComponent', () => {
|
||||
|
||||
let widget: DateTimeWidgetComponent;
|
||||
let fixture: ComponentFixture<DateTimeWidgetComponent>;
|
||||
let element: HTMLElement;
|
||||
let form: FormModel;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -44,6 +45,9 @@ describe('DateTimeWidgetComponent', () => {
|
||||
|
||||
element = fixture.nativeElement;
|
||||
widget = fixture.componentInstance;
|
||||
|
||||
form = new FormModel();
|
||||
form.fieldValidators = [new DateFieldValidator(), new DateTimeFieldValidator() ];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -51,9 +55,9 @@ describe('DateTimeWidgetComponent', () => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should setup min value for date picker', () => {
|
||||
const minValue = '1982-03-13T10:00:000Z';
|
||||
widget.field = new FormFieldModel(null, {
|
||||
it('should setup min value for date picker', async () => {
|
||||
const minValue = '1982-03-13T10:00:00Z';
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: 'date-id',
|
||||
name: 'date-name',
|
||||
type: 'datetime',
|
||||
@@ -61,13 +65,13 @@ describe('DateTimeWidgetComponent', () => {
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const expected = moment(minValue, 'YYYY-MM-DDTHH:mm:ssZ');
|
||||
expect(widget.minDate.isSame(expected)).toBeTruthy();
|
||||
expect(widget.minDate.toISOString()).toBe(`1982-03-13T10:00:00.000Z`);
|
||||
});
|
||||
|
||||
it('should date field be present', () => {
|
||||
widget.field = new FormFieldModel(null, {
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: 'date-id',
|
||||
name: 'date-name',
|
||||
type: 'datetime'
|
||||
@@ -78,35 +82,161 @@ describe('DateTimeWidgetComponent', () => {
|
||||
expect(element.querySelector('#data-time-widget')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should setup max value for date picker', () => {
|
||||
const maxValue = '1982-03-13T10:00:000Z';
|
||||
it('should setup max value for date picker', async () => {
|
||||
const maxValue = '1982-03-13T10:00:00Z';
|
||||
widget.field = new FormFieldModel(null, {
|
||||
maxValue
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const expected = moment(maxValue, 'YYYY-MM-DDTHH:mm:ssZ');
|
||||
expect(widget.maxDate.isSame(expected)).toBeTruthy();
|
||||
expect(widget.maxDate.toISOString()).toBe('1982-03-13T10:00:00.000Z');
|
||||
});
|
||||
|
||||
it('should eval visibility on date changed', () => {
|
||||
spyOn(widget, 'onFieldChanged').and.callThrough();
|
||||
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '09-12-9999 10:00 AM',
|
||||
type: 'datetime',
|
||||
readOnly: 'false'
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
const mockDate = moment('1982-03-13T10:00:000Z', 'YYYY-MM-DDTHH:mm:ssZ');
|
||||
widget.onDateChanged(mockDate);
|
||||
widget.onDateChanged({ value: new Date('1982-03-13T10:00:00.000Z') } as any);
|
||||
|
||||
expect(widget.onFieldChanged).toHaveBeenCalledWith(field);
|
||||
});
|
||||
|
||||
it('should validate the initial datetime value', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
|
||||
fixture.whenStable();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(field.isValid).toBeTrue();
|
||||
});
|
||||
|
||||
it('should validate the updated datetime value', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
|
||||
fixture.whenStable();
|
||||
await fixture.whenStable();
|
||||
|
||||
widget.onDateChanged({ value: new Date('9999-09-12T09:10:00.000Z') } as any);
|
||||
|
||||
expect(field.value).toBe('9999-09-12T09:10:00.000Z');
|
||||
expect(field.isValid).toBeTrue();
|
||||
});
|
||||
|
||||
it('should forwad the incorrect datetime input for further validation', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
widget.onDateChanged({
|
||||
value: null,
|
||||
targetElement: {
|
||||
value: '123abc'
|
||||
}
|
||||
} as any);
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(field.value).toBe('123abc');
|
||||
expect(field.isValid).toBeFalse();
|
||||
expect(field.validationSummary.message).toBe('D-M-YYYY hh:mm A');
|
||||
});
|
||||
|
||||
it('should process direct keyboard input', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
|
||||
fixture.whenStable();
|
||||
await fixture.whenStable();
|
||||
|
||||
widget.onValueChanged({ target: { value: '9999-09-12T09:10:00.000Z' } } as any);
|
||||
|
||||
expect(field.value).toBe('9999-09-12T09:10:00.000Z');
|
||||
expect(field.isValid).toBeTrue();
|
||||
});
|
||||
|
||||
it('should fail validating incorrect keyboard input', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
widget.onValueChanged({
|
||||
target: {
|
||||
value: '123abc'
|
||||
}
|
||||
} as any);
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(field.value).toBe('123abc');
|
||||
expect(field.isValid).toBeFalse();
|
||||
expect(field.validationSummary.message).toBe('D-M-YYYY hh:mm A');
|
||||
});
|
||||
|
||||
it('should allow empty dates when not required', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '9999-09-12T09:00:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
widget.field = field;
|
||||
|
||||
fixture.whenStable();
|
||||
await fixture.whenStable();
|
||||
|
||||
widget.onDateChanged({ value: null, targetElement: { value: '' } } as any);
|
||||
|
||||
expect(field.value).toBe('');
|
||||
expect(field.isValid).toBeTrue();
|
||||
});
|
||||
|
||||
describe('when tooltip is set', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -166,97 +296,95 @@ describe('DateTimeWidgetComponent', () => {
|
||||
it('should be able to display label with asterisk', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const asterisk: HTMLElement = element.querySelector('.adf-asterisk');
|
||||
const asterisk = element.querySelector<HTMLElement>('.adf-asterisk');
|
||||
|
||||
expect(asterisk).toBeTruthy();
|
||||
expect(asterisk.textContent).toEqual('*');
|
||||
expect(asterisk).not.toBeNull();
|
||||
expect(asterisk?.textContent).toEqual('*');
|
||||
});
|
||||
});
|
||||
|
||||
describe('template check', () => {
|
||||
|
||||
it('should show visible date widget', () => {
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
it('should show visible date widget', async () => {
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '30-11-9999 10:30 AM',
|
||||
type: 'datetime',
|
||||
readOnly: 'false'
|
||||
value: '9999-11-30T10:30:00.000Z',
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(element.querySelector('#date-field-id')).toBeDefined();
|
||||
expect(element.querySelector('#date-field-id')).not.toBeNull();
|
||||
|
||||
const dateElement: any = element.querySelector('#date-field-id');
|
||||
expect(dateElement.value).toBe('30-11-9999 10:30 AM');
|
||||
const dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
|
||||
expect(dateElement).not.toBeNull();
|
||||
expect(dateElement?.value).toBe('30-11-9999 10:30 AM');
|
||||
});
|
||||
|
||||
it('should show the correct format type', () => {
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
it('should show the correct format type', async () => {
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '12-30-9999 10:30 AM',
|
||||
value: '9999-12-30T10:30:00.000Z',
|
||||
dateDisplayFormat: 'MM-DD-YYYY HH:mm A',
|
||||
type: 'datetime',
|
||||
readOnly: 'false'
|
||||
type: 'datetime'
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(element.querySelector('#date-field-id')).toBeDefined();
|
||||
expect(element.querySelector('#date-field-id')).not.toBeNull();
|
||||
|
||||
const dateElement: any = element.querySelector('#date-field-id');
|
||||
expect(dateElement.value).toContain('12-30-9999 10:30 AM');
|
||||
const dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
|
||||
expect(dateElement).not.toBeNull();
|
||||
expect(dateElement?.value).toContain('12-30-9999 10:30 AM');
|
||||
});
|
||||
|
||||
it('should disable date button when is readonly', () => {
|
||||
widget.field = new FormFieldModel(new FormModel(), {
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'date-name',
|
||||
value: '12-30-9999 10:30 AM',
|
||||
value: '9999-12-30T10:30:00.000Z',
|
||||
dateDisplayFormat: 'MM-DD-YYYY HH:mm A',
|
||||
type: 'datetime',
|
||||
readOnly: 'false'
|
||||
type: 'datetime'
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
let dateButton = element.querySelector<HTMLButtonElement>('button');
|
||||
expect(dateButton.disabled).toBeFalsy();
|
||||
expect(dateButton).not.toBeNull();
|
||||
expect(dateButton?.disabled).toBeFalsy();
|
||||
|
||||
widget.field.readOnly = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
dateButton = element.querySelector<HTMLButtonElement>('button');
|
||||
expect(dateButton.disabled).toBeTruthy();
|
||||
expect(dateButton).not.toBeNull();
|
||||
expect(dateButton?.disabled).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display always the json value', () => {
|
||||
const field = new FormFieldModel(new FormModel(), {
|
||||
it('should display always the json value', async () => {
|
||||
const field = new FormFieldModel(form, {
|
||||
id: 'date-field-id',
|
||||
name: 'datetime-field-name',
|
||||
value: '12-30-9999 10:30 AM',
|
||||
value: '9999-12-30T10:30:00.000Z',
|
||||
type: 'datetime',
|
||||
readOnly: 'false'
|
||||
dateDisplayFormat: 'MM-DD-YYYY HH:mm A'
|
||||
});
|
||||
field.isVisible = true;
|
||||
field.dateDisplayFormat = 'MM-DD-YYYY HH:mm A';
|
||||
widget.field = field;
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(element.querySelector('#date-field-id')).toBeDefined();
|
||||
expect(element.querySelector('#date-field-id')).not.toBeNull();
|
||||
const dateElement = element.querySelector<HTMLInputElement>('#date-field-id');
|
||||
expect(dateElement).not.toBeNull();
|
||||
expect(dateElement?.value).toContain('12-30-9999 10:30 AM');
|
||||
|
||||
const dateElement: any = element.querySelector('#date-field-id');
|
||||
expect(dateElement.value).toContain('12-30-9999 10:30 AM');
|
||||
widget.field.value = '2020-03-02T00:00:00.000Z';
|
||||
|
||||
widget.field.value = '03-02-2020 12:00 AM';
|
||||
fixture.componentInstance.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(dateElement.value).toContain('03-02-2020 12:00 AM');
|
||||
expect(dateElement?.value).toContain('03-02-2020 00:00 AM');
|
||||
});
|
||||
|
||||
describe('when form model has left labels', () => {
|
||||
|
@@ -17,76 +17,88 @@
|
||||
|
||||
/* eslint-disable @angular-eslint/component-selector */
|
||||
|
||||
import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
|
||||
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
|
||||
import { DatetimeAdapter, MAT_DATETIME_FORMATS } from '@mat-datetimepicker/core';
|
||||
import { MomentDatetimeAdapter, MAT_MOMENT_DATETIME_FORMATS } from '@mat-datetimepicker/moment';
|
||||
import moment, { Moment } from 'moment';
|
||||
import { UserPreferencesService, UserPreferenceValues } from '../../../../common/services/user-preferences.service';
|
||||
import { MomentDateAdapter } from '../../../../common/utils/moment-date-adapter';
|
||||
import { MOMENT_DATE_FORMATS } from '../../../../common/utils/moment-date-formats.model';
|
||||
import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerInputEvent } from '@mat-datetimepicker/core';
|
||||
import { FormService } from '../../../services/form.service';
|
||||
import { WidgetComponent } from '../widget.component';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '../../../../common/utils/date-fns-adapter';
|
||||
import { ADF_DATETIME_FORMATS, AdfDateTimeFnsAdapter } from '../../../../common/utils/datetime-fns-adapter';
|
||||
import { DateFnsUtils } from '../../../../common';
|
||||
import { isValid } from 'date-fns';
|
||||
|
||||
@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 }
|
||||
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS },
|
||||
{ provide: MAT_DATETIME_FORMATS, useValue: ADF_DATETIME_FORMATS },
|
||||
{ provide: DateAdapter, useClass: AdfDateFnsAdapter },
|
||||
{ provide: DatetimeAdapter, useClass: AdfDateTimeFnsAdapter }
|
||||
],
|
||||
selector: 'date-time-widget',
|
||||
templateUrl: './date-time.widget.html',
|
||||
styleUrls: ['./date-time.widget.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, OnDestroy {
|
||||
export class DateTimeWidgetComponent extends WidgetComponent implements OnInit {
|
||||
minDate: Date;
|
||||
maxDate: Date;
|
||||
|
||||
minDate: Moment;
|
||||
maxDate: Moment;
|
||||
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
@Input()
|
||||
value: any = null;
|
||||
|
||||
constructor(public formService: FormService,
|
||||
private dateAdapter: DateAdapter<Moment>,
|
||||
private userPreferencesService: UserPreferencesService) {
|
||||
private dateAdapter: DateAdapter<Date>,
|
||||
private dateTimeAdapter: DatetimeAdapter<Date>) {
|
||||
super(formService);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.userPreferencesService
|
||||
.select(UserPreferenceValues.Locale)
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(locale => this.dateAdapter.setLocale(locale));
|
||||
if (this.field.dateDisplayFormat) {
|
||||
const dateAdapter = this.dateAdapter as AdfDateFnsAdapter;
|
||||
dateAdapter.displayFormat = this.field.dateDisplayFormat;
|
||||
|
||||
const momentDateAdapter = this.dateAdapter as MomentDateAdapter;
|
||||
momentDateAdapter.overrideDisplayFormat = this.field.dateDisplayFormat;
|
||||
const dateTimeAdapter = this.dateTimeAdapter as AdfDateTimeFnsAdapter;
|
||||
dateTimeAdapter.displayFormat = this.field.dateDisplayFormat;
|
||||
}
|
||||
|
||||
if (this.field) {
|
||||
if (this.field.minValue) {
|
||||
this.minDate = moment.utc(this.field.minValue, 'YYYY-MM-DDTHH:mm:ssZ');
|
||||
this.minDate = DateFnsUtils.localToUtc(new Date(this.field.minValue));
|
||||
}
|
||||
|
||||
if (this.field.maxValue) {
|
||||
this.maxDate = moment.utc(this.field.maxValue, 'YYYY-MM-DDTHH:mm:ssZ');
|
||||
this.maxDate = DateFnsUtils.localToUtc(new Date(this.field.maxValue));
|
||||
}
|
||||
|
||||
if (this.field.value) {
|
||||
this.value = DateFnsUtils.localToUtc(new Date(this.field.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
onValueChanged(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const newValue = this.dateTimeAdapter.parse(input.value, this.field.dateDisplayFormat);
|
||||
|
||||
if (isValid(newValue)) {
|
||||
this.field.value = DateFnsUtils.utcToLocal(newValue).toISOString();
|
||||
} else {
|
||||
this.field.value = input.value;
|
||||
}
|
||||
|
||||
this.onFieldChanged(this.field);
|
||||
}
|
||||
|
||||
onDateChanged(newDateValue) {
|
||||
const date = moment(newDateValue, this.field.dateDisplayFormat, true);
|
||||
if (date.isValid()) {
|
||||
this.field.value = moment(date).utc().local().format(this.field.dateDisplayFormat);
|
||||
onDateChanged(event: MatDatetimepickerInputEvent<Date>) {
|
||||
const newValue = event.value;
|
||||
const input = event.targetElement as HTMLInputElement;
|
||||
|
||||
if (newValue && isValid(newValue)) {
|
||||
this.field.value = DateFnsUtils.utcToLocal(newValue).toISOString();
|
||||
} else {
|
||||
this.field.value = newDateValue;
|
||||
this.field.value = input.value;
|
||||
}
|
||||
|
||||
this.onFieldChanged(this.field);
|
||||
}
|
||||
}
|
||||
|
@@ -23,13 +23,12 @@ import { FormService } from '../../../services/form.service';
|
||||
import { WidgetComponent } from '../widget.component';
|
||||
import { Subject } from 'rxjs';
|
||||
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
|
||||
import { ADF_FORM_DATE_FORMATS } from '../../../date-formats';
|
||||
import { AdfDateFnsAdapter } from '../../../../common/utils/date-fns-adapter';
|
||||
import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '../../../../common/utils/date-fns-adapter';
|
||||
|
||||
@Component({
|
||||
selector: 'date-widget',
|
||||
providers: [
|
||||
{ provide: MAT_DATE_FORMATS, useValue: ADF_FORM_DATE_FORMATS },
|
||||
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS },
|
||||
{ provide: DateAdapter, useClass: AdfDateFnsAdapter }
|
||||
],
|
||||
templateUrl: './date.widget.html',
|
||||
|
@@ -1,31 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { MatDateFormats } from '@angular/material/core';
|
||||
|
||||
export const ADF_FORM_DATE_FORMATS: MatDateFormats = {
|
||||
parse: {
|
||||
dateInput: 'dd-MM-yyyy'
|
||||
},
|
||||
display: {
|
||||
dateInput: 'dd-MM-yyyy',
|
||||
monthLabel: 'LLL',
|
||||
monthYearLabel: 'LLL uuuu',
|
||||
dateA11yLabel: 'PP',
|
||||
monthYearA11yLabel: 'LLLL uuuu'
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user