diff --git a/e2e/process-services/pages/form-demo.page.ts b/e2e/process-services/pages/form-demo.page.ts deleted file mode 100644 index 96ad0afe37b..00000000000 --- a/e2e/process-services/pages/form-demo.page.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*! - * @license - * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { BrowserActions, BrowserVisibility, ConfigEditorPage } from '@alfresco/adf-testing'; -import { $$, $ } from 'protractor'; - -export class FormDemoPage { - - formCloudEditor = $$('.mat-tab-list .mat-tab-label').get(1); - formCloudRender = $$('.mat-tab-list .mat-tab-label').get(0); - - configEditorPage = new ConfigEditorPage(); - - async goToEditor(): Promise { - await BrowserActions.click(this.formCloudEditor); - } - - async goToRenderedForm(): Promise { - await BrowserActions.click(this.formCloudRender); - } - - async setConfigToEditor(text: string): Promise { - const configEditor = $('#adf-form-config-editor'); - const form = $('adf-form'); - await this.goToEditor(); - await BrowserVisibility.waitUntilElementIsVisible(configEditor); - await this.configEditorPage.enterBulkConfiguration(text); - await this.goToRenderedForm(); - await BrowserVisibility.waitUntilElementIsVisible(form); - } -} diff --git a/e2e/process-services/widgets/date-widget.e2e.ts b/e2e/process-services/widgets/date-widget.e2e.ts deleted file mode 100644 index 5f9d162b14f..00000000000 --- a/e2e/process-services/widgets/date-widget.e2e.ts +++ /dev/null @@ -1,127 +0,0 @@ -/*! - * @license - * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - createApiService, - ApplicationsUtil, - BrowserActions, - FormPage, - LoginPage, - ProcessUtil, - UsersActions, - Widget, UserModel -} from '@alfresco/adf-testing'; -import { TasksPage } from '../pages/tasks.page'; -import { browser } from 'protractor'; -import { FormDemoPage } from '../pages/form-demo.page'; -import { customDateFormAPS1 } from '../../resources/forms/custom-date-form'; -import CONSTANTS = require('../../util/constants'); -import { ProcessServicesPage } from '../pages/process-services.page'; -import { AppDefinitionRepresentation, ProcessInstanceRepresentation } from '@alfresco/js-api'; - -describe('Date widget', () => { - - const app = browser.params.resources.Files.WIDGET_CHECK_APP.DATE; - - const loginPage = new LoginPage(); - const taskPage = new TasksPage(); - const widget = new Widget(); - - const dateWidget = widget.dateWidget(); - let appModel: AppDefinitionRepresentation; - let processUserModel: UserModel; - let deployedAppId: number; - let process: ProcessInstanceRepresentation; - - const apiService = createApiService(); - const usersActions = new UsersActions(apiService); - const applicationsService = new ApplicationsUtil(apiService); - const processUtil = new ProcessUtil(apiService); - - beforeAll(async () => { - await apiService.loginWithProfile('admin'); - - processUserModel = await usersActions.createUser(); - - await apiService.login(processUserModel.username, processUserModel.password); - appModel = await applicationsService.importPublishDeployApp(browser.params.resources.Files.WIDGET_CHECK_APP.file_path); - - deployedAppId = await applicationsService.getAppDefinitionId(appModel.id); - - process = await processUtil.startProcessByDefinitionName(appModel.name, app.processName); - await loginPage.login(processUserModel.username, processUserModel.password); - }); - - afterAll(async () => { - await processUtil.cancelProcessInstance(process.id); - await apiService.loginWithProfile('admin'); - await usersActions.deleteTenant(processUserModel.tenantId); - }); - - describe('Simple App', () => { - beforeEach(async () => { - await new ProcessServicesPage().goToAppByAppId(`${deployedAppId}`); - - await taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); - await taskPage.formFields().checkFormIsDisplayed(); - }); - - it('[C268814] Should be able to set general settings for Date widget', async () => { - await expect(await dateWidget.getDateLabel(app.FIELD.date_input)).toContain('Date'); - await expect(await taskPage.formFields().isCompleteFormButtonEnabled()).toEqual(false); - await dateWidget.setDateInput(app.FIELD.date_input, '20-10-2018'); - await taskPage.formFields().saveForm(); - await expect(await taskPage.formFields().isCompleteFormButtonEnabled()).toEqual(true); - }); - - it('[C277234] Should be able to set advanced settings for Date widget ', async () => { - await dateWidget.setDateInput(app.FIELD.date_between_input, '20-10-2017'); - await taskPage.formFields().saveForm(); - await expect(await dateWidget.getErrorMessage(app.FIELD.date_between_input)).toBe('Can\'t be less than 1-10-2018'); - await dateWidget.clearDateInput(app.FIELD.date_between_input); - await dateWidget.setDateInput(app.FIELD.date_between_input, '20-10-2019'); - await taskPage.formFields().saveForm(); - await expect(await dateWidget.getErrorMessage(app.FIELD.date_between_input)).toBe('Can\'t be greater than 31-10-2018'); - }); - }); - - describe('Form Demo Page', () => { - const formDemoPage = new FormDemoPage(); - const formJson = JSON.parse(customDateFormAPS1); - const formPage = new FormPage(); - - beforeAll(async () => { - const urlFormDemoPage = `${browser.baseUrl}/form`; - await BrowserActions.getUrl(urlFormDemoPage); - }); - - it('[C313199] Should display the validation for min and max date values with custom date format', async () => { - await formDemoPage.setConfigToEditor(formJson); - await dateWidget.setDateInput('datefield', '18-7-19'); - await formPage.saveForm(); - await expect(await dateWidget.getErrorMessage('datefield')).toBe('Can\'t be less than 19-7-19'); - await dateWidget.clearDateInput('datefield'); - await dateWidget.setDateInput('datefield', '20-7-19'); - await formPage.saveForm(); - await expect(await dateWidget.getErrorMessage('datefield')).toBe('Can\'t be greater than 19-8-19'); - await dateWidget.clearDateInput('datefield'); - await dateWidget.setDateInput('datefield', '19-7-19'); - await formPage.saveForm(); - await dateWidget.checkErrorMessageIsNotDisplayed('datefield'); - }); - }); -}); diff --git a/e2e/resources/forms/custom-date-form.ts b/e2e/resources/forms/custom-date-form.ts deleted file mode 100644 index 0b118bb8e4f..00000000000 --- a/e2e/resources/forms/custom-date-form.ts +++ /dev/null @@ -1,179 +0,0 @@ -/*! - * @license - * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const customDateFormAPS1 = `{ - "formRepresentation":{ - "id": 18977, - "name": "APS1customDateFrom", - "description": "", - "version": 1, - "lastUpdatedBy": 1, - "lastUpdatedByFullName": " Administrator", - "lastUpdated": "2019-08-21T09:29:18.042+0000", - "stencilSetId": 0, - "referenceId": null, - "formDefinition": { - "tabs": [], - "fields": [ - { - "fieldType": "ContainerRepresentation", - "id": "1566223482682", - "name": "Label", - "type": "container", - "value": null, - "required": false, - "readOnly": false, - "overrideId": false, - "colspan": 1, - "placeholder": null, - "minLength": 0, - "maxLength": 0, - "minValue": null, - "maxValue": null, - "regexPattern": null, - "optionType": null, - "hasEmptyValue": null, - "options": null, - "restUrl": null, - "restResponsePath": null, - "restIdProperty": null, - "restLabelProperty": null, - "tab": null, - "className": null, - "dateDisplayFormat": null, - "layout": null, - "sizeX": 2, - "sizeY": 1, - "row": -1, - "col": -1, - "visibilityCondition": null, - "numberOfColumns": 2, - "fields": { - "1": [ - { - "fieldType": "FormFieldRepresentation", - "id": "datefield", - "name": "DateField", - "type": "date", - "value": null, - "required": false, - "readOnly": false, - "overrideId": false, - "colspan": 1, - "placeholder": null, - "minLength": 0, - "maxLength": 0, - "minValue": "19-7-2019", - "maxValue": "19-8-2019", - "regexPattern": null, - "optionType": null, - "hasEmptyValue": null, - "options": null, - "restUrl": null, - "restResponsePath": null, - "restIdProperty": null, - "restLabelProperty": null, - "tab": null, - "className": null, - "params": { - "existingColspan": 1, - "maxColspan": 2 - }, - "dateDisplayFormat": "YY-M-D", - "layout": { - "row": -1, - "column": -1, - "colspan": 1 - }, - "sizeX": 1, - "sizeY": 1, - "row": -1, - "col": -1, - "visibilityCondition": null - } - ], - "2": [] - } - } - ], - "outcomes": [], - "javascriptEvents": [], - "className": "", - "style": "", - "customFieldTemplates": {}, - "metadata": {}, - "variables": [], - "customFieldsValueInfo": {}, - "gridsterForm": false - } - } -}`; - -export const customDateFormAPS2 = `{ - "formRepresentation":{ - "id":"form-71f621f5-7113-4bb8-a646-8fe36f27cdf4", - "name":"APS2customDateForm", - "description":"", - "version":0, - "standalone":true, - "formDefinition":{ - "tabs":[ - - ], - "fields":[ - { - "id":"c207088c-e0f5-402e-8513-a865f3777c25", - "name":"Label", - "type":"container", - "tab":null, - "numberOfColumns":2, - "fields":{ - "1":[ - { - "id":"datefield", - "name":"DateField", - "type":"date", - "required":false, - "colspan":1, - "placeholder":null, - "minValue":"2019-07-19", - "maxValue":"2019-08-19", - "visibilityCondition":null, - "params":{ - "existingColspan":1, - "maxColspan":2 - }, - "dateDisplayFormat":"YY-M-D" - } - ], - "2":[ - - ] - } - } - ], - "outcomes":[ - - ], - "metadata":{ - }, - "variables":[ - - ] - } - } -}`; diff --git a/lib/core/src/lib/common/utils/date-fns-adapter.ts b/lib/core/src/lib/common/utils/date-fns-adapter.ts new file mode 100644 index 00000000000..f74e4b547c0 --- /dev/null +++ b/lib/core/src/lib/common/utils/date-fns-adapter.ts @@ -0,0 +1,89 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DateFnsAdapter } from '@angular/material-date-fns-adapter'; +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'; + +/** + * Date-fns adapter with moment-to-date-fns conversion. + * + * Automatically switches locales based on user preferences. + * Supports custom display format. + * + * @example + * + * Add the following to the component `providers` section + * + * providers: [ + * { provide: MAT_DATE_FORMATS, useValue: ADF_FORM_DATE_FORMATS }, + * { provide: DateAdapter, useClass: AdfDateFnsAdapter } + * ] + * + * Setting custom format + * + * constructor(private dateAdapter: DateAdapter) {} + * + * ngOnInit() { + * const adapter = this.dateAdapter as AdfDateFnsAdapter; + adapter.displayFormat = ''; + * } + */ +@Injectable() +export class AdfDateFnsAdapter extends DateFnsAdapter { + private _displayFormat?: string = null; + + get displayFormat(): string | null { + return this._displayFormat; + } + + set displayFormat(value: string | null) { + this._displayFormat = value ? DateFnsUtils.convertMomentToDateFnsFormat(value) : null; + } + + constructor( + @Optional() @Inject(MAT_DATE_LOCALE) matDateLocale: Locale, + @Optional() @Inject(MAT_DATE_FORMATS) private formats: MatDateFormats, + preferences: UserPreferencesService + ) { + super(matDateLocale); + + preferences.select(UserPreferenceValues.Locale).subscribe((locale: string) => { + this.setLocale(DateFnsUtils.getLocaleFromString(locale)); + }); + } + + override parse(value: any, parseFormat: string | string[]): Date { + const format = Array.isArray(parseFormat) + ? parseFormat.map(DateFnsUtils.convertMomentToDateFnsFormat) + : DateFnsUtils.convertMomentToDateFnsFormat(parseFormat); + return super.parse(value, format); + } + + override format(date: Date, displayFormat: string): string { + displayFormat = DateFnsUtils.convertMomentToDateFnsFormat(displayFormat); + + if (this.displayFormat && displayFormat === this.formats?.display?.dateInput) { + return super.format(date, this.displayFormat || displayFormat); + } + + return super.format(date, displayFormat); + } +} diff --git a/lib/core/src/lib/common/utils/public-api.ts b/lib/core/src/lib/common/utils/public-api.ts index 8b3fbaa6164..2c48ff567b6 100644 --- a/lib/core/src/lib/common/utils/public-api.ts +++ b/lib/core/src/lib/common/utils/public-api.ts @@ -21,3 +21,4 @@ export * from './moment-date-formats.model'; export * from './moment-date-adapter'; export * from './string-utils'; export * from './date-fns-utils'; +export * from './date-fns-adapter'; diff --git a/lib/core/src/lib/core.module.ts b/lib/core/src/lib/core.module.ts index cdc7b2b84fe..b54776216cd 100644 --- a/lib/core/src/lib/core.module.ts +++ b/lib/core/src/lib/core.module.ts @@ -64,6 +64,7 @@ import { loadAppConfig } from './app-config/app-config.loader'; import { AppConfigService } from './app-config/app-config.service'; import { StorageService } from './common/services/storage.service'; import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-factories/alfresco-api-v2-loader.service'; +import { AdfDateFnsAdapter } from './common/utils/date-fns-adapter'; @NgModule({ imports: [ @@ -148,6 +149,7 @@ export class CoreModule { TranslateStore, TranslateService, { provide: TranslateLoader, useClass: TranslateLoaderService }, + AdfDateFnsAdapter, { provide: APP_INITIALIZER, useFactory: loadAppConfig, 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 7c364f8885d..98bb017c23d 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 @@ -16,18 +16,21 @@ */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import moment from 'moment'; +import { DateAdapter } from '@angular/material/core'; import { FormFieldModel } from '../core/form-field.model'; import { FormModel } from '../core/form.model'; import { DateWidgetComponent } from './date.widget'; import { CoreTestingModule } from '../../../../testing/core.testing.module'; import { TranslateModule } from '@ngx-translate/core'; import { FormFieldTypes } from '../core/form-field-types'; +import { DateFieldValidator, MaxDateFieldValidator, MinDateFieldValidator } from '../core/form-field-validator'; describe('DateWidgetComponent', () => { let widget: DateWidgetComponent; let fixture: ComponentFixture; let element: HTMLElement; + let adapter: DateAdapter; + let form: FormModel; beforeEach(() => { TestBed.configureTestingModule({ @@ -36,14 +39,19 @@ describe('DateWidgetComponent', () => { CoreTestingModule ] }); + + form = new FormModel(); + form.fieldValidators = [new DateFieldValidator(), new MinDateFieldValidator(), new MaxDateFieldValidator()]; + fixture = TestBed.createComponent(DateWidgetComponent); + adapter = fixture.debugElement.injector.get(DateAdapter); element = fixture.nativeElement; widget = fixture.componentInstance; }); it('[C310333] - should be able to set a placeholder', () => { - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(form, { id: 'date-id', name: 'date-name', placeholder: 'My Placeholder' @@ -54,7 +62,7 @@ describe('DateWidgetComponent', () => { it('should setup min value for date picker', () => { const minValue = '13-03-1982'; - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(form, { id: 'date-id', name: 'date-name', minValue @@ -62,13 +70,65 @@ describe('DateWidgetComponent', () => { widget.ngOnInit(); - const expected = moment(minValue, widget.field.dateDisplayFormat); - expect(widget.minDate.isSame(expected)).toBeTruthy(); + const expected = adapter.parse(minValue, widget.DATE_FORMAT) as Date; + expect(adapter.compareDate(widget.minDate, expected)).toBe(0); + }); + + it('should validate min date value constraint', async () => { + const minValue = '13-03-1982'; + + const field = new FormFieldModel(form, { + id: 'date-id', + type: 'date', + name: 'date-name', + dateDisplayFormat: 'DD-MM-YYYY', + minValue + }); + + widget.field = field; + widget.ngOnInit(); + + widget.onDateChange({ + value: new Date('1982/03/12') + } as any); + + 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'; + + const field = new FormFieldModel(form, { + id: 'date-id', + type: 'date', + name: 'date-name', + dateDisplayFormat: 'DD-MM-YYYY', + maxValue + }); + + widget.field = field; + widget.ngOnInit(); + + widget.onDateChange({ + value: new Date('2023/03/13') + } as any); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(widget.field.isValid).toBeFalsy(); + expect(field.validationSummary.message).toBe('FORM.FIELD.VALIDATOR.NOT_GREATER_THAN'); + expect(field.validationSummary.attributes.get('maxValue')).toBe('13-03-1982'); }); it('should date field be present', () => { const minValue = '13-03-1982'; - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(form, { minValue }); @@ -80,28 +140,28 @@ describe('DateWidgetComponent', () => { it('should setup max value for date picker', () => { const maxValue = '31-03-1982'; - widget.field = new FormFieldModel(null, { + widget.field = new FormFieldModel(form, { maxValue }); widget.ngOnInit(); - const expected = moment(maxValue, widget.field.dateDisplayFormat); - expect(widget.maxDate.isSame(expected)).toBeTruthy(); + const expected = adapter.parse(maxValue, widget.DATE_FORMAT) as Date; + expect(adapter.compareDate(widget.maxDate, expected)).toBe(0); }); it('should eval visibility on date changed', () => { spyOn(widget, 'onFieldChanged').and.callThrough(); - const field = new FormFieldModel(new FormModel(), { + const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9-9-9999', - type: 'date', - readOnly: 'false' + type: 'date' }); + widget.field = field; widget.onDateChange({ - value: moment('12/12/2012', widget.field.dateDisplayFormat) + value: new Date('12/12/2012') } as any); expect(widget.onFieldChanged).toHaveBeenCalledWith(field); @@ -137,14 +197,12 @@ describe('DateWidgetComponent', () => { }); it('should show visible date widget', async () => { - widget.field = new FormFieldModel(new FormModel(), { + widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9-9-9999', - type: 'date', - readOnly: 'false' + type: 'date' }); - widget.field.isVisible = true; fixture.detectChanges(); await fixture.whenStable(); @@ -156,15 +214,13 @@ describe('DateWidgetComponent', () => { }); it('[C310335] - Should be able to change display format for Date widget', async () => { - widget.field = new FormFieldModel(new FormModel(), { + widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - value: '12-30-9999', + value: '30-12-9999', type: 'date', - readOnly: 'false' + dateDisplayFormat: 'MM-DD-YYYY' }); - widget.field.isVisible = true; - widget.field.dateDisplayFormat = 'MM-DD-YYYY'; fixture.detectChanges(); await fixture.whenStable(); @@ -172,7 +228,7 @@ describe('DateWidgetComponent', () => { let dateElement = element.querySelector('#date-field-id'); expect(dateElement?.value).toContain('12-30-9999'); - widget.field.value = '05.06.2019'; + widget.field.value = '05-06-2019'; widget.field.dateDisplayFormat = 'DD.MM.YYYY'; fixture.componentInstance.ngOnInit(); @@ -184,30 +240,29 @@ describe('DateWidgetComponent', () => { }); it('should disable date button when is readonly', () => { - widget.field = new FormFieldModel(new FormModel(), { + widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: '9-9-9999', - type: 'date', - readOnly: 'false' + type: 'date' }); - widget.field.isVisible = true; - widget.field.readOnly = false; fixture.detectChanges(); let dateButton = element.querySelector('button'); + expect(dateButton).toBeDefined(); expect(dateButton.disabled).toBeFalsy(); widget.field.readOnly = true; fixture.detectChanges(); dateButton = element.querySelector('button'); + expect(dateButton).toBeDefined(); expect(dateButton.disabled).toBeTruthy(); }); it('should set isValid to false when the value is not a correct date value', () => { - widget.field = new FormFieldModel(new FormModel(), { + widget.field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', value: 'aa', @@ -223,23 +278,22 @@ describe('DateWidgetComponent', () => { }); it('should display always the json value', async () => { - const field = new FormFieldModel(new FormModel(), { + const field = new FormFieldModel(form, { id: 'date-field-id', name: 'date-name', - value: '12-30-9999', + value: '30-12-9999', type: 'date', - readOnly: 'false' + dateDisplayFormat: 'MM-DD-YYYY' }); - field.isVisible = true; - field.dateDisplayFormat = 'MM-DD-YYYY'; widget.field = field; fixture.detectChanges(); await fixture.whenStable(); const dateElement = element.querySelector('#date-field-id'); - expect(dateElement?.value).toContain('12-30-9999'); + expect(dateElement).toBeDefined(); + expect(dateElement.value).toContain('12-30-9999'); widget.field.value = '03-02-2020'; @@ -247,6 +301,6 @@ describe('DateWidgetComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - expect(dateElement?.value).toContain('03-02-2020'); + expect(dateElement.value).toContain('02-03-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 74c916bf4be..3fa5818e65c 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 @@ -17,23 +17,21 @@ /* eslint-disable @angular-eslint/component-selector */ -import { UserPreferencesService, UserPreferenceValues } from '../../../../common/services/user-preferences.service'; -import { MomentDateAdapter } from '../../../../common/utils/moment-date-adapter'; -import { MOMENT_DATE_FORMATS } from '../../../../common/utils/moment-date-formats.model'; import { Component, OnInit, ViewEncapsulation, OnDestroy, Input } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import moment, { Moment } from 'moment'; import { FormService } from '../../../services/form.service'; import { WidgetComponent } from '../widget.component'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; import { MatDatepickerInputEvent } from '@angular/material/datepicker'; +import { ADF_FORM_DATE_FORMATS } from '../../../date-formats'; +import { AdfDateFnsAdapter } from '../../../../common/utils/date-fns-adapter'; @Component({ selector: 'date-widget', providers: [ - { provide: DateAdapter, useClass: MomentDateAdapter }, - { provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }], + { provide: MAT_DATE_FORMATS, useValue: ADF_FORM_DATE_FORMATS }, + { provide: DateAdapter, useClass: AdfDateFnsAdapter } + ], templateUrl: './date.widget.html', host: { '(click)': 'event($event)', @@ -49,44 +47,40 @@ import { MatDatepickerInputEvent } from '@angular/material/datepicker'; encapsulation: ViewEncapsulation.None }) export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDestroy { + DATE_FORMAT = 'dd-MM-yyyy'; - DATE_FORMAT = 'DD-MM-YYYY'; - - minDate: Moment; - maxDate: Moment; - startAt: Moment; + minDate: Date; + maxDate: Date; + startAt: Date; @Input() value: any = null; private onDestroy$ = new Subject(); - constructor(public formService: FormService, - private dateAdapter: DateAdapter, - private userPreferencesService: UserPreferencesService) { + constructor(public formService: FormService, private dateAdapter: DateAdapter) { super(formService); } ngOnInit() { - this.userPreferencesService - .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(locale => this.dateAdapter.setLocale(locale)); - - const momentDateAdapter = this.dateAdapter as MomentDateAdapter; - momentDateAdapter.overrideDisplayFormat = this.field.dateDisplayFormat; + if (this.field.dateDisplayFormat) { + const adapter = this.dateAdapter as AdfDateFnsAdapter; + adapter.displayFormat = this.field.dateDisplayFormat; + } if (this.field) { if (this.field.minValue) { - this.minDate = moment(this.field.minValue, this.DATE_FORMAT); + this.minDate = this.dateAdapter.parse(this.field.minValue, this.DATE_FORMAT); } if (this.field.maxValue) { - this.maxDate = moment(this.field.maxValue, this.DATE_FORMAT); + this.maxDate = this.dateAdapter.parse(this.field.maxValue, this.DATE_FORMAT); } - this.startAt = moment(this.field.value, this.field.dateDisplayFormat); - this.value = moment(this.field.value, this.field.dateDisplayFormat); + 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); + } } } @@ -95,16 +89,16 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit, OnDe this.onDestroy$.complete(); } - onDateChange(event: MatDatepickerInputEvent) { + onDateChange(event: MatDatepickerInputEvent) { const value = event.value; const input = event.targetElement as HTMLInputElement; - const date = moment(value, this.field.dateDisplayFormat, true); - if (date.isValid()) { - this.field.value = date.format(this.field.dateDisplayFormat); + if (value) { + this.field.value = this.dateAdapter.format(value, this.DATE_FORMAT); } else { this.field.value = input.value; } + this.onFieldChanged(this.field); } } diff --git a/lib/core/src/lib/form/date-formats.ts b/lib/core/src/lib/form/date-formats.ts new file mode 100644 index 00000000000..eba4aed87de --- /dev/null +++ b/lib/core/src/lib/form/date-formats.ts @@ -0,0 +1,31 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { MatDateFormats } from '@angular/material/core'; + +export const ADF_FORM_DATE_FORMATS: MatDateFormats = { + parse: { + dateInput: 'dd-MM-yyyy' + }, + display: { + dateInput: 'dd-MM-yyyy', + monthLabel: 'LLL', + monthYearLabel: 'LLL uuuu', + dateA11yLabel: 'PP', + monthYearA11yLabel: 'LLLL uuuu' + } +};