AAE-44946 - Sending also previous value to allow valuation and valida… (#11836)

* AAE-44946 - Sending also previous value to allow valuation and validation

* AAE-44946 - Sending also previous value to allow valuation and validation

* AAE-44749 - Fixed wrong subscribe in test

* AAE-45038 - Fixing logs and unit tests
This commit is contained in:
Vito Albano
2026-05-20 14:19:23 +05:30
committed by Anamika Dey
parent cb32883f56
commit 669c85c76b
8 changed files with 101 additions and 29 deletions
@@ -31,7 +31,6 @@
'adf-property-value-has-error': isEditable && hasErrors,
'adf-property-value-not-editable': !editable
}"
[disabled]="isReadonlyProperty || !editable"
title="{{ property.label | translate }}"
[placeholder]="property.default"
[attr.aria-label]="property.label | translate"
@@ -53,7 +52,6 @@
'adf-property-value-editable': editable,
'adf-property-readonly-value': isReadonlyProperty || !editable,
}"
[disabled]="isReadonlyProperty || !editable"
[placeholder]="property.default"
[attr.aria-label]="property.label | translate"
[formControl]="textInput"
@@ -198,8 +198,11 @@ describe('CardViewTextItemComponent', () => {
key: 'namekey',
editable: true
});
spyOn(component.property, 'isValid').and.returnValue(false);
component.editable = true;
component.ngOnChanges({ property: new SimpleChange(null, null, true) });
fixture.detectChanges();
spyOn(component.property, 'isValid').and.returnValue(false);
spyOn(component.textInput, 'setErrors');
const textField = await getTextField(component.property.key);
await textField.blur();
@@ -216,8 +219,11 @@ describe('CardViewTextItemComponent', () => {
key: 'namekey',
editable: true
});
spyOn(component.property, 'isValid').and.returnValue(false);
component.editable = true;
component.ngOnChanges({ property: new SimpleChange(null, null, true) });
fixture.detectChanges();
spyOn(component.property, 'isValid').and.returnValue(false);
spyOn(component.textInput, 'markAsTouched');
const textField = await getTextField(component.property.key);
await textField.blur();
@@ -232,6 +238,10 @@ describe('CardViewTextItemComponent', () => {
key: 'namekey',
editable: true
});
component.editable = true;
component.ngOnChanges({ property: new SimpleChange(null, null, true) });
fixture.detectChanges();
spyOn(component.property, 'isValid').and.returnValue(false);
spyOn(component.property, 'getValidationErrors').and.returnValue([
{
@@ -241,7 +251,6 @@ describe('CardViewTextItemComponent', () => {
message: 'Error 2'
}
] as CardViewItemValidator[]);
component.editable = true;
const textField = await getTextField(component.property.key);
await textField.blur();
@@ -544,6 +553,9 @@ describe('CardViewTextItemComponent', () => {
target: { ...component.property, isValidValue: true },
changed: {
textkey: expectedText
},
previousValue: {
textkey: 'FAKE-DEFAULT-KEY'
}
});
@@ -617,7 +629,7 @@ describe('CardViewTextItemComponent', () => {
await fixture.whenStable();
const property = { ...component.property, isValidValue: true };
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, 'updated-value');
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, 'updated-value', { previousValue: 'Lorem ipsum' });
});
it('should trigger the update event if the editedValue is NOT valid', async () => {
@@ -629,7 +641,7 @@ describe('CardViewTextItemComponent', () => {
await fixture.whenStable();
const property = { ...component.property, isValidValue: false };
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, '@invalid-value');
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, '@invalid-value', { previousValue: 'Lorem ipsum' });
});
it('should trigger the update event if the editedValue is valid', async () => {
@@ -707,6 +719,9 @@ describe('CardViewTextItemComponent', () => {
target: { ...component.property, isValidValue: true },
changed: {
textkey: expectedText
},
previousValue: {
textkey: 'Lorem ipsum'
}
});
});
@@ -752,6 +767,9 @@ describe('CardViewTextItemComponent', () => {
target: { ...component.property, isValidValue: true },
changed: {
textkey: expectedText
},
previousValue: {
textkey: 'Lorem ipsum'
}
});
@@ -871,6 +889,9 @@ describe('CardViewTextItemComponent', () => {
target: { ...component.property, isValidValue: true },
changed: {
textkey: expectedNumber.toString()
},
previousValue: {
textkey: 10
}
});
@@ -929,6 +950,9 @@ describe('CardViewTextItemComponent', () => {
target: { ...component.property, isValidValue: true },
changed: {
textkey: expectedNumber.toString()
},
previousValue: {
textkey: 77.33
}
});
@@ -81,6 +81,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
errors: CardViewItemValidator[];
templateType: string;
textInput = new UntypedFormControl();
private initialValue: string | string[];
private readonly destroyRef = inject(DestroyRef);
@@ -101,12 +102,10 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
this.resetValue();
this.setTemplateType();
if (changes.editable) {
if (this.isEditable) {
this.textInput.enable();
} else {
this.textInput.disable();
}
if (this.isEditable) {
this.textInput.enable();
} else {
this.textInput.disable();
}
}
@@ -132,6 +131,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
this.textInput.setValue(this.editedValue);
}
this.initialValue = Array.isArray(this.property.value) ? [...this.property.value] : this.property.value;
this.resetErrorMessages();
}
@@ -143,15 +143,20 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
update(): void {
if (this.isEditable) {
const previousValue = this.initialValue;
this.resetErrorMessages();
if (this.property.isValid(this.editedValue)) {
this.property.value = this.prepareValueForUpload(this.property, this.editedValue);
this.cardViewUpdateService.update({ ...this.property, isValidValue: true } as CardViewTextItemModel, this.property.value);
this.cardViewUpdateService.update({ ...this.property, isValidValue: true } as CardViewTextItemModel, this.property.value, {
previousValue
});
} else {
this.errors = this.property.getValidationErrors(this.editedValue);
this.textInput.setErrors({ customError: true });
this.textInput.markAsTouched();
this.cardViewUpdateService.update({ ...this.property, isValidValue: false } as CardViewTextItemModel, this.editedValue);
this.cardViewUpdateService.update({ ...this.property, isValidValue: false } as CardViewTextItemModel, this.editedValue, {
previousValue
});
}
}
}
@@ -0,0 +1,20 @@
/*!
* @license
* Copyright © 2005-2025 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 interface CardViewUpdateOptions {
previousValue?: any;
}
@@ -27,3 +27,4 @@ export * from './card-view-selectitem-properties.interface';
export * from './base-card-view-update.interface';
export * from './click-notification.interface';
export * from './update-notification.interface';
export * from './card-view-update-options.interface';
@@ -20,4 +20,5 @@ import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
export interface UpdateNotification {
target: CardViewBaseItemModel;
changed: any;
previousValue?: any;
}
@@ -15,7 +15,8 @@
* limitations under the License.
*/
import { fakeAsync, TestBed } from '@angular/core/testing';
import { TestBed } from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
import { CardViewUpdateService, transformKeyToObject } from './card-view-update.service';
@@ -59,19 +60,39 @@ describe('CardViewUpdateService', () => {
cardViewUpdateService = TestBed.inject(CardViewUpdateService);
});
it('should send updated message with proper parameters', fakeAsync(() => {
cardViewUpdateService.itemUpdated$.subscribe(({ target, changed }) => {
expect(target).toBe(property);
expect(changed).toEqual({ 'property-key': 'changed-property-value' });
});
it('should send updated message with proper parameters', async () => {
const updatePromise = firstValueFrom(cardViewUpdateService.itemUpdated$);
cardViewUpdateService.update(property, 'changed-property-value');
}));
it('should send clicked message with proper parameters', fakeAsync(() => {
cardViewUpdateService.itemClicked$.subscribe(({ target }) => {
expect(target).toBe(property);
});
const { target, changed } = await updatePromise;
expect(target).toBe(property);
expect(changed).toEqual({ 'property-key': 'changed-property-value' });
});
it('should include previousValue when provided', async () => {
const updatePromise = firstValueFrom(cardViewUpdateService.itemUpdated$);
cardViewUpdateService.update(property, 'new-value', { previousValue: 'old-value' });
const { target, changed, previousValue } = await updatePromise;
expect(target).toBe(property);
expect(changed).toEqual({ 'property-key': 'new-value' });
expect(previousValue).toEqual({ 'property-key': 'old-value' });
});
it('should not include previousValue when not provided', async () => {
const updatePromise = firstValueFrom(cardViewUpdateService.itemUpdated$);
cardViewUpdateService.update(property, 'changed-property-value');
const notification = await updatePromise;
expect(notification.previousValue).toBeUndefined();
});
it('should send clicked message with proper parameters', async () => {
const clickedPromise = firstValueFrom(cardViewUpdateService.itemClicked$);
cardViewUpdateService.clicked(property);
}));
const { target } = await clickedPromise;
expect(target).toBe(property);
});
});
});
@@ -21,6 +21,7 @@ import { BaseCardViewUpdate } from '../interfaces/base-card-view-update.interfac
import { ClickNotification } from '../interfaces/click-notification.interface';
import { UpdateNotification } from '../interfaces/update-notification.interface';
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
import { CardViewUpdateOptions } from '../interfaces/card-view-update-options.interface';
export const transformKeyToObject = (key: string, value): any => {
const objectLevels: string[] = key.split('.').reverse();
@@ -37,10 +38,11 @@ export class CardViewUpdateService implements BaseCardViewUpdate {
updateItem$ = new Subject<CardViewBaseItemModel>();
autocompleteInputValue$ = new Subject<string>();
update(property: CardViewBaseItemModel, newValue: any) {
update(property: CardViewBaseItemModel, newValue: any, options?: CardViewUpdateOptions) {
this.itemUpdated$.next({
target: property,
changed: transformKeyToObject(property.key, newValue)
changed: transformKeyToObject(property.key, newValue),
...(options?.previousValue !== undefined && { previousValue: transformKeyToObject(property.key, options.previousValue) })
});
}