diff --git a/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.scss b/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.scss index dd6aa7bc54f..797a1c5e0b9 100644 --- a/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.scss +++ b/lib/core/src/lib/card-view/components/card-view-dateitem/card-view-dateitem.component.scss @@ -12,7 +12,6 @@ &.adf-property-value-editable { display: flex; align-items: center; - justify-content: flex-end; border-radius: 6px; border-bottom: inherit; margin-bottom: 18px; diff --git a/lib/core/src/lib/common/utils/date-fns-adapter.ts b/lib/core/src/lib/common/utils/date-fns-adapter.ts index 3b0b8fda701..5b5fcfe700d 100644 --- a/lib/core/src/lib/common/utils/date-fns-adapter.ts +++ b/lib/core/src/lib/common/utils/date-fns-adapter.ts @@ -20,7 +20,7 @@ import { DateFnsUtils } from './date-fns-utils'; import { Inject, Injectable, Optional } from '@angular/core'; import { MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatDateFormats } from '@angular/material/core'; import { UserPreferenceValues, UserPreferencesService } from '../services/user-preferences.service'; -import { Locale } from 'date-fns'; +import { isValid, Locale, parse } from 'date-fns'; /** * Date-fns adapter with moment-to-date-fns conversion. @@ -47,15 +47,17 @@ import { Locale } from 'date-fns'; * } */ +export const DEFAULT_DATE_FORMAT = 'dd-MM-yyyy'; + /** * Material date formats for Date-fns */ export const ADF_DATE_FORMATS: MatDateFormats = { parse: { - dateInput: 'dd-MM-yyyy' + dateInput: DEFAULT_DATE_FORMAT }, display: { - dateInput: 'dd-MM-yyyy', + dateInput: DEFAULT_DATE_FORMAT, monthLabel: 'LLL', monthYearLabel: 'LLL uuuu', dateA11yLabel: 'PP', @@ -88,10 +90,11 @@ export class AdfDateFnsAdapter extends DateFnsAdapter { } override parse(value: any, parseFormat: string | string[]): Date { + const dateValue = this.isValid(value) ? value : this.parseAndValidateDate(value); const format = Array.isArray(parseFormat) ? parseFormat.map(DateFnsUtils.convertMomentToDateFnsFormat) : DateFnsUtils.convertMomentToDateFnsFormat(parseFormat); - return super.parse(value, format); + return super.parse(dateValue, format); } override format(date: Date, displayFormat: string): string { @@ -103,4 +106,9 @@ export class AdfDateFnsAdapter extends DateFnsAdapter { return super.format(date, displayFormat); } + + private parseAndValidateDate(value: any): Date { + const parsedDate = parse(value, this.displayFormat || DEFAULT_DATE_FORMAT, new Date()); + return isValid(parsedDate) ? parsedDate : value; + } } diff --git a/lib/core/src/lib/common/utils/datetime-fns-adapter.ts b/lib/core/src/lib/common/utils/datetime-fns-adapter.ts index e44c89bcf9f..24c09a8aa07 100644 --- a/lib/core/src/lib/common/utils/datetime-fns-adapter.ts +++ b/lib/core/src/lib/common/utils/datetime-fns-adapter.ts @@ -19,7 +19,7 @@ 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'; +import { Locale, addHours, addMinutes, isValid, parse } from 'date-fns'; /** * Material date/time formats for Date-fns (mat-datetimepicker) @@ -126,7 +126,8 @@ export class AdfDateTimeFnsAdapter extends DatetimeAdapter { } override parse(value: any, parseFormat: any): Date { - return this._delegate.parse(value, parseFormat); + const dateToParse = isValid(new Date(value)) ? parse(value, this.displayFormat, new Date()) : value; + return this._delegate.parse(dateToParse, parseFormat); } override format(date: Date, displayFormat: any): string { diff --git a/lib/core/src/lib/form/components/form-renderer.component.spec.ts b/lib/core/src/lib/form/components/form-renderer.component.spec.ts index 4dc682e51cf..f67e5fc8d96 100644 --- a/lib/core/src/lib/form/components/form-renderer.component.spec.ts +++ b/lib/core/src/lib/form/components/form-renderer.component.spec.ts @@ -110,7 +110,7 @@ describe('Form Renderer Component', () => { expectElementToBeHidden(displayTextElementContainer); inputDateTestOne.value = '2019-11-19'; - inputDateTestOne.dispatchEvent(new Event('change')); + inputDateTestOne.dispatchEvent(new Event('input')); fixture.detectChanges(); await fixture.whenStable(); @@ -130,7 +130,7 @@ describe('Form Renderer Component', () => { expectElementToBeVisible(displayTextElementContainer); inputDateTestOne.value = '2019-11-19'; - inputDateTestOne.dispatchEvent(new Event('change')); + inputDateTestOne.dispatchEvent(new Event('input')); fixture.detectChanges(); await fixture.whenStable(); diff --git a/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts b/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts index 90c7f5c543d..87c1f8555a9 100644 --- a/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts +++ b/lib/core/src/lib/form/components/mock/form-renderer.component.mock.ts @@ -1552,7 +1552,7 @@ export const formDateVisibility = { 2: [ { id: 'Text0pqd1u', - name: 'Text', + name: 'Text equal specific date', type: 'text', readOnly: false, required: false, @@ -1589,7 +1589,7 @@ export const formDateVisibility = { 1: [ { id: 'Text0uyqd3', - name: 'Text', + name: 'Text NOT equal specific date', type: 'text', readOnly: false, required: false, diff --git a/lib/core/src/lib/form/components/widgets/checkbox/checkbox.widget.html b/lib/core/src/lib/form/components/widgets/checkbox/checkbox.widget.html index e914e288abe..825d0d0af70 100644 --- a/lib/core/src/lib/form/components/widgets/checkbox/checkbox.widget.html +++ b/lib/core/src/lib/form/components/widgets/checkbox/checkbox.widget.html @@ -2,6 +2,7 @@ [class.adf-invalid]="!field.isValid && isTouched()"> 0) { this.attributes.forEach((value, key) => { result[key] = typeof value === 'string' ? value : JSON.stringify(value); diff --git a/lib/core/src/lib/form/components/widgets/core/form-field-types.ts b/lib/core/src/lib/form/components/widgets/core/form-field-types.ts index b934ec99455..f62cfd6ffea 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field-types.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field-types.ts @@ -15,7 +15,7 @@ * limitations under the License. */ - /* eslint-disable @angular-eslint/component-selector */ +/* eslint-disable @angular-eslint/component-selector */ export class FormFieldTypes { static CONTAINER: string = 'container'; @@ -50,20 +50,13 @@ export class FormFieldTypes { static DATA_TABLE: string = 'data-table'; static DISPLAY_EXTERNAL_PROPERTY: string = 'display-external-property'; - static READONLY_TYPES: string[] = [ - FormFieldTypes.HYPERLINK, - FormFieldTypes.DISPLAY_VALUE, - FormFieldTypes.READONLY_TEXT, - FormFieldTypes.GROUP - ]; + static READONLY_TYPES: string[] = [FormFieldTypes.HYPERLINK, FormFieldTypes.DISPLAY_VALUE, FormFieldTypes.READONLY_TEXT, FormFieldTypes.GROUP]; - static VALIDATABLE_TYPES: string[] = [ - FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY - ]; + static VALIDATABLE_TYPES: string[] = [FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY]; - static CONSTANT_VALUE_TYPES: string[] = [ - FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY - ]; + static REACTIVE_TYPES: string[] = [FormFieldTypes.DATE, FormFieldTypes.DATETIME]; + + static CONSTANT_VALUE_TYPES: string[] = [FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY]; static isReadOnlyType(type: string) { return FormFieldTypes.READONLY_TYPES.includes(type); @@ -73,6 +66,10 @@ export class FormFieldTypes { return FormFieldTypes.VALIDATABLE_TYPES.includes(type); } + static isReactiveType(type: string): boolean { + return FormFieldTypes.REACTIVE_TYPES.includes(type); + } + static isConstantValueType(type: string) { return FormFieldTypes.CONSTANT_VALUE_TYPES.includes(type); } diff --git a/lib/core/src/lib/form/components/widgets/core/form-field-validator.spec.ts b/lib/core/src/lib/form/components/widgets/core/form-field-validator.spec.ts index f478af0b45a..48513ffdc90 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field-validator.spec.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field-validator.spec.ts @@ -16,7 +16,6 @@ */ import { ErrorMessageModel } from './error-message.model'; -import { FormFieldOption } from './form-field-option'; import { FormFieldTypes } from './form-field-types'; import { FixedValueFieldValidator, @@ -27,11 +26,6 @@ import { NumberFieldValidator, RegExFieldValidator, RequiredFieldValidator, - MaxDateTimeFieldValidator, - MinDateTimeFieldValidator, - MaxDateFieldValidator, - MinDateFieldValidator, - DateTimeFieldValidator, DecimalFieldValidator } from './form-field-validator'; import { FormFieldModel } from './form-field.model'; @@ -65,22 +59,6 @@ describe('FormFieldValidator', () => { expect(validator.validate(field)).toBe(true); }); - it('should fail (display error) for dropdown with empty value', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DROPDOWN, - value: '', - options: [{ id: 'empty', name: 'Choose option...' }], - hasEmptyValue: true, - required: true - }); - - field.emptyOption = { id: '' } as FormFieldOption; - expect(validator.validate(field)).toBe(false); - - field.value = ''; - expect(validator.validate(field)).toBe(true); - }); - it('should fail (display error) for multiple type dropdown with zero selection', () => { const field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.DROPDOWN, @@ -196,20 +174,6 @@ describe('FormFieldValidator', () => { expect(validator.validate(field)).toBe(true); }); - it('should fail (display error) for date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: null, - required: true - }); - - field.value = null; - expect(validator.validate(field)).toBe(false); - - field.value = ''; - expect(validator.validate(field)).toBe(false); - }); - it('should succeed for text', () => { const field = new FormFieldModel(new FormModel(), { type: FormFieldTypes.TEXT, @@ -664,466 +628,6 @@ describe('FormFieldValidator', () => { }); }); - describe('MaxDateTimeFieldValidator', () => { - let validator: MaxDateTimeFieldValidator; - - beforeEach(() => { - validator = new MaxDateTimeFieldValidator(); - }); - - it('should require maxValue defined', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME - }); - expect(validator.isSupported(field)).toBe(false); - - field.maxValue = '9999-02-08 10:10 AM'; - expect(validator.isSupported(field)).toBe(true); - }); - - it('should support date time widgets only', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - maxValue: '9999-02-08 10:10 AM' - }); - - expect(validator.isSupported(field)).toBe(true); - - field.type = FormFieldTypes.TEXT; - expect(validator.isSupported(field)).toBe(false); - }); - - it('should allow empty values', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: null, - maxValue: '9999-02-08 10:10 AM' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should succeed for unsupported types', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.TEXT - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should take into account that max value is in UTC and NOT fail (display error) validating value checking the time', () => { - const localValidValue = '2018-03-30T22:59:00.000Z'; - - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: localValidValue, - maxValue: '2018-03-31T23:00:00.000Z' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should take into account that max value is in UTC and fail (display error) validating value checking the time', () => { - const localInvalidValue = '2018-03-30T23:01:00.000Z'; - - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: localInvalidValue, - maxValue: `2018-03-30T23:00:00.000Z` - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - expect(field.validationSummary.message).toBe('FORM.FIELD.VALIDATOR.NOT_GREATER_THAN'); - }); - - it('should succeed validating value checking the time', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '9999-02-08T09:10:00.000Z', - maxValue: '9999-02-08T10:10:00.000Z' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should fail (display error) validating value checking the time', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '9999-02-08T11:10:00.000Z', - maxValue: '9999-02-08T10:10:00.000Z' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - - it('should succeed validating value checking the date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '9999-02-08T09:10:00.000Z', - maxValue: '9999-02-08T10:10:00.000Z' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should fail (display error) validating value checking the date', () => { - const 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)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - }); - - describe('MinDateTimeFieldValidator', () => { - let validator: MinDateTimeFieldValidator; - - beforeEach(() => { - validator = new MinDateTimeFieldValidator(); - }); - - it('should require minValue defined', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME - }); - expect(validator.isSupported(field)).toBe(false); - - field.minValue = '9999-02-08 09:10 AM'; - expect(validator.isSupported(field)).toBe(true); - }); - - it('should support date time widgets only', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - minValue: '9999-02-08 09:10 AM' - }); - - expect(validator.isSupported(field)).toBe(true); - - field.type = FormFieldTypes.TEXT; - expect(validator.isSupported(field)).toBe(false); - }); - - it('should allow empty values', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: null, - minValue: '9999-02-08 09:10 AM' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should succeed for unsupported types', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.TEXT - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should take into account that min value is in UTC and NOT fail (display error) validating value checking the time', () => { - 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.000Z' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should take into account that min value is in UTC and fail (display error) validating value checking the time', () => { - const localInvalidValue = '2018-3-02 05:59 AM'; - - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: localInvalidValue, - minValue: '2018-03-02T06:00:00+00:00' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - expect(field.validationSummary.message).toBe('FORM.FIELD.VALIDATOR.NOT_LESS_THAN'); - }); - - it('should succeed validating value by time', () => { - const 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)).toBe(true); - }); - - it('should succeed validating value by date', () => { - const 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)).toBe(true); - }); - - it('should fail (display error) validating value by time', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '9999-08-02T08:10:00.000Z', - minValue: '9999-08-02T08:11:00.000Z' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - - it('should fail (display error) validating value by date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '9999-02-07T09:10:00.000Z', - minValue: '9999-02-08T09:10:00.000Z' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - }); - - describe('MaxDateFieldValidator', () => { - let validator: MaxDateFieldValidator; - - beforeEach(() => { - validator = new MaxDateFieldValidator(); - }); - - it('should require maxValue defined', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE - }); - expect(validator.isSupported(field)).toBe(false); - - field.maxValue = '9999-02-08'; - expect(validator.isSupported(field)).toBe(true); - }); - - it('should support date widgets only', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - maxValue: '9999-02-08' - }); - - expect(validator.isSupported(field)).toBe(true); - - field.type = FormFieldTypes.TEXT; - expect(validator.isSupported(field)).toBe(false); - }); - - it('should allow empty values', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: null, - maxValue: '9999-02-08' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should succeed for unsupported types', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.TEXT - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should succeed validating value checking the date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - maxValue: '9999-02-09' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should fail (display error) validating value checking the date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - maxValue: '9999-02-07' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - - it('should validate with APS1 format', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - maxValue: '09-02-9999' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should fail (display error) validating with APS1 format', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - maxValue: '07-02-9999' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - }); - - describe('MinDateFieldValidator', () => { - let validator: MinDateFieldValidator; - - beforeEach(() => { - validator = new MinDateFieldValidator(); - }); - - it('should require maxValue defined', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE - }); - expect(validator.isSupported(field)).toBe(false); - - field.minValue = '9999-02-08'; - expect(validator.isSupported(field)).toBe(true); - }); - - it('should support date widgets only', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - minValue: '9999-02-08' - }); - - expect(validator.isSupported(field)).toBe(true); - - field.type = FormFieldTypes.TEXT; - expect(validator.isSupported(field)).toBe(false); - }); - - it('should allow empty values', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: null, - minValue: '9999-02-08' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should succeed for unsupported types', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.TEXT - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should succeed validating value checking the date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - minValue: '9999-02-07' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should fail (display error) validating value checking the date', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - minValue: '9999-02-09' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - - it('should validate with APS1 format', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - minValue: '07-02-9999' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should fail (display error) validating with APS1 format', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATE, - value: '9999-02-08T00:00:00', - minValue: '09-02-9999' - }); - - field.validationSummary = new ErrorMessageModel(); - expect(validator.validate(field)).toBe(false); - expect(field.validationSummary).not.toBeNull(); - }); - }); - - describe('DateTimeFieldValidator', () => { - let validator: DateTimeFieldValidator; - - beforeEach(() => { - validator = new DateTimeFieldValidator(); - }); - - it('should validate dateTime format with dateDisplayFormat', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '2021-06-09 14:10', - dateDisplayFormat: 'YYYY-MM-DD HH:mm' - }); - - expect(validator.validate(field)).toBe(true); - }); - - it('should validate dateTime format with default format', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '9-6-2021 11:10 AM' - }); - expect(field.value).toBe('9-6-2021 11:10 AM'); - expect(field.dateDisplayFormat).toBe('D-M-YYYY hh:mm A'); - expect(validator.validate(field)).toBe(true); - }); - - it('should not validate dateTime format with default format', () => { - const field = new FormFieldModel(new FormModel(), { - type: FormFieldTypes.DATETIME, - value: '2021-06-09 14:10 AM' // 14:10 does not conform to A - }); - expect(field.value).toBe('2021-06-09 14:10 AM'); - expect(field.dateDisplayFormat).toBe('D-M-YYYY hh:mm A'); - expect(validator.validate(field)).toBe(false); - }); - }); - describe('DecimalFieldValidator', () => { let decimalValidator: DecimalFieldValidator; diff --git a/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts b/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts index 79f0f63de75..aa28adafdad 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts @@ -20,8 +20,6 @@ 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 { isSupported(field: FormFieldModel): boolean; @@ -42,8 +40,6 @@ export class RequiredFieldValidator implements FormFieldValidator { FormFieldTypes.UPLOAD, FormFieldTypes.AMOUNT, FormFieldTypes.DYNAMIC_TABLE, - FormFieldTypes.DATE, - FormFieldTypes.DATETIME, FormFieldTypes.ATTACH_FOLDER, FormFieldTypes.DECIMAL, FormFieldTypes.DISPLAY_EXTERNAL_PROPERTY @@ -127,208 +123,6 @@ export class NumberFieldValidator implements FormFieldValidator { } } -export class DateFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.DATE]; - - // Validates that the input string is a valid date formatted as (default D-M-YYYY) - static isValidDate(inputDate: string, dateFormat: string = 'D-M-YYYY'): boolean { - return DateFnsUtils.isValidDate(inputDate, dateFormat); - } - - isSupported(field: FormFieldModel): boolean { - return field && this.supportedTypes.indexOf(field.type) > -1; - } - - validate(field: FormFieldModel): boolean { - if (this.isSupported(field) && field.value && field.isVisible) { - if (DateFieldValidator.isValidDate(field.value, field.dateDisplayFormat)) { - return true; - } - field.validationSummary.message = field.dateDisplayFormat; - return false; - } - return true; - } -} - -export class DateTimeFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.DATETIME]; - - isSupported(field: FormFieldModel): boolean { - return field && this.supportedTypes.indexOf(field.type) > -1; - } - - static isValidDateTime(input: string): boolean { - const date = DateFnsUtils.getDate(input); - return isDateValid(date); - } - - validate(field: FormFieldModel): boolean { - if (this.isSupported(field) && field.value && field.isVisible) { - if (DateTimeFieldValidator.isValidDateTime(field.value)) { - return true; - } - field.validationSummary.message = field.dateDisplayFormat; - return false; - } - return true; - } -} - -export abstract class BoundaryDateFieldValidator implements FormFieldValidator { - DATE_FORMAT_CLOUD = 'YYYY-MM-DD'; - DATE_FORMAT = 'DD-MM-YYYY'; - - supportedTypes = [FormFieldTypes.DATE]; - - 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)) { - field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; - isValid = false; - } else { - isValid = this.checkDate(field, dateFormat); - } - } - return isValid; - } - - extractDateFormat(date: string): string { - const brokenDownDate = date.split('-'); - return brokenDownDate[0].length === 4 ? this.DATE_FORMAT_CLOUD : this.DATE_FORMAT; - } - - abstract checkDate(field: FormFieldModel, dateFormat: string); - abstract isSupported(field: FormFieldModel); -} - -export class MinDateFieldValidator extends BoundaryDateFieldValidator { - checkDate(field: FormFieldModel, dateFormat: string): boolean { - let isValid = true; - const fieldValueData = DateFnsUtils.parseDate(field.value, dateFormat, { dateOnly: true }); - const minValueDateFormat = this.extractDateFormat(field.minValue); - const min = DateFnsUtils.parseDate(field.minValue, minValueDateFormat); - - if (DateFnsUtils.isBeforeDate(fieldValueData, min)) { - field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; - field.validationSummary.attributes.set('minValue', DateFnsUtils.formatDate(min, field.dateDisplayFormat).toLocaleUpperCase()); - isValid = false; - } - return isValid; - } - - isSupported(field: FormFieldModel): boolean { - return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.minValue; - } -} - -export class MaxDateFieldValidator extends BoundaryDateFieldValidator { - checkDate(field: FormFieldModel, dateFormat: string): boolean { - let isValid = true; - const fieldValueData = DateFnsUtils.parseDate(field.value, dateFormat, { dateOnly: true }); - const maxValueDateFormat = this.extractDateFormat(field.maxValue); - const max = DateFnsUtils.parseDate(field.maxValue, maxValueDateFormat); - - if (DateFnsUtils.isAfterDate(fieldValueData, max)) { - field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_GREATER_THAN`; - field.validationSummary.attributes.set('maxValue', DateFnsUtils.formatDate(max, field.dateDisplayFormat).toLocaleUpperCase()); - isValid = false; - } - return isValid; - } - - isSupported(field: FormFieldModel): boolean { - return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.maxValue; - } -} - -export abstract class BoundaryDateTimeFieldValidator implements FormFieldValidator { - private supportedTypes = [FormFieldTypes.DATETIME]; - - isSupported(field: FormFieldModel): boolean { - return field && this.supportedTypes.indexOf(field.type) > -1 && !!field[this.getSubjectField()]; - } - - validate(field: FormFieldModel): boolean { - let isValid = true; - if (this.isSupported(field) && field.value && field.isVisible) { - if (!DateTimeFieldValidator.isValidDateTime(field.value)) { - field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; - isValid = false; - } else { - isValid = this.checkDateTime(field); - } - } - return isValid; - } - - private checkDateTime(field: FormFieldModel): boolean { - let isValid = true; - const fieldValueDate = DateFnsUtils.getDate(field.value); - const subjectFieldDate = DateFnsUtils.getDate(field[this.getSubjectField()]); - - if (this.compareDates(fieldValueDate, subjectFieldDate)) { - field.validationSummary.message = this.getErrorMessage(); - field.validationSummary.attributes.set(this.getSubjectField(), DateFnsUtils.formatDate(subjectFieldDate, field.dateDisplayFormat)); - isValid = false; - } - return isValid; - } - - protected abstract compareDates(fieldValueDate: Date, subjectFieldDate: Date): boolean; - - protected abstract getSubjectField(): string; - - protected abstract getErrorMessage(): string; -} - -/** - * Validates the min constraint for the datetime value. - * - * Notes for developers: - * the format of the min/max values is always the ISO datetime: i.e. 2023-10-01T15:21:00.000Z. - * Min/Max values can be parsed with standard `new Date(value)` calls. - * - */ -export class MinDateTimeFieldValidator extends BoundaryDateTimeFieldValidator { - protected compareDates(fieldValueDate: Date, subjectFieldDate: Date): boolean { - return isBefore(fieldValueDate, subjectFieldDate); - } - - protected getSubjectField(): string { - return 'minValue'; - } - - protected getErrorMessage(): string { - return `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; - } -} - -/** - * Validates the max constraint for the datetime value. - * - * Notes for developers: - * the format of the min/max values is always the ISO datetime: i.e. 2023-10-01T15:21:00.000Z. - * Min/Max values can be parsed with standard `new Date(value)` calls. - * - */ -export class MaxDateTimeFieldValidator extends BoundaryDateTimeFieldValidator { - protected compareDates(fieldValueDate: Date, subjectFieldDate: Date): boolean { - return isAfter(fieldValueDate, subjectFieldDate); - } - - protected getSubjectField(): string { - return 'maxValue'; - } - - protected getErrorMessage(): string { - return `FORM.FIELD.VALIDATOR.NOT_GREATER_THAN`; - } -} - export class MinLengthFieldValidator implements FormFieldValidator { private supportedTypes = [FormFieldTypes.TEXT, FormFieldTypes.MULTILINE_TEXT]; @@ -523,12 +317,6 @@ export const FORM_FIELD_VALIDATORS = [ new MinValueFieldValidator(), new MaxValueFieldValidator(), new RegExFieldValidator(), - new DateFieldValidator(), - new DateTimeFieldValidator(), - new MinDateFieldValidator(), - new MaxDateFieldValidator(), new FixedValueFieldValidator(), - new MinDateTimeFieldValidator(), - new MaxDateTimeFieldValidator(), new DecimalFieldValidator() ]; diff --git a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts index 2ce1740999e..e32b3e93bd5 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts @@ -143,6 +143,10 @@ export class FormFieldModel extends FormWidgetModel { this._isValid = false; } + markAsValid() { + this._isValid = true; + } + validate(): boolean { this.validationSummary = new ErrorMessageModel(); diff --git a/lib/core/src/lib/form/components/widgets/core/form.model.ts b/lib/core/src/lib/form/components/widgets/core/form.model.ts index 4c32b15dd7e..b77a825aa91 100644 --- a/lib/core/src/lib/form/components/widgets/core/form.model.ts +++ b/lib/core/src/lib/form/components/widgets/core/form.model.ts @@ -148,13 +148,13 @@ export class FormModel implements ProcessFormModel { validateForm(): void { const validateFormEvent: any = new ValidateFormEvent(this); - const errorsField: FormFieldModel[] = []; - - for (let i = 0; i < this.fieldsCache.length; i++) { - if (!this.fieldsCache[i].validate()) { - errorsField.push(this.fieldsCache[i]); + const errorsField: FormFieldModel[] = this.fieldsCache.filter((field) => { + if (!FormFieldTypes.isReactiveType(field.type)) { + return !field.validate(); + } else { + return field.validationSummary.isActive(); } - } + }); this.isValid = errorsField.length <= 0; @@ -191,7 +191,7 @@ export class FormModel implements ProcessFormModel { return; } - if (!field.validate()) { + if (!FormFieldTypes.isReactiveType(field.type) && !field.validate()) { this.markAsInvalid(); } diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.html b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.html index 28b47585666..7f0efa81018 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.html +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.html @@ -1,6 +1,6 @@
diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts index aa5ad983f55..319a4568f76 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.spec.ts @@ -22,12 +22,10 @@ import { DateTimeWidgetComponent } from './date-time.widget'; import { TranslateModule } from '@ngx-translate/core'; import { MatTooltipModule } from '@angular/material/tooltip'; import { FormFieldTypes } from '../core/form-field-types'; -import { DateFieldValidator, DateTimeFieldValidator } from '../core'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { MatInputHarness } from '@angular/material/input/testing'; import { addMinutes } from 'date-fns'; -import { HttpClientModule } from '@angular/common/http'; import { MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core'; @@ -35,6 +33,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatButtonModule } from '@angular/material/button'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('DateTimeWidgetComponent', () => { let loader: HarnessLoader; @@ -47,7 +46,7 @@ describe('DateTimeWidgetComponent', () => { TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot(), - HttpClientModule, + HttpClientTestingModule, NoopAnimationsModule, MatDialogModule, MatMenuModule, @@ -65,7 +64,6 @@ describe('DateTimeWidgetComponent', () => { widget = fixture.componentInstance; form = new FormModel(); - form.fieldValidators = [new DateFieldValidator(), new DateTimeFieldValidator()]; loader = TestbedHarnessEnvironment.loader(fixture); }); @@ -74,17 +72,16 @@ describe('DateTimeWidgetComponent', () => { TestBed.resetTestingModule(); }); - it('should setup min value for date picker', async () => { + it('should setup min value for date picker', () => { const minValue = '1982-03-13T10:00:00Z'; widget.field = new FormFieldModel(form, { id: 'date-id', name: 'date-name', - type: 'datetime', + type: FormFieldTypes.DATETIME, minValue }); fixture.detectChanges(); - await fixture.whenStable(); expect(widget.minDate.toISOString()).toBe(`1982-03-13T10:00:00.000Z`); }); @@ -93,7 +90,7 @@ describe('DateTimeWidgetComponent', () => { widget.field = new FormFieldModel(form, { id: 'date-id', name: 'date-name', - type: 'datetime' + type: FormFieldTypes.DATETIME }); fixture.detectChanges(); @@ -101,13 +98,12 @@ describe('DateTimeWidgetComponent', () => { expect(element.querySelector('#data-time-widget')).not.toBeNull(); }); - it('should setup max value for date picker', async () => { + it('should setup max value for date picker', () => { const maxValue = '1982-03-13T10:00:00Z'; widget.field = new FormFieldModel(null, { maxValue }); fixture.detectChanges(); - await fixture.whenStable(); expect(widget.maxDate.toISOString()).toBe('1982-03-13T10:00:00.000Z'); }); @@ -119,76 +115,70 @@ describe('DateTimeWidgetComponent', () => { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; - widget.onDateChanged({ value: new Date('1982-03-13T10:00:00.000Z') } as any); + + fixture.detectChanges(); + + widget.datetimeInputControl.setValue(new Date('1982-03-13T10:00:00.000Z')); expect(widget.onFieldChanged).toHaveBeenCalledWith(field); }); - it('should validate the initial datetime value', async () => { + it('should validate the initial datetime value', () => { const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; - fixture.whenStable(); - await fixture.whenStable(); + fixture.detectChanges(); expect(field.isValid).toBeTrue(); }); - it('should validate the updated datetime value', async () => { + it('should validate the updated datetime value', () => { const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; - fixture.whenStable(); - await fixture.whenStable(); + fixture.detectChanges(); let expectedDate = new Date('9999-09-12T09:10:00.000Z'); expectedDate = addMinutes(expectedDate, expectedDate.getTimezoneOffset()); - widget.onDateChanged({ value: expectedDate } as any); + widget.datetimeInputControl.setValue(expectedDate); - expect(field.value).toBe('9999-09-12T09:10:00.000Z'); + expect(field.value).toEqual(new Date('9999-09-12T09:10:00.000Z')); expect(field.isValid).toBeTrue(); }); - it('should forwad the incorrect datetime input for further validation', async () => { + it('should forwad the incorrect datetime input for further validation', () => { const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; fixture.detectChanges(); - await fixture.whenStable(); - widget.onDateChanged({ - value: null, - targetElement: { - value: '123abc' - } - } as any); + widget.datetimeInputControl.setValue(new Date('123abc')); fixture.detectChanges(); - await fixture.whenStable(); - expect(field.value).toBe('123abc'); + expect(widget.datetimeInputControl.invalid).toBeTrue(); expect(field.isValid).toBeFalse(); expect(field.validationSummary.message).toBe('D-M-YYYY hh:mm A'); }); @@ -198,7 +188,7 @@ describe('DateTimeWidgetComponent', () => { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; @@ -206,35 +196,31 @@ describe('DateTimeWidgetComponent', () => { fixture.whenStable(); await fixture.whenStable(); - widget.onValueChanged({ target: { value: '9999-09-12T09:10:00.000Z' } } as any); + const input = await loader.getHarness(MatInputHarness); + await input.setValue('9999-09-12T09:10:00.000Z'); - expect(field.value).toBe('9999-09-12T09:10:00.000Z'); + expect(field.value).toEqual(new Date('9999-09-12T09:10:00.000Z')); expect(field.isValid).toBeTrue(); }); - it('should fail validating incorrect keyboard input', async () => { + it('should fail validating incorrect keyboard input', () => { const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; fixture.detectChanges(); - await fixture.whenStable(); - - widget.onValueChanged({ - target: { - value: '123abc' - } - } as any); - fixture.detectChanges(); - await fixture.whenStable(); + const dateTimeInput = fixture.nativeElement.querySelector('input'); + dateTimeInput.value = '123abc'; + dateTimeInput.dispatchEvent(new Event('input')); - expect(field.value).toBe('123abc'); + expect(widget.datetimeInputControl.invalid).toBeTrue(); + expect(field.value).toBe(null); expect(field.isValid).toBeFalse(); expect(field.validationSummary.message).toBe('D-M-YYYY hh:mm A'); }); @@ -244,7 +230,7 @@ describe('DateTimeWidgetComponent', () => { id: 'date-field-id', name: 'date-name', value: '9999-09-12T09:00:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); widget.field = field; @@ -252,9 +238,12 @@ describe('DateTimeWidgetComponent', () => { fixture.whenStable(); await fixture.whenStable(); - widget.onDateChanged({ value: null, targetElement: { value: '' } } as any); + const input = await loader.getHarness(MatInputHarness); + await input.setValue(null); - expect(field.value).toBe(''); + expect(widget.datetimeInputControl.value).toBe(null); + expect(widget.datetimeInputControl.valid).toBeTrue(); + expect(field.value).toBe(null); expect(field.isValid).toBeTrue(); }); @@ -282,6 +271,7 @@ describe('DateTimeWidgetComponent', () => { type: FormFieldTypes.DATETIME, required: true }); + fixture.detectChanges(); }); it('should be marked as invalid after interaction', () => { @@ -296,8 +286,6 @@ describe('DateTimeWidgetComponent', () => { }); it('should be able to display label with asterisk', () => { - fixture.detectChanges(); - const asterisk = element.querySelector('.adf-asterisk'); expect(asterisk).not.toBeNull(); @@ -311,7 +299,7 @@ describe('DateTimeWidgetComponent', () => { id: 'date-field-id', name: 'date-name', value: '9999-11-30T10:30:00.000Z', - type: 'datetime' + type: FormFieldTypes.DATETIME }); fixture.detectChanges(); @@ -327,7 +315,7 @@ describe('DateTimeWidgetComponent', () => { name: 'date-name', value: '9999-12-30T10:30:00.000Z', dateDisplayFormat: 'MM-DD-YYYY HH:mm A', - type: 'datetime' + type: FormFieldTypes.DATETIME }); fixture.detectChanges(); @@ -343,7 +331,7 @@ describe('DateTimeWidgetComponent', () => { name: 'date-name', value: '9999-12-30T10:30:00.000Z', dateDisplayFormat: 'MM-DD-YYYY HH:mm A', - type: 'datetime' + type: FormFieldTypes.DATETIME }); fixture.detectChanges(); @@ -365,7 +353,7 @@ describe('DateTimeWidgetComponent', () => { id: 'date-field-id', name: 'datetime-field-name', value: '9999-12-30T10:30:00.000Z', - type: 'datetime', + type: FormFieldTypes.DATETIME, dateDisplayFormat: 'MM-DD-YYYY HH:mm A' }); widget.field = field; @@ -385,6 +373,31 @@ describe('DateTimeWidgetComponent', () => { expect(await input.getValue()).toBe('03-02-2020 00:00 AM'); }); + it('should display value with specified format when format of provided datetime is different', async () => { + const field = new FormFieldModel(form, { + id: 'date-field-id', + name: 'datetime-field-name', + value: '9999-12-30T10:30:00.000Z', + type: FormFieldTypes.DATETIME, + dateDisplayFormat: 'MM/DD/YYYY HH;mm A' + }); + widget.field = field; + + fixture.detectChanges(); + await fixture.whenStable(); + + const input = await loader.getHarness(MatInputHarness); + expect(await input.getValue()).toBe('12/30/9999 10;30 AM'); + + widget.field.value = '2020-03-02T00:00:00.000Z'; + + fixture.componentInstance.ngOnInit(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(await input.getValue()).toBe('03/02/2020 00;00 AM'); + }); + describe('when form model has left labels', () => { it('should have left labels classes on leftLabels true', () => { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: true }), { diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts index 8954d265b1b..a76849b45e4 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts @@ -18,18 +18,20 @@ /* eslint-disable @angular-eslint/component-selector */ import { NgIf } from '@angular/common'; -import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerInputEvent, MatDatetimepickerModule } from '@mat-datetimepicker/core'; +import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerModule } from '@mat-datetimepicker/core'; import { TranslateModule } from '@ngx-translate/core'; -import { isValid } from 'date-fns'; import { ADF_DATE_FORMATS, ADF_DATETIME_FORMATS, AdfDateFnsAdapter, AdfDateTimeFnsAdapter, DateFnsUtils } from '../../../../common'; import { FormService } from '../../../services/form.service'; import { ErrorWidgetComponent } from '../error/error.component'; import { WidgetComponent } from '../widget.component'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { ErrorMessageModel } from '../core/error-message.model'; @Component({ selector: 'date-time-widget', @@ -42,68 +44,116 @@ import { WidgetComponent } from '../widget.component'; ], templateUrl: './date-time.widget.html', styleUrls: ['./date-time.widget.scss'], - imports: [NgIf, TranslateModule, MatFormFieldModule, MatInputModule, MatDatetimepickerModule, FormsModule, ErrorWidgetComponent], + imports: [NgIf, TranslateModule, MatFormFieldModule, MatInputModule, MatDatetimepickerModule, ReactiveFormsModule, ErrorWidgetComponent], encapsulation: ViewEncapsulation.None }) -export class DateTimeWidgetComponent extends WidgetComponent implements OnInit { +export class DateTimeWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { minDate: Date; maxDate: Date; + datetimeInputControl: FormControl = new FormControl(null); - @Input() - value: any = null; + private onDestroy$ = new Subject(); - constructor(public formService: FormService, private dateAdapter: DateAdapter, private dateTimeAdapter: DatetimeAdapter) { - super(formService); + public readonly formService = inject(FormService); + private readonly dateAdapter = inject(DateAdapter); + private readonly dateTimeAdapter = inject(DatetimeAdapter); + + ngOnInit(): void { + this.patchFormControl(); + this.initDateAdapter(); + this.initDateRange(); + this.subscribeToDateChanges(); + this.updateField(); } - ngOnInit() { - if (this.field.dateDisplayFormat) { - const dateAdapter = this.dateAdapter as AdfDateFnsAdapter; - dateAdapter.displayFormat = this.field.dateDisplayFormat; + updateField(): void { + this.validateField(); + this.onFieldChanged(this.field); + } - const dateTimeAdapter = this.dateTimeAdapter as AdfDateTimeFnsAdapter; - dateTimeAdapter.displayFormat = this.field.dateDisplayFormat; + private patchFormControl(): void { + this.datetimeInputControl.setValue(this.field.value, { emitEvent: false }); + this.datetimeInputControl.setValidators(this.isRequired() ? [Validators.required] : []); + if (this.field?.readOnly || this.readOnly) { + this.datetimeInputControl.disable({ emitEvent: false }); } - if (this.field) { - if (this.field.minValue) { - this.minDate = DateFnsUtils.getDate(this.field.minValue); - } + this.datetimeInputControl.updateValueAndValidity({ emitEvent: false }); + } - if (this.field.maxValue) { - this.maxDate = DateFnsUtils.getDate(this.field.maxValue); - } + private subscribeToDateChanges(): void { + this.datetimeInputControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newDate: Date) => { + this.field.value = newDate; + this.updateField(); + }); + } + + private validateField(): void { + if (this.datetimeInputControl?.invalid) { + this.handleErrors(this.datetimeInputControl.errors); + this.field.markAsInvalid(); + } else { + this.resetErrors(); + this.field.markAsValid(); + } + } - if (this.field.value) { - this.value = DateFnsUtils.getDate(this.field.value); + private handleErrors(errors: ValidationErrors): void { + const errorAttributes = new Map(); + switch (true) { + case !!errors.matDatepickerParse: + this.updateValidationSummary(this.field.dateDisplayFormat || this.field.defaultDateTimeFormat); + break; + case !!errors.required: + this.updateValidationSummary('FORM.FIELD.REQUIRED'); + break; + case !!errors.matDatepickerMin: { + const minValue = DateFnsUtils.formatDate(errors.matDatepickerMin.min, this.field.dateDisplayFormat).toLocaleUpperCase(); + errorAttributes.set('minValue', minValue); + this.updateValidationSummary('FORM.FIELD.VALIDATOR.NOT_LESS_THAN', errorAttributes); + break; + } + case !!errors.matDatepickerMax: { + const maxValue = DateFnsUtils.formatDate(errors.matDatepickerMax.max, this.field.dateDisplayFormat).toLocaleUpperCase(); + errorAttributes.set('maxValue', maxValue); + this.updateValidationSummary('FORM.FIELD.VALIDATOR.NOT_GREATER_THAN', errorAttributes); + break; } + default: + break; } } - onValueChanged(event: Event) { - const input = event.target as HTMLInputElement; - const newValue = this.dateTimeAdapter.parse(input.value, this.field.dateDisplayFormat); + private updateValidationSummary(message: string, attributes?: Map): void { + this.field.validationSummary = new ErrorMessageModel({ message, attributes }); + } - if (isValid(newValue)) { - this.field.value = newValue.toISOString(); - } else { - this.field.value = input.value; - } + private resetErrors(): void { + this.updateValidationSummary(''); + } - this.value = DateFnsUtils.getDate(this.field.value); - this.onFieldChanged(this.field); + private initDateAdapter(): void { + if (this.field?.dateDisplayFormat) { + const dateAdapter = this.dateAdapter as AdfDateFnsAdapter; + dateAdapter.displayFormat = this.field.dateDisplayFormat; + + const dateTimeAdapter = this.dateTimeAdapter as AdfDateTimeFnsAdapter; + dateTimeAdapter.displayFormat = this.field.dateDisplayFormat; + } } - onDateChanged(event: MatDatetimepickerInputEvent) { - const newValue = event.value; - const input = event.targetElement as HTMLInputElement; + private initDateRange(): void { + if (this.field?.minValue) { + this.minDate = DateFnsUtils.getDate(this.field.minValue); + } - if (newValue && isValid(newValue)) { - this.field.value = newValue.toISOString(); - } else { - this.field.value = input.value; + if (this.field?.maxValue) { + this.maxDate = DateFnsUtils.getDate(this.field.maxValue); } + } - this.onFieldChanged(this.field); + ngOnDestroy(): void { + this.onDestroy$.next(); + this.onDestroy$.complete(); } } diff --git a/lib/core/src/lib/form/components/widgets/date/date.widget.html b/lib/core/src/lib/form/components/widgets/date/date.widget.html index 5b05f452496..728b27ecf85 100644 --- a/lib/core/src/lib/form/components/widgets/date/date.widget.html +++ b/lib/core/src/lib/form/components/widgets/date/date.widget.html @@ -1,4 +1,4 @@ -
+
+ (blur)="updateField()"> - - +
diff --git a/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts b/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts index 5719cd6121e..a84b6b24a0a 100644 --- a/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts +++ b/lib/core/src/lib/form/components/widgets/date/date.widget.spec.ts @@ -18,8 +18,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DateAdapter } from '@angular/material/core'; import { CoreTestingModule } from '../../../../testing'; -import { DateFieldValidator, FormFieldModel, FormFieldTypes, FormModel, MaxDateFieldValidator, MinDateFieldValidator } from '../core'; +import { FormFieldModel, FormFieldTypes, FormModel } from '../core'; import { DateWidgetComponent } from './date.widget'; +import { DEFAULT_DATE_FORMAT } from '../../../../common'; +import { isEqual } from 'date-fns'; describe('DateWidgetComponent', () => { let widget: DateWidgetComponent; @@ -34,7 +36,6 @@ describe('DateWidgetComponent', () => { }); form = new FormModel(); - form.fieldValidators = [new DateFieldValidator(), new MinDateFieldValidator(), new MaxDateFieldValidator()]; fixture = TestBed.createComponent(DateWidgetComponent); adapter = fixture.debugElement.injector.get(DateAdapter); @@ -54,8 +55,8 @@ describe('DateWidgetComponent', () => { }); it('should setup min value for date picker', () => { - const minValue = '13-03-1982'; - widget.field = new FormFieldModel(form, { + const minValue = '1982-03-13'; + widget.field = new FormFieldModel(null, { id: 'date-id', name: 'date-name', minValue @@ -63,12 +64,12 @@ describe('DateWidgetComponent', () => { widget.ngOnInit(); - const expected = adapter.parse(minValue, widget.DATE_FORMAT) as Date; - expect(adapter.compareDate(widget.minDate, expected)).toBe(0); + const expected = adapter.parse(minValue, DEFAULT_DATE_FORMAT); + expect(isEqual(widget.minDate, expected)).toBeTrue(); }); - it('should validate min date value constraint', async () => { - const minValue = '13-03-1982'; + it('should validate min date value constraint', () => { + const minValue = '1982-03-13'; const field = new FormFieldModel(form, { id: 'date-id', @@ -79,22 +80,19 @@ describe('DateWidgetComponent', () => { }); widget.field = field; - widget.ngOnInit(); + fixture.detectChanges(); - widget.onDateChange({ - value: new Date('1982/03/12') - } as any); + widget.dateInputControl.setValue(new Date('1982/03/12')); fixture.detectChanges(); - await fixture.whenStable(); expect(widget.field.isValid).toBeFalsy(); expect(field.validationSummary.message).toBe('FORM.FIELD.VALIDATOR.NOT_LESS_THAN'); expect(field.validationSummary.attributes.get('minValue')).toBe('13-03-1982'); }); - it('should validate max date value constraint', async () => { - const maxValue = '13-03-1982'; + it('should validate max date value constraint', () => { + const maxValue = '1982-03-13'; const field = new FormFieldModel(form, { id: 'date-id', @@ -105,14 +103,11 @@ describe('DateWidgetComponent', () => { }); widget.field = field; - widget.ngOnInit(); + fixture.detectChanges(); - widget.onDateChange({ - value: new Date('2023/03/13') - } as any); + widget.dateInputControl.setValue(new Date('2023/03/13')); fixture.detectChanges(); - await fixture.whenStable(); expect(widget.field.isValid).toBeFalsy(); expect(field.validationSummary.message).toBe('FORM.FIELD.VALIDATOR.NOT_GREATER_THAN'); @@ -132,13 +127,13 @@ describe('DateWidgetComponent', () => { }); it('should setup max value for date picker', () => { - const maxValue = '31-03-1982'; + const maxValue = '1982-03-31'; widget.field = new FormFieldModel(form, { maxValue }); - widget.ngOnInit(); + fixture.detectChanges(); - const expected = adapter.parse(maxValue, widget.DATE_FORMAT) as Date; + const expected = adapter.parse(maxValue, DEFAULT_DATE_FORMAT) as Date; expect(adapter.compareDate(widget.maxDate, expected)).toBe(0); }); @@ -153,9 +148,10 @@ describe('DateWidgetComponent', () => { }); widget.field = field; - widget.onDateChange({ - value: new Date('12/12/2012') - } as any); + + fixture.detectChanges(); + + widget.dateInputControl.setValue(new Date('12/12/2012')); expect(widget.onFieldChanged).toHaveBeenCalledWith(field); }); @@ -187,16 +183,15 @@ describe('DateWidgetComponent', () => { TestBed.resetTestingModule(); }); - it('should show visible date widget', async () => { + it('should show visible date widget', () => { widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - value: '9-9-9999', - type: 'date' + value: new Date('9-9-9999'), + type: FormFieldTypes.DATE }); fixture.detectChanges(); - await fixture.whenStable(); const dateElement = element.querySelector('#date-field-id'); expect(dateElement).not.toBeNull(); @@ -204,30 +199,20 @@ describe('DateWidgetComponent', () => { expect(dateElement?.value).toContain('9-9-9999'); }); - it('[C310335] - Should be able to change display format for Date widget', async () => { + it('[C310335] - Should be able to change display format for Date widget', () => { widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - value: '30-12-9999', - type: 'date', - dateDisplayFormat: 'MM-DD-YYYY' + value: new Date('12-30-9999'), + type: FormFieldTypes.DATE, + dateDisplayFormat: 'dd.MM.yyyy' }); fixture.detectChanges(); - await fixture.whenStable(); - let dateElement = element.querySelector('#date-field-id'); - expect(dateElement?.value).toContain('12-30-9999'); - - widget.field.value = '05-06-2019'; - widget.field.dateDisplayFormat = 'DD.MM.YYYY'; - - fixture.componentInstance.ngOnInit(); - fixture.detectChanges(); - await fixture.whenStable(); dateElement = element.querySelector('#date-field-id'); - expect(dateElement?.value).toContain('05.06.2019'); + expect(dateElement?.value).toContain('30.12.9999'); }); it('should disable date button when is readonly', () => { @@ -235,7 +220,7 @@ describe('DateWidgetComponent', () => { id: 'date-field-id', name: 'date-name', value: '9-9-9999', - type: 'date' + type: FormFieldTypes.DATE }); fixture.detectChanges(); @@ -257,41 +242,44 @@ describe('DateWidgetComponent', () => { id: 'date-field-id', name: 'date-name', value: 'aa', - type: 'date', + type: FormFieldTypes.DATE, readOnly: 'false' }); widget.field.isVisible = true; widget.field.readOnly = false; fixture.detectChanges(); + widget.dateInputControl.setValue(new Date('invalid date')); + + fixture.detectChanges(); + expect(widget.field.isValid).toBeFalsy(); }); }); - it('should display always the json value', async () => { + it('should display always the json value', () => { const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - value: '30-12-9999', - type: 'date', - dateDisplayFormat: 'MM-DD-YYYY' + value: new Date('12-30-9999'), + type: FormFieldTypes.DATE, + dateDisplayFormat: 'MM-dd-yyyy' }); widget.field = field; fixture.detectChanges(); - await fixture.whenStable(); const dateElement = element.querySelector('#date-field-id'); expect(dateElement).toBeDefined(); expect(dateElement.value).toContain('12-30-9999'); - widget.field.value = '03-02-2020'; + dateElement.value = '03-02-2020'; + dateElement.dispatchEvent(new Event('input')); fixture.componentInstance.ngOnInit(); fixture.detectChanges(); - await fixture.whenStable(); - expect(dateElement.value).toContain('02-03-2020'); + expect(dateElement.value).toContain('03-02-2020'); }); }); diff --git a/lib/core/src/lib/form/components/widgets/date/date.widget.ts b/lib/core/src/lib/form/components/widgets/date/date.widget.ts index b8342ccdff6..918d44b2d23 100644 --- a/lib/core/src/lib/form/components/widgets/date/date.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date/date.widget.ts @@ -18,18 +18,21 @@ /* eslint-disable @angular-eslint/component-selector */ import { NgIf } from '@angular/common'; -import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { Component, inject, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import { MatDatepickerInputEvent, MatDatepickerModule } from '@angular/material/datepicker'; +import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { TranslateModule } from '@ngx-translate/core'; import { Subject } from 'rxjs'; -import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '../../../../common'; +import { takeUntil } from 'rxjs/operators'; +import { ADF_DATE_FORMATS, AdfDateFnsAdapter, DateFnsUtils, DEFAULT_DATE_FORMAT } from '../../../../common'; import { FormService } from '../../../services/form.service'; import { ErrorWidgetComponent } from '../error/error.component'; import { WidgetComponent } from '../widget.component'; +import { ErrorMessageModel } from '../core/error-message.model'; +import { parseISO } from 'date-fns'; @Component({ selector: 'date-widget', @@ -50,62 +53,120 @@ import { WidgetComponent } from '../widget.component'; '(invalid)': 'event($event)', '(select)': 'event($event)' }, - imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, FormsModule, ErrorWidgetComponent, NgIf], + imports: [MatFormFieldModule, TranslateModule, MatInputModule, MatDatepickerModule, ReactiveFormsModule, ErrorWidgetComponent, NgIf], encapsulation: ViewEncapsulation.None }) export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { - DATE_FORMAT = 'dd-MM-yyyy'; - minDate: Date; maxDate: Date; startAt: Date; - @Input() - value: any = null; + dateInputControl: FormControl = new FormControl(null); + + private onDestroy$ = new Subject(); - private onDestroy$ = new Subject(); + public readonly formService = inject(FormService); + private readonly dateAdapter = inject(DateAdapter); - constructor(public formService: FormService, private dateAdapter: DateAdapter) { - super(formService); + ngOnInit(): void { + this.patchFormControl(); + this.initDateAdapter(); + this.initDateRange(); + this.initStartAt(); + this.subscribeToDateChanges(); + this.updateField(); } - ngOnInit() { - if (this.field.dateDisplayFormat) { - const adapter = this.dateAdapter as AdfDateFnsAdapter; - adapter.displayFormat = this.field.dateDisplayFormat; + updateField(): void { + this.validateField(); + this.onFieldChanged(this.field); + } + private patchFormControl(): void { + this.dateInputControl.setValue(this.field.value, { emitEvent: false }); + this.dateInputControl.setValidators(this.isRequired() ? [Validators.required] : []); + if (this.field?.readOnly || this.readOnly) { + this.dateInputControl.disable({ emitEvent: false }); } - if (this.field) { - if (this.field.minValue) { - this.minDate = this.dateAdapter.parse(this.field.minValue, this.DATE_FORMAT); - } + this.dateInputControl.updateValueAndValidity({ emitEvent: false }); + } - if (this.field.maxValue) { - this.maxDate = this.dateAdapter.parse(this.field.maxValue, this.DATE_FORMAT); - } + private subscribeToDateChanges(): void { + this.dateInputControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newDate: Date) => { + this.field.value = newDate; + this.updateField(); + }); + } + + private validateField(): void { + if (this.dateInputControl.invalid) { + this.handleErrors(this.dateInputControl.errors); + this.field.markAsInvalid(); + } else { + this.resetErrors(); + this.field.markAsValid(); + } + } - if (this.field.value) { - this.startAt = this.dateAdapter.parse(this.field.value, this.DATE_FORMAT); - this.value = this.dateAdapter.parse(this.field.value, this.DATE_FORMAT); + private handleErrors(errors: ValidationErrors): void { + const errorAttributes = new Map(); + switch (true) { + case !!errors.matDatepickerParse: + this.updateValidationSummary(this.field.dateDisplayFormat || this.field.defaultDateTimeFormat); + break; + case !!errors.required: + this.updateValidationSummary('FORM.FIELD.REQUIRED'); + break; + case !!errors.matDatepickerMin: { + const minValue = DateFnsUtils.formatDate(errors.matDatepickerMin.min, this.field.dateDisplayFormat).toLocaleUpperCase(); + errorAttributes.set('minValue', minValue); + this.updateValidationSummary('FORM.FIELD.VALIDATOR.NOT_LESS_THAN', errorAttributes); + break; } + case !!errors.matDatepickerMax: { + const maxValue = DateFnsUtils.formatDate(errors.matDatepickerMax.max, this.field.dateDisplayFormat).toLocaleUpperCase(); + errorAttributes.set('maxValue', maxValue); + this.updateValidationSummary('FORM.FIELD.VALIDATOR.NOT_GREATER_THAN', errorAttributes); + break; + } + default: + break; } } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + private updateValidationSummary(message: string, attributes?: Map): void { + this.field.validationSummary = new ErrorMessageModel({ message, attributes }); } - onDateChange(event: MatDatepickerInputEvent) { - const value = event.value; - const input = event.targetElement as HTMLInputElement; + private resetErrors(): void { + this.updateValidationSummary(''); + } - if (value) { - this.field.value = this.dateAdapter.format(value, this.DATE_FORMAT); - } else { - this.field.value = input.value; + private initDateAdapter(): void { + if (this.field?.dateDisplayFormat) { + const adapter = this.dateAdapter as AdfDateFnsAdapter; + adapter.displayFormat = this.field.dateDisplayFormat; } + } - this.onFieldChanged(this.field); + private initDateRange(): void { + if (this.field?.minValue) { + this.minDate = parseISO(this.field.minValue); + } + + if (this.field?.maxValue) { + this.maxDate = parseISO(this.field.maxValue); + } + } + + private initStartAt(): void { + if (this.field?.value) { + this.startAt = this.dateAdapter.parse(this.field.value, DEFAULT_DATE_FORMAT); + } + } + + ngOnDestroy(): void { + this.onDestroy$.next(); + this.onDestroy$.complete(); } } diff --git a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts index 7b75a23ad1a..482f82ff48d 100644 --- a/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/form-cloud.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, HostListener, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy, HostListener, OnInit, ChangeDetectorRef } from '@angular/core'; import { Observable, of, forkJoin, Subject, Subscription } from 'rxjs'; import { switchMap, takeUntil, map, filter } from 'rxjs/operators'; import { @@ -130,7 +130,8 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, private dialog: MatDialog, protected visibilityService: WidgetVisibilityService, private readonly displayModeService: DisplayModeService, - private spinnerService: FormCloudSpinnerService + private spinnerService: FormCloudSpinnerService, + private readonly changeDetector: ChangeDetectorRef ) { super(); @@ -421,6 +422,7 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges, this.displayModeOn.emit(this.displayModeService.findConfiguration(this.displayMode, this.displayModeConfigurations)); } + this.changeDetector.detectChanges(); this.formLoaded.emit(form); } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.html b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.html index d6d4b8817a1..03818c514e1 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.html +++ b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.html @@ -1,4 +1,4 @@ -
+
@@ -7,24 +7,21 @@ - + (blur)="updateField()"> - - +
diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts index 6d2c8ddae87..e56c36e501f 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.spec.ts @@ -17,7 +17,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DateCloudWidgetComponent } from './date-cloud.widget'; -import { FormFieldModel, FormModel, FormFieldTypes, DateFieldValidator, MinDateFieldValidator, MaxDateFieldValidator } from '@alfresco/adf-core'; +import { FormFieldModel, FormModel, FormFieldTypes, DEFAULT_DATE_FORMAT } from '@alfresco/adf-core'; import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; import { DateAdapter } from '@angular/material/core'; import { isEqual, subDays, addDays } from 'date-fns'; @@ -35,7 +35,6 @@ describe('DateWidgetComponent', () => { }); form = new FormModel(); - form.fieldValidators = [new DateFieldValidator(), new MinDateFieldValidator(), new MaxDateFieldValidator()]; fixture = TestBed.createComponent(DateCloudWidgetComponent); adapter = fixture.debugElement.injector.get(DateAdapter); @@ -52,9 +51,9 @@ describe('DateWidgetComponent', () => { minValue }); - widget.ngOnInit(); + fixture.detectChanges(); - const expected = adapter.parse(minValue, widget.DATE_FORMAT); + const expected = adapter.parse(minValue, DEFAULT_DATE_FORMAT); expect(isEqual(widget.minDate, expected)).toBeTrue(); }); @@ -77,9 +76,9 @@ describe('DateWidgetComponent', () => { type: FormFieldTypes.DATE, maxValue }); - widget.ngOnInit(); + fixture.detectChanges(); - const expected = adapter.parse(maxValue, widget.DATE_FORMAT); + const expected = adapter.parse(maxValue, DEFAULT_DATE_FORMAT); expect(isEqual(widget.maxDate, expected)).toBeTrue(); }); @@ -95,7 +94,10 @@ describe('DateWidgetComponent', () => { }); widget.field = field; - widget.onDateChanged({ value: adapter.today() } as any); + + fixture.detectChanges(); + + widget.dateInputControl.setValue(new Date('9999-9-9')); expect(widget.onFieldChanged).toHaveBeenCalledWith(field); }); @@ -106,17 +108,15 @@ describe('DateWidgetComponent', () => { TestBed.resetTestingModule(); }); - it('should show visible date widget', async () => { + it('should show visible date widget', () => { widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - // always stored as dd-MM-yyyy value: '9999-9-9', type: FormFieldTypes.DATE }); fixture.detectChanges(); - await fixture.whenStable(); const dateElement = element.querySelector('#date-field-id'); expect(dateElement).not.toBeNull(); @@ -128,10 +128,9 @@ describe('DateWidgetComponent', () => { widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - // always stored as dd-MM-yyyy - value: '30-12-9999', + value: new Date('12-30-9999'), type: FormFieldTypes.DATE, - dateDisplayFormat: 'YYYY-DD-MM' + dateDisplayFormat: 'yyyy-dd-MM' }); fixture.detectChanges(); @@ -141,31 +140,29 @@ describe('DateWidgetComponent', () => { expect(dateElement.value).toContain('9999-30-12'); }); - it('should disable date button when is readonly', async () => { + it('should disable date button when is readonly', () => { widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9999-9-9', type: FormFieldTypes.DATE, - readOnly: 'false' + readOnly: false }); widget.field.isVisible = true; widget.field.readOnly = false; fixture.detectChanges(); - await fixture.whenStable(); let dateButton = element.querySelector('button'); expect(dateButton.disabled).toBeFalsy(); widget.field.readOnly = true; fixture.detectChanges(); - await fixture.whenStable(); dateButton = element.querySelector('button'); expect(dateButton.disabled).toBeTruthy(); }); - it('should set isValid to false when the value is not a correct date value', async () => { + it('should set isValid to false when the value is not a correct date value', () => { widget.field = new FormFieldModel(new FormModel(), { id: 'date-field-id', name: 'date-name', @@ -177,42 +174,43 @@ describe('DateWidgetComponent', () => { widget.field.readOnly = false; fixture.detectChanges(); - await fixture.whenStable(); + + widget.dateInputControl.setValue(new Date('invalid date')); + + fixture.detectChanges(); expect(widget.field.isValid).toBeFalsy(); }); }); - it('should display always the json value', async () => { + it('should display always the json value', () => { const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - // always stored as dd-MM-yyyy - value: '30-12-9999', + value: new Date('12-30-9999'), type: FormFieldTypes.DATE, - readOnly: 'false', - dateDisplayFormat: 'MM-DD-YYYY' + readOnly: false, + dateDisplayFormat: 'MM-dd-yyyy' }); widget.field = field; fixture.detectChanges(); - await fixture.whenStable(); const dateElement = element.querySelector('#date-field-id'); expect(dateElement).toBeDefined(); expect(dateElement.value).toContain('12-30-9999'); - widget.field.value = '03-02-2020'; + dateElement.value = '03-02-2020'; + dateElement.dispatchEvent(new Event('input')); fixture.componentInstance.ngOnInit(); fixture.detectChanges(); - await fixture.whenStable(); - expect(dateElement.value).toContain('02-03-2020'); + expect(dateElement.value).toContain('03-02-2020'); }); describe('when form model has left labels', () => { - it('should have left labels classes on leftLabels true', async () => { + it('should have left labels classes on leftLabels true', () => { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: true }), { id: 'date-id', name: 'date-name', @@ -223,7 +221,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const widgetContainer = element.querySelector('.adf-left-label-input-container'); expect(widgetContainer).not.toBeNull(); @@ -235,7 +232,7 @@ describe('DateWidgetComponent', () => { expect(adfLeftLabel).not.toBeNull(); }); - it('should not have left labels classes on leftLabels false', async () => { + it('should not have left labels classes on leftLabels false', () => { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: false }), { id: 'date-id', name: 'date-name', @@ -246,7 +243,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const widgetContainer = element.querySelector('.adf-left-label-input-container'); expect(widgetContainer).toBeNull(); @@ -258,7 +254,7 @@ describe('DateWidgetComponent', () => { expect(adfLeftLabel).toBeNull(); }); - it('should not have left labels classes on leftLabels not present', async () => { + it('should not have left labels classes on leftLabels not present', () => { widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), { id: 'date-id', name: 'date-name', @@ -269,7 +265,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const widgetContainer = element.querySelector('.adf-left-label-input-container'); expect(widgetContainer).toBeNull(); @@ -291,7 +286,7 @@ describe('DateWidgetComponent', () => { }); describe('Minimum date range value and date', () => { - it('should set minimum date range date to today if minimum date range value is 0', async () => { + it('should set minimum date range date to today if minimum date range value is 0', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -300,7 +295,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const expectedMinDate = adapter.today(); @@ -308,7 +302,7 @@ describe('DateWidgetComponent', () => { expect(widget.field.minValue).toBe(todayString); }); - it('should set minimum date range date to null if minimum date range value is null', async () => { + it('should set minimum date range date to null if minimum date range value is null', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -317,13 +311,12 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); expect(widget.minDate).toBeNull(); expect(widget.field.minValue).toBeNull(); }); - it('should set minimum date range date to today minus abs(minDateRangeValue) if minimum date range value is negative', async () => { + it('should set minimum date range date to today minus abs(minDateRangeValue) if minimum date range value is negative', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -332,7 +325,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const expectedMinDate = subDays(adapter.today(), 2); @@ -340,7 +332,7 @@ describe('DateWidgetComponent', () => { expect(widget.field.minValue).toBe('20-02-2022'); }); - it('should set minimum date range date to today plus minDateRangeValue if minimum date range value is positive', async () => { + it('should set minimum date range date to today plus minDateRangeValue if minimum date range value is positive', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -349,7 +341,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const expectedMinDate = addDays(adapter.today(), 2); @@ -359,7 +350,7 @@ describe('DateWidgetComponent', () => { }); describe('Maximum date range value and date', () => { - it('should set maximum date range date to today if maximum date range value is 0', async () => { + it('should set maximum date range date to today if maximum date range value is 0', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -368,7 +359,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const expectedMaxDate = adapter.today(); @@ -376,7 +366,7 @@ describe('DateWidgetComponent', () => { expect(widget.field.maxValue).toBe(todayString); }); - it('should set maximum date range date to null if maximum date range value is null', async () => { + it('should set maximum date range date to null if maximum date range value is null', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -385,13 +375,12 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); expect(widget.maxDate).toBeNull(); expect(widget.field.maxValue).toBeNull(); }); - it('should set maximum date range date to today minus abs(maxDateRangeValue) if maximum date range value is negative', async () => { + it('should set maximum date range date to today minus abs(maxDateRangeValue) if maximum date range value is negative', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -400,7 +389,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const expectedMaxDate = subDays(adapter.today(), 2); @@ -408,7 +396,7 @@ describe('DateWidgetComponent', () => { expect(widget.field.maxValue).toBe('20-02-2022'); }); - it('should set maximum date range date to today plus maxDateRangeValue if maximum date range value is positive', async () => { + it('should set maximum date range date to today plus maxDateRangeValue if maximum date range value is positive', () => { widget.field = new FormFieldModel(form, { type: FormFieldTypes.DATE, dynamicDateRangeSelection: true, @@ -417,7 +405,6 @@ describe('DateWidgetComponent', () => { }); fixture.detectChanges(); - await fixture.whenStable(); const expectedMaxDate = addDays(adapter.today(), 2); @@ -435,9 +422,8 @@ describe('DateWidgetComponent', () => { }); }); - it('should be able to display label with asterisk', async () => { + it('should be able to display label with asterisk', () => { fixture.detectChanges(); - await fixture.whenStable(); const asterisk: HTMLElement = element.querySelector('.adf-asterisk'); @@ -445,9 +431,8 @@ describe('DateWidgetComponent', () => { expect(asterisk.textContent).toEqual('*'); }); - it('should be invalid after user interaction without typing', async () => { + it('should be invalid after user interaction without typing', () => { fixture.detectChanges(); - await fixture.whenStable(); expect(element.querySelector('.adf-invalid')).toBeFalsy(); @@ -455,7 +440,6 @@ describe('DateWidgetComponent', () => { dateCloudInput.dispatchEvent(new Event('blur')); fixture.detectChanges(); - await fixture.whenStable(); expect(element.querySelector('.adf-invalid')).toBeTruthy(); }); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts index 0f0aceb4a64..b4fcf444527 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/date/date-cloud.widget.ts @@ -17,15 +17,42 @@ /* eslint-disable @angular-eslint/component-selector */ -import { Component, OnInit, ViewEncapsulation, OnDestroy, Input } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation, OnDestroy, inject } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { Subject } from 'rxjs'; -import { WidgetComponent, FormService, AdfDateFnsAdapter, DateFnsUtils, ADF_DATE_FORMATS } from '@alfresco/adf-core'; -import { MatDatepickerInputEvent } from '@angular/material/datepicker'; -import { addDays } from 'date-fns'; +import { takeUntil } from 'rxjs/operators'; +import { + WidgetComponent, + FormService, + AdfDateFnsAdapter, + DateFnsUtils, + ADF_DATE_FORMATS, + ErrorWidgetComponent, + ErrorMessageModel, + DEFAULT_DATE_FORMAT +} from '@alfresco/adf-core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { addDays, parseISO } from 'date-fns'; +import { FormControl, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms'; +import { NgIf } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ selector: 'date-widget', + standalone: true, + imports: [ + NgIf, + TranslateModule, + MatFormFieldModule, + MatInputModule, + MatDatepickerModule, + MatTooltipModule, + ReactiveFormsModule, + ErrorWidgetComponent + ], providers: [ { provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS }, { provide: DateAdapter, useClass: AdfDateFnsAdapter } @@ -47,80 +74,143 @@ import { addDays } from 'date-fns'; }) export class DateCloudWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { typeId = 'DateCloudWidgetComponent'; - readonly DATE_FORMAT = 'dd-MM-yyyy'; minDate: Date = null; maxDate: Date = null; startAt: Date = null; - /** - * Current date value. - * The value is always stored in the format `dd-MM-yyyy`, - * but displayed in the UI component using `dateDisplayFormat` - */ - @Input() - value: any = null; + dateInputControl: FormControl = new FormControl(null); + + private onDestroy$ = new Subject(); - private onDestroy$ = new Subject(); + public readonly formService = inject(FormService); + private readonly dateAdapter = inject(DateAdapter); - constructor(public formService: FormService, private dateAdapter: DateAdapter) { - super(formService); + ngOnInit(): void { + this.patchFormControl(); + this.initDateAdapter(); + this.initRangeSelection(); + this.initStartAt(); + this.subscribeToDateChanges(); + this.updateField(); } - ngOnInit() { - if (this.field.dateDisplayFormat) { - const adapter = this.dateAdapter as AdfDateFnsAdapter; - adapter.displayFormat = this.field.dateDisplayFormat; + updateField(): void { + this.validateField(); + this.onFieldChanged(this.field); + } + + private patchFormControl(): void { + this.dateInputControl.setValue(this.field.value, { emitEvent: false }); + this.dateInputControl.setValidators(this.isRequired() ? [Validators.required] : []); + if (this.field?.readOnly || this.readOnly) { + this.dateInputControl.disable({ emitEvent: false }); } - if (this.field) { - if (this.field.dynamicDateRangeSelection) { - if (this.field.minDateRangeValue === null) { - this.minDate = null; - this.field.minValue = null; - } else { - this.minDate = addDays(this.dateAdapter.today(), this.field.minDateRangeValue); - this.field.minValue = DateFnsUtils.formatDate(this.minDate, this.DATE_FORMAT); - } - if (this.field.maxDateRangeValue === null) { - this.maxDate = null; - this.field.maxValue = null; - } else { - this.maxDate = addDays(this.dateAdapter.today(), this.field.maxDateRangeValue); - this.field.maxValue = DateFnsUtils.formatDate(this.maxDate, this.DATE_FORMAT); - } - } else { - if (this.field.minValue) { - this.minDate = this.dateAdapter.parse(this.field.minValue, this.DATE_FORMAT); - } - - if (this.field.maxValue) { - this.maxDate = this.dateAdapter.parse(this.field.maxValue, this.DATE_FORMAT); - } - } + this.dateInputControl.updateValueAndValidity({ emitEvent: false }); + } + + private subscribeToDateChanges(): void { + this.dateInputControl.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newDate: Date) => { + this.field.value = newDate; + this.updateField(); + }); + } + + private validateField(): void { + if (this.dateInputControl.invalid) { + this.handleErrors(this.dateInputControl.errors); + this.field.markAsInvalid(); + } else { + this.resetErrors(); + this.field.markAsValid(); + } + } - if (this.field.value) { - this.startAt = this.dateAdapter.parse(this.field.value, this.DATE_FORMAT); - this.value = this.dateAdapter.parse(this.field.value, this.DATE_FORMAT); + private handleErrors(errors: ValidationErrors): void { + const errorAttributes = new Map(); + switch (true) { + case !!errors.matDatepickerParse: + this.updateValidationSummary(this.field.dateDisplayFormat || this.field.defaultDateTimeFormat); + break; + case !!errors.required: + this.updateValidationSummary('FORM.FIELD.REQUIRED'); + break; + case !!errors.matDatepickerMin: { + const minValue = DateFnsUtils.formatDate(errors.matDatepickerMin.min, this.field.dateDisplayFormat).toLocaleUpperCase(); + errorAttributes.set('minValue', minValue); + this.updateValidationSummary('FORM.FIELD.VALIDATOR.NOT_LESS_THAN', errorAttributes); + break; } + case !!errors.matDatepickerMax: { + const maxValue = DateFnsUtils.formatDate(errors.matDatepickerMax.max, this.field.dateDisplayFormat).toLocaleUpperCase(); + errorAttributes.set('maxValue', maxValue); + this.updateValidationSummary('FORM.FIELD.VALIDATOR.NOT_GREATER_THAN', errorAttributes); + break; + } + default: + break; } } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); + private updateValidationSummary(message: string, attributes?: Map): void { + this.field.validationSummary = new ErrorMessageModel({ message, attributes }); + } + + private resetErrors(): void { + this.updateValidationSummary(''); + } + + private initDateAdapter(): void { + if (this.field?.dateDisplayFormat) { + const adapter = this.dateAdapter as AdfDateFnsAdapter; + adapter.displayFormat = this.field.dateDisplayFormat; + } } - onDateChanged(event: MatDatepickerInputEvent) { - const value = event.value; - const input = event.targetElement as HTMLInputElement; + private initStartAt(): void { + if (this.field?.value) { + this.startAt = this.dateAdapter.parse(this.field.value, DEFAULT_DATE_FORMAT); + } + } - if (value) { - this.field.value = this.dateAdapter.format(value, this.DATE_FORMAT); + private initRangeSelection(): void { + if (this.field?.dynamicDateRangeSelection) { + this.setDynamicRangeSelection(); } else { - this.field.value = input.value; + this.setStaticRangeSelection(); } + } - this.onFieldChanged(this.field); + private setDynamicRangeSelection(): void { + if (this.field.minDateRangeValue === null) { + this.minDate = null; + this.field.minValue = null; + } else { + this.minDate = addDays(this.dateAdapter.today(), this.field.minDateRangeValue); + this.field.minValue = DateFnsUtils.formatDate(this.minDate, DEFAULT_DATE_FORMAT); + } + if (this.field.maxDateRangeValue === null) { + this.maxDate = null; + this.field.maxValue = null; + } else { + this.maxDate = addDays(this.dateAdapter.today(), this.field.maxDateRangeValue); + this.field.maxValue = DateFnsUtils.formatDate(this.maxDate, DEFAULT_DATE_FORMAT); + } + } + + private setStaticRangeSelection(): void { + if (this.field?.minValue) { + this.minDate = parseISO(this.field.minValue); + } + + if (this.field?.maxValue) { + this.maxDate = parseISO(this.field.maxValue); + } + } + + ngOnDestroy(): void { + this.onDestroy$.next(); + this.onDestroy$.complete(); } } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.scss b/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.scss index 99f853677d7..7b5ae54e8c6 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.scss +++ b/lib/process-services-cloud/src/lib/form/components/widgets/radio-buttons/radio-buttons-cloud.widget.scss @@ -43,9 +43,8 @@ margin: 5px; label { - span { - white-space: normal; - } + white-space: normal; + word-break: break-word; } } diff --git a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts index e85dd31fe6b..e37e1a16b0d 100644 --- a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts @@ -29,8 +29,6 @@ import { CONTENT_UPLOAD_DIRECTIVES, ContentNodeSelectorModule } from '@alfresco/adf-content-services'; - -import { DateCloudWidgetComponent } from './components/widgets/date/date-cloud.widget'; import { DropdownCloudWidgetComponent } from './components/widgets/dropdown/dropdown-cloud.widget'; import { GroupCloudWidgetComponent } from './components/widgets/group/group-cloud.widget'; import { PeopleCloudWidgetComponent } from './components/widgets/people/people-cloud.widget'; @@ -78,7 +76,6 @@ import { FormCloudSpinnerService } from './services/spinner/form-cloud-spinner.s DropdownCloudWidgetComponent, RadioButtonsCloudWidgetComponent, AttachFileCloudWidgetComponent, - DateCloudWidgetComponent, PeopleCloudWidgetComponent, GroupCloudWidgetComponent, PropertiesViewerWrapperComponent, @@ -96,7 +93,6 @@ import { FormCloudSpinnerService } from './services/spinner/form-cloud-spinner.s DropdownCloudWidgetComponent, RadioButtonsCloudWidgetComponent, AttachFileCloudWidgetComponent, - DateCloudWidgetComponent, PeopleCloudWidgetComponent, GroupCloudWidgetComponent, PropertiesViewerWidgetComponent, diff --git a/package-lock.json b/package-lock.json index 7e7bad79b35..344f40aa40f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -150,7 +150,6 @@ "sass-loader": "13.3.2", "selenium-webdriver": "^4.14.0", "shelljs": "^0.8.5", - "shx": "^0.3.4", "spdx-license-list": "^6.9.0", "stylelint": "^16.3.1", "stylelint-config-standard-scss": "^13.1.0", @@ -32544,22 +32543,6 @@ "node": "*" } }, - "node_modules/shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "dev": true, - "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", diff --git a/package.json b/package.json index f8437da3ad8..91ea5332620 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,6 @@ "06": "echo -------------------------------------------- Clean ----------------------------------------------", "clean": "rimraf dist node_modules dist/libs", "playwright": "npx playwright test --config='e2e-playwright/playwright.config.ts'", - "js_api_build": "shx rm -rf ./dist/libs/js-api && npm-run-all js_api_build_cjs js_api_build_esm5 js_api_build_esm2015 js_api_build_types js_api_assets", - "js_api_build_cjs": "tsc -p ./lib/js-api/tsconfig/tsconfig.cjs.json", - "js_api_build_esm5": "shx rm -rf ./dist/libs/js-api/esm5 && tsc -p ./lib/js-api/tsconfig/tsconfig.esm5.json", - "js_api_build_esm2015": "shx rm -rf ./dist/libs/js-api/esm2015 && tsc -p ./lib/js-api/tsconfig/tsconfig.esm2015.json", - "js_api_build_types": "shx rm -rf ./dist/libs/js-api/typings && tsc -p ./lib/js-api/tsconfig/tsconfig.types.json", - "js_api_assets": "shx cp ./lib/js-api/package.json ./dist/libs/js-api/ && shx cp ./lib/js-api/README.md ./dist/libs/js-api/ && shx cp ./lib/js-api/LICENSE.txt ./dist/libs/js-api/", "postinstall": "npm run update-webdriver", "update-webdriver": "./scripts/webdriver-update-newest/update-to-newest-webdriver.sh", "update-webdriver:windows": "powershell -executionPolicy bypass ./scripts/update-webdriver.ps1" @@ -197,7 +191,6 @@ "sass-loader": "13.3.2", "selenium-webdriver": "^4.14.0", "shelljs": "^0.8.5", - "shx": "^0.3.4", "spdx-license-list": "^6.9.0", "stylelint": "^16.3.1", "stylelint-config-standard-scss": "^13.1.0",