[APPS-2108] migrate cardview and task filters to date-fns (#9006)

* move based edit task filter to date-fns

* migrate cardview component to date-fns

* bug fixes for card view types, utc and multivalue

* fix copy to clipboard typing issue

* mark moment adapter for deprecation

* exclude e2e

* try migrate metadata smoke
This commit is contained in:
Denys Vuika
2023-10-18 14:17:30 +01:00
committed by GitHub
parent 7d5fbecf3f
commit 678df4298d
11 changed files with 150 additions and 105 deletions

View File

@@ -17,7 +17,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import moment from 'moment';
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
import { CardViewUpdateService } from '../../services/card-view-update.service';
import { CardViewDateItemComponent } from './card-view-dateitem.component';
@@ -26,6 +25,7 @@ import { ClipboardService } from '../../../clipboard/clipboard.service';
import { CardViewDatetimeItemModel } from '../../models/card-view-datetimeitem.model';
import { TranslateModule } from '@ngx-translate/core';
import { AppConfigService } from '@alfresco/adf-core';
import { MatDatetimepickerInputEvent } from '@mat-datetimepicker/core';
describe('CardViewDateItemComponent', () => {
@@ -192,15 +192,15 @@ describe('CardViewDateItemComponent', () => {
const itemUpdatedSpy = spyOn(cardViewUpdateService.itemUpdated$, 'next');
component.editable = true;
component.property.editable = true;
const expectedDate = moment('Jul 10 2017', 'MMM DD YYYY');
const expectedDate = new Date('Jul 10 2017');
fixture.detectChanges();
const property = { ...component.property };
component.onDateChanged({ value: expectedDate });
component.onDateChanged({ value: expectedDate } as MatDatetimepickerInputEvent<Date>);
expect(itemUpdatedSpy).toHaveBeenCalledWith({
target: property,
changed: {
dateKey: expectedDate.toDate()
dateKey: expectedDate
}
});
});
@@ -209,13 +209,13 @@ describe('CardViewDateItemComponent', () => {
component.editable = true;
component.property.editable = true;
component.property.value = null;
const expectedDate = moment('Jul 10 2017', 'MMM DD YY');
const expectedDate = new Date('Jul 10 2017');
fixture.detectChanges();
component.onDateChanged({ value: expectedDate });
component.onDateChanged({ value: expectedDate } as MatDatetimepickerInputEvent<Date>);
await fixture.whenStable();
expect(component.property.value).toEqual(expectedDate.toDate());
expect(component.property.value).toEqual(expectedDate);
});
it('should copy value to clipboard on double click', () => {
@@ -236,7 +236,7 @@ describe('CardViewDateItemComponent', () => {
it('should render the clear icon in case of displayClearAction:true', () => {
component.editable = true;
component.property.editable = true;
component.property.value = 'Jul 10 2017';
component.property.value = new Date('Jul 10 2017');
fixture.detectChanges();
const datePickerClearToggle = fixture.debugElement.query(By.css(`[data-automation-id="datepicker-date-clear-${component.property.key}"]`));
@@ -257,7 +257,7 @@ describe('CardViewDateItemComponent', () => {
component.editable = true;
component.property.editable = true;
component.displayClearAction = false;
component.property.value = 'Jul 10 2017';
component.property.value = new Date('Jul 10 2017');
fixture.detectChanges();
const datePickerClearToggle = fixture.debugElement.query(By.css(`[data-automation-id="datepicker-date-clear--${component.property.key}"]`));
@@ -267,7 +267,7 @@ describe('CardViewDateItemComponent', () => {
it('should remove the property value after a successful clear attempt', async () => {
component.editable = true;
component.property.editable = true;
component.property.value = 'Jul 10 2017';
component.property.value = new Date('Jul 10 2017');
fixture.detectChanges();
component.onDateClear();
@@ -294,7 +294,7 @@ describe('CardViewDateItemComponent', () => {
component.editable = true;
component.property.editable = true;
component.property.default = 'Jul 10 2017';
component.property.value = 'Jul 10 2017';
component.property.value = new Date('Jul 10 2017');
fixture.detectChanges();
const property = { ...component.property };
@@ -320,8 +320,8 @@ describe('CardViewDateItemComponent', () => {
component.property.default = 'Jul 10 2017 00:01:00';
component.property.key = 'fake-key';
component.dateFormat = 'M/d/yy, h:mm a';
component.property.value = 'Jul 10 2017 00:01:00';
const expectedDate = moment('Jul 10 2018', 'MMM DD YY h:m:s');
component.property.value = new Date('Jul 10 2017 00:01:00');
const expectedDate = new Date('Jul 10 2018');
fixture.detectChanges();
await fixture.whenStable();
@@ -329,10 +329,10 @@ describe('CardViewDateItemComponent', () => {
const element = fixture.debugElement.nativeElement.querySelector('span[data-automation-id="card-date-value-fake-key"]');
expect(element).toBeDefined();
expect(element.innerText).toEqual('Jul 10, 2017');
component.onDateChanged({ value: expectedDate });
component.onDateChanged({ value: expectedDate } as MatDatetimepickerInputEvent<Date>);
fixture.detectChanges();
expect(component.property.value).toEqual(expectedDate.toDate());
expect(component.property.value).toEqual(expectedDate);
});
it('should render chips for multivalue dates when chips are enabled', async () => {

View File

@@ -17,26 +17,26 @@
import { Component, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerComponent } from '@mat-datetimepicker/core';
import { MAT_MOMENT_DATETIME_FORMATS, MomentDatetimeAdapter } from '@mat-datetimepicker/moment';
import moment, { Moment } from 'moment';
import { DatetimeAdapter, MAT_DATETIME_FORMATS, MatDatetimepickerComponent, MatDatetimepickerInputEvent } from '@mat-datetimepicker/core';
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
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 { AppConfigService } from '../../../app-config/app-config.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { BaseCardView } from '../base-card-view';
import { ClipboardService } from '../../../clipboard/clipboard.service';
import { TranslationService } from '../../../translation/translation.service';
import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '../../../common/utils/date-fns-adapter';
import { ADF_DATETIME_FORMATS, AdfDateTimeFnsAdapter } from '../../../common/utils/datetime-fns-adapter';
import { DateFnsUtils } from '../../../common';
import { isValid } from 'date-fns';
@Component({
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS },
{ provide: DatetimeAdapter, useClass: MomentDatetimeAdapter },
{ provide: MAT_DATETIME_FORMATS, useValue: MAT_MOMENT_DATETIME_FORMATS }
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS },
{ provide: MAT_DATETIME_FORMATS, useValue: ADF_DATETIME_FORMATS },
{ provide: DateAdapter, useClass: AdfDateFnsAdapter },
{ provide: DatetimeAdapter, useClass: AdfDateTimeFnsAdapter }
],
selector: 'adf-card-view-dateitem',
templateUrl: './card-view-dateitem.component.html',
@@ -44,7 +44,6 @@ import { TranslationService } from '../../../translation/translation.service';
encapsulation: ViewEncapsulation.None
})
export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemModel> implements OnInit, OnDestroy {
@Input()
property: CardViewDateItemModel;
@@ -60,16 +59,18 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
@ViewChild('datetimePicker')
public datepicker: MatDatetimepickerComponent<any>;
valueDate: Moment;
valueDate: Date;
dateFormat: string;
private onDestroy$ = new Subject<boolean>();
constructor(private dateAdapter: DateAdapter<Moment>,
private userPreferencesService: UserPreferencesService,
private appConfig: AppConfigService,
private clipboardService: ClipboardService,
private translateService: TranslationService) {
constructor(
private dateAdapter: DateAdapter<Date>,
private userPreferencesService: UserPreferencesService,
private appConfig: AppConfigService,
private clipboardService: ClipboardService,
private translateService: TranslationService
) {
super();
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
}
@@ -78,16 +79,20 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
this.userPreferencesService
.select(UserPreferenceValues.Locale)
.pipe(takeUntil(this.onDestroy$))
.subscribe(locale => {
.subscribe((locale) => {
this.property.locale = locale;
});
(this.dateAdapter as MomentDateAdapter).overrideDisplayFormat = 'MMM DD';
(this.dateAdapter as AdfDateFnsAdapter).displayFormat = 'MMM DD';
if (this.property.value) {
this.valueDate = moment(this.property.value, this.dateFormat);
} else if (this.property.multivalued && !this.property.value) {
this.property.value = [];
if (this.property.multivalued) {
if (!this.property.value) {
this.property.value = [];
}
} else {
if (this.property.value && !Array.isArray(this.property.value)) {
this.valueDate = DateFnsUtils.localToUtc(DateFnsUtils.parseDate(this.property.value, this.dateFormat));
}
}
}
@@ -112,12 +117,11 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
this.datepicker.open();
}
onDateChanged(newDateValue) {
if (newDateValue) {
const momentDate = moment(newDateValue.value, this.dateFormat, true);
if (momentDate.isValid()) {
this.valueDate = momentDate;
this.property.value = momentDate.toDate();
onDateChanged(event: MatDatetimepickerInputEvent<Date>) {
if (event.value) {
if (isValid(event.value)) {
this.valueDate = event.value;
this.property.value = DateFnsUtils.utcToLocal(event.value);
this.update();
}
}
@@ -130,24 +134,27 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
this.property.default = null;
}
copyToClipboard(valueToCopy: string) {
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
copyToClipboard(valueToCopy: string | string[]) {
if (typeof valueToCopy === 'string') {
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
}
}
addDateToList(newDateValue) {
if (newDateValue) {
const momentDate = moment(newDateValue.value, this.dateFormat, true);
if (momentDate.isValid()) {
this.property.value.push(momentDate.toDate());
addDateToList(event: MatDatetimepickerInputEvent<Date>) {
if (event.value) {
if (isValid(event.value) && this.property.multivalued && Array.isArray(this.property.value)) {
this.property.value.push(DateFnsUtils.utcToLocal(event.value));
this.update();
}
}
}
removeValueFromList(itemIndex: number) {
this.property.value.splice(itemIndex, 1);
this.update();
if (this.property.multivalued && Array.isArray(this.property.value)) {
this.property.value.splice(itemIndex, 1);
this.update();
}
}
update() {

View File

@@ -18,9 +18,9 @@
import { CardViewItemProperties, CardViewItemValidator } from '../interfaces/card-view.interfaces';
import validatorsMap from '../validators/validators.map';
export abstract class CardViewBaseItemModel {
export abstract class CardViewBaseItemModel<T = any> {
label: string;
value: any;
value: T;
key: any;
default: any;
editable: boolean;
@@ -32,21 +32,21 @@ export abstract class CardViewBaseItemModel {
type?: string;
multivalued?: boolean;
constructor(cardViewItemProperties: CardViewItemProperties) {
this.label = cardViewItemProperties.label || '';
this.value = cardViewItemProperties.value?.displayName || cardViewItemProperties.value;
this.key = cardViewItemProperties.key;
this.default = cardViewItemProperties.default;
this.editable = !!cardViewItemProperties.editable;
this.clickable = !!cardViewItemProperties.clickable;
this.icon = cardViewItemProperties.icon || '';
this.hint = cardViewItemProperties.hint || '';
this.validators = cardViewItemProperties.validators || [];
this.data = cardViewItemProperties.data || null;
this.multivalued = !!cardViewItemProperties.multivalued;
constructor(props: CardViewItemProperties) {
this.label = props.label || '';
this.value = props.value?.displayName || props.value;
this.key = props.key;
this.default = props.default;
this.editable = !!props.editable;
this.clickable = !!props.clickable;
this.icon = props.icon || '';
this.hint = props.hint || '';
this.validators = props.validators || [];
this.data = props.data || null;
this.multivalued = !!props.multivalued;
if (cardViewItemProperties?.constraints?.length ?? 0) {
for (const constraint of cardViewItemProperties.constraints) {
if (props?.constraints?.length ?? 0) {
for (const constraint of props.constraints) {
if (constraint.type !== 'LIST') {
this.validators.push(validatorsMap[constraint.type.toLowerCase()](constraint.parameters));
}
@@ -55,10 +55,15 @@ export abstract class CardViewBaseItemModel {
}
isEmpty(): boolean {
return this.value === undefined || this.value === null || this.value.length === 0;
return (
this.value === undefined ||
this.value === null ||
(typeof this.value === 'string' && this.value.length === 0) ||
(Array.isArray(this.value) && this.value.length === 0)
);
}
isValid(newValue: any): boolean {
isValid(newValue: T): boolean {
if (!this.validators.length) {
return true;
}
@@ -66,7 +71,7 @@ export abstract class CardViewBaseItemModel {
return this.validators.map((validator) => validator.isValid(newValue)).reduce((isValidUntilNow, isValid) => isValidUntilNow && isValid, true);
}
getValidationErrors(value): CardViewItemValidator[] {
getValidationErrors(value: T): CardViewItemValidator[] {
if (!this.validators.length) {
return [];
}

View File

@@ -21,7 +21,9 @@ import { CardViewBaseItemModel } from './card-view-baseitem.model';
import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
import { LocalizedDatePipe } from '../../pipes/localized-date.pipe';
export class CardViewDateItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type DateItemType = Date | Date[] | null;
export class CardViewDateItemModel extends CardViewBaseItemModel<DateItemType> implements CardViewItem, DynamicComponentModel {
type: string = 'date';
format: string;
locale: string;
@@ -41,19 +43,19 @@ export class CardViewDateItemModel extends CardViewBaseItemModel implements Card
}
get displayValue() {
get displayValue(): string | string[] {
if (this.multivalued) {
if (this.value) {
if (this.value && Array.isArray(this.value)) {
return this.value.map((date) => this.transformDate(date));
} else {
return this.default ? [this.default] : [];
}
} else {
return this.value ? this.transformDate(this.value) : this.default;
return this.value && !Array.isArray(this.value) ? this.transformDate(this.value) : this.default;
}
}
transformDate(value: any) {
transformDate(value: Date | string | number): string {
this.localizedDatePipe = new LocalizedDatePipe();
return this.localizedDatePipe.transform(value, this.format, this.locale);
}

View File

@@ -21,7 +21,7 @@ export class CardViewItemFloatValidator implements CardViewItemValidator {
message = 'CORE.CARDVIEW.VALIDATORS.FLOAT_VALIDATION_ERROR';
isValid(value: any): boolean {
isValid(value: any | any[]): boolean {
if (Array.isArray(value)) {
return value.every(this.isDecimalNumber);
}

View File

@@ -21,7 +21,7 @@ export class CardViewItemIntValidator implements CardViewItemValidator {
message = 'CORE.CARDVIEW.VALIDATORS.INT_VALIDATION_ERROR';
isValid(value: any): boolean {
isValid(value: any | any[]): boolean {
if (Array.isArray(value)) {
return value.every(this.isIntegerNumber);
}

View File

@@ -20,6 +20,10 @@ import { DateAdapter } from '@angular/material/core';
import moment, { isMoment, Moment } from 'moment';
import { UserPreferencesService, UserPreferenceValues } from '../services/user-preferences.service';
/**
* @deprecated this class is deprecated and should not be used.
* Consider using `AdfDateFnsAdapter` or `AdfDateTimeFnsAdapter` instead
*/
@Injectable()
export class MomentDateAdapter extends DateAdapter<Moment> {
private localeData: any = moment.localeData();

View File

@@ -55,10 +55,10 @@ export class LocalizedDatePipe implements PipeTransform, OnDestroy {
}
}
transform(value: any, format?: string, locale?: string): any {
transform(value: Date | string | number, format?: string, locale?: string): string {
const actualFormat = format || this.defaultFormat;
const actualLocale = locale || this.defaultLocale;
const datePipe: DatePipe = new DatePipe(actualLocale);
const datePipe = new DatePipe(actualLocale);
return datePipe.transform(value, actualFormat);
}