mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ACS-8824] Disable node properties save button if values are invalid (#10560)
* [ACS-8824] Disable node properties save button if values are invalid * [ACS-8824] remove commented code * [ACS-8824] unit tests [ci:force] * [ACS-8824] cr fixes
This commit is contained in:
committed by
GitHub
parent
1634716bbe
commit
eaa1008018
@@ -38,7 +38,7 @@
|
|||||||
(click)="saveChanges($event)"
|
(click)="saveChanges($event)"
|
||||||
color="primary"
|
color="primary"
|
||||||
data-automation-id="save-general-info-metadata"
|
data-automation-id="save-general-info-metadata"
|
||||||
[disabled]="!hasMetadataChanged">
|
[disabled]="!hasMetadataChanged || invalidProperties.size > 0">
|
||||||
<mat-icon>check</mat-icon>
|
<mat-icon>check</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -235,7 +235,7 @@
|
|||||||
(click)="saveChanges($event)"
|
(click)="saveChanges($event)"
|
||||||
color="primary"
|
color="primary"
|
||||||
data-automation-id="save-metadata"
|
data-automation-id="save-metadata"
|
||||||
[disabled]="!hasMetadataChanged">
|
[disabled]="!hasMetadataChanged || invalidProperties.size > 0">
|
||||||
<mat-icon>check</mat-icon>
|
<mat-icon>check</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -114,7 +114,8 @@ describe('ContentMetadataComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
};
|
};
|
||||||
|
|
||||||
const clickOnGroupSave = () => fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]')).nativeElement.click();
|
const getGroupSaveButton = () => fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]')).nativeElement;
|
||||||
|
const clickOnGroupSaveButton = () => getGroupSaveButton().click();
|
||||||
|
|
||||||
const findTagsCreator = (): TagsCreatorComponent => fixture.debugElement.query(By.directive(TagsCreatorComponent))?.componentInstance;
|
const findTagsCreator = (): TagsCreatorComponent => fixture.debugElement.query(By.directive(TagsCreatorComponent))?.componentInstance;
|
||||||
const getToggleEditButton = () => fixture.debugElement.query(By.css('[data-automation-id="meta-data-general-info-edit"]'));
|
const getToggleEditButton = () => fixture.debugElement.query(By.css('[data-automation-id="meta-data-general-info-edit"]'));
|
||||||
@@ -288,6 +289,135 @@ describe('ContentMetadataComponent', () => {
|
|||||||
expect(contentMetadataService.getGroupedProperties).toHaveBeenCalled();
|
expect(contentMetadataService.getGroupedProperties).toHaveBeenCalled();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('Save button - Grouped Properties', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
getGroupedPropertiesSpy.and.returnValue(
|
||||||
|
of([
|
||||||
|
{
|
||||||
|
editable: true,
|
||||||
|
title: 'test',
|
||||||
|
properties: []
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
component.readOnly = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable the save button if metadata has not changed', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
toggleEditModeForGroup();
|
||||||
|
expect(getGroupSaveButton().disabled).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable the save button if there are invalid properties', fakeAsync(() => {
|
||||||
|
updateService.update(
|
||||||
|
{
|
||||||
|
key: 'properties.property-key',
|
||||||
|
isValidValue: false
|
||||||
|
} as CardViewBaseItemModel,
|
||||||
|
'updated-value'
|
||||||
|
);
|
||||||
|
tick(500);
|
||||||
|
fixture.detectChanges();
|
||||||
|
toggleEditModeForGroup();
|
||||||
|
expect(getGroupSaveButton().disabled).toBeTrue();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should enable the save button if metadata has changed and there are no invalid properties', fakeAsync(() => {
|
||||||
|
updateService.update(
|
||||||
|
{
|
||||||
|
key: 'properties.property-key',
|
||||||
|
isValidValue: true
|
||||||
|
} as CardViewBaseItemModel,
|
||||||
|
'updated-value'
|
||||||
|
);
|
||||||
|
tick(500);
|
||||||
|
fixture.detectChanges();
|
||||||
|
toggleEditModeForGroup();
|
||||||
|
expect(getGroupSaveButton().disabled).toBeFalse();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Save button - Basic Properties', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
spyOn(contentMetadataService, 'getBasicProperties').and.returnValue(of([]));
|
||||||
|
component.ngOnInit();
|
||||||
|
component.readOnly = false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should disable the save button if metadata has not changed', fakeAsync(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
toggleEditModeForGeneralInfo();
|
||||||
|
expect(findSaveGeneralInfoButton().disabled).toBeTrue();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should enable the save button if metadata has changed and there are no invalid properties', fakeAsync(() => {
|
||||||
|
updateService.update(
|
||||||
|
{
|
||||||
|
key: 'properties.property-key',
|
||||||
|
isValidValue: true
|
||||||
|
} as CardViewBaseItemModel,
|
||||||
|
'updated-value'
|
||||||
|
);
|
||||||
|
tick(500);
|
||||||
|
fixture.detectChanges();
|
||||||
|
toggleEditModeForGeneralInfo();
|
||||||
|
expect(findSaveGeneralInfoButton().disabled).toBeFalse();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should disable the save button if there are invalid properties', fakeAsync(() => {
|
||||||
|
updateService.update(
|
||||||
|
{
|
||||||
|
key: 'properties.property-key',
|
||||||
|
isValidValue: false
|
||||||
|
} as CardViewBaseItemModel,
|
||||||
|
'updated-value'
|
||||||
|
);
|
||||||
|
tick(500);
|
||||||
|
fixture.detectChanges();
|
||||||
|
toggleEditModeForGeneralInfo();
|
||||||
|
expect(findSaveGeneralInfoButton().disabled).toBeTrue();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateInvalidProperties', () => {
|
||||||
|
it('should add the property key to invalidProperties if isValidValue is false and key is not present', fakeAsync(() => {
|
||||||
|
const property = { key: 'properties.property-key', isValidValue: false } as CardViewBaseItemModel;
|
||||||
|
expect(component.invalidProperties.size).toBe(0);
|
||||||
|
updateService.update(property, 'updated-value');
|
||||||
|
tick(500);
|
||||||
|
expect(component.invalidProperties.has(property.key)).toBeTrue();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not add the property key to invalidProperties if isValidValue is false and key is already present', fakeAsync(() => {
|
||||||
|
const property = { key: 'properties.property-key', isValidValue: false } as CardViewBaseItemModel;
|
||||||
|
updateService.update(property, 'updated-value-1');
|
||||||
|
tick(500);
|
||||||
|
updateService.update(property, 'updated-value-2');
|
||||||
|
tick(500);
|
||||||
|
expect(component.invalidProperties.size).toBe(1);
|
||||||
|
expect(component.invalidProperties.has(property.key)).toBeTrue();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should remove the property key from invalidProperties if isValidValue is true and key is present', fakeAsync(() => {
|
||||||
|
updateService.update({ key: 'properties.property-key', isValidValue: false } as CardViewBaseItemModel, 'updated-value');
|
||||||
|
tick(500);
|
||||||
|
expect(component.invalidProperties).toContain('properties.property-key');
|
||||||
|
updateService.update({ key: 'properties.property-key', isValidValue: true } as CardViewBaseItemModel, 'updated-value');
|
||||||
|
tick(500);
|
||||||
|
expect(component.invalidProperties.has('properties.property-key')).toBeFalse();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not change invalidProperties if isValidValue is true and key is not present', fakeAsync(() => {
|
||||||
|
expect(component.invalidProperties.size).toBe(0);
|
||||||
|
updateService.update({ key: 'properties.property-key', isValidValue: true } as CardViewBaseItemModel, 'updated-value');
|
||||||
|
tick(500);
|
||||||
|
expect(component.invalidProperties.size).toBe(0);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it('should save changedProperties on save click', fakeAsync(() => {
|
it('should save changedProperties on save click', fakeAsync(() => {
|
||||||
getGroupedPropertiesSpy.and.returnValue(
|
getGroupedPropertiesSpy.and.returnValue(
|
||||||
of([
|
of([
|
||||||
@@ -309,7 +439,7 @@ describe('ContentMetadataComponent', () => {
|
|||||||
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
|
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
|
||||||
toggleEditModeForGroup();
|
toggleEditModeForGroup();
|
||||||
|
|
||||||
clickOnGroupSave();
|
clickOnGroupSaveButton();
|
||||||
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
||||||
expect(component.node).toEqual(expectedNode);
|
expect(component.node).toEqual(expectedNode);
|
||||||
}));
|
}));
|
||||||
|
@@ -164,6 +164,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
categoriesManagementMode = CategoriesManagementMode.ASSIGN;
|
categoriesManagementMode = CategoriesManagementMode.ASSIGN;
|
||||||
classifiableChanged = this.classifiableChangedSubject.asObservable();
|
classifiableChanged = this.classifiableChangedSubject.asObservable();
|
||||||
editing = false;
|
editing = false;
|
||||||
|
invalidProperties = new Set<string>();
|
||||||
editedPanelTitle = '';
|
editedPanelTitle = '';
|
||||||
currentPanel: ContentMetadataPanel = {
|
currentPanel: ContentMetadataPanel = {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
@@ -194,6 +195,7 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
.subscribe((updatedNode: UpdateNotification) => {
|
.subscribe((updatedNode: UpdateNotification) => {
|
||||||
this.hasMetadataChanged = true;
|
this.hasMetadataChanged = true;
|
||||||
this.targetProperty = updatedNode.target;
|
this.targetProperty = updatedNode.target;
|
||||||
|
this.updateInvalidProperties();
|
||||||
this.updateChanges(updatedNode.changed);
|
this.updateChanges(updatedNode.changed);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -549,4 +551,12 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
return observables;
|
return observables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateInvalidProperties() {
|
||||||
|
if (this.targetProperty?.isValidValue === false) {
|
||||||
|
this.invalidProperties.add(this.targetProperty.key);
|
||||||
|
} else if (this.targetProperty?.isValidValue === true) {
|
||||||
|
this.invalidProperties.delete(this.targetProperty.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,13 +17,7 @@
|
|||||||
|
|
||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { Node } from '@alfresco/js-api';
|
import { Node } from '@alfresco/js-api';
|
||||||
import {
|
import { CardViewDateItemModel, CardViewItemMatchValidator, CardViewTextItemModel, FileSizePipe, TranslationService } from '@alfresco/adf-core';
|
||||||
CardViewDateItemModel,
|
|
||||||
CardViewItemMatchValidator,
|
|
||||||
CardViewTextItemModel,
|
|
||||||
FileSizePipe,
|
|
||||||
TranslationService
|
|
||||||
} from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -44,9 +38,7 @@ export class BasicPropertiesService {
|
|||||||
value: node.name,
|
value: node.name,
|
||||||
key: 'properties.cm:name',
|
key: 'properties.cm:name',
|
||||||
editable: true,
|
editable: true,
|
||||||
validators: [
|
validators: [new CardViewItemMatchValidator('[\\/\\*\\\\"\\\\:|?<>]')]
|
||||||
new CardViewItemMatchValidator('[\\/\\*\\\\"\\\\:]')
|
|
||||||
]
|
|
||||||
}),
|
}),
|
||||||
new CardViewTextItemModel({
|
new CardViewTextItemModel({
|
||||||
label: 'CORE.METADATA.BASIC.TITLE',
|
label: 'CORE.METADATA.BASIC.TITLE',
|
||||||
|
@@ -505,7 +505,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
||||||
target: { ...component.property },
|
target: { ...component.property, isValidValue: true },
|
||||||
changed: {
|
changed: {
|
||||||
textkey: expectedText
|
textkey: expectedText
|
||||||
}
|
}
|
||||||
@@ -580,11 +580,11 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await updateTextField(component.property.key, 'updated-value');
|
await updateTextField(component.property.key, 'updated-value');
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
const property = { ...component.property };
|
const property = { ...component.property, isValidValue: true };
|
||||||
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, 'updated-value');
|
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, 'updated-value');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT trigger the update event if the editedValue is invalid', async () => {
|
it('should trigger the update event if the editedValue is NOT valid', async () => {
|
||||||
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
||||||
spyOn(cardViewUpdateService, 'update');
|
spyOn(cardViewUpdateService, 'update');
|
||||||
component.property.isValid = () => false;
|
component.property.isValid = () => false;
|
||||||
@@ -592,7 +592,8 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await updateTextField(component.property.key, '@invalid-value');
|
await updateTextField(component.property.key, '@invalid-value');
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(cardViewUpdateService.update).not.toHaveBeenCalled();
|
const property = { ...component.property, isValidValue: false };
|
||||||
|
expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, '@invalid-value');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger the update event if the editedValue is valid', async () => {
|
it('should trigger the update event if the editedValue is valid', async () => {
|
||||||
@@ -666,7 +667,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
||||||
target: { ...component.property },
|
target: { ...component.property, isValidValue: true },
|
||||||
changed: {
|
changed: {
|
||||||
textkey: expectedText
|
textkey: expectedText
|
||||||
}
|
}
|
||||||
@@ -711,7 +712,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
||||||
target: { ...component.property },
|
target: { ...component.property, isValidValue: true },
|
||||||
changed: {
|
changed: {
|
||||||
textkey: expectedText
|
textkey: expectedText
|
||||||
}
|
}
|
||||||
@@ -826,7 +827,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
||||||
target: { ...component.property },
|
target: { ...component.property, isValidValue: true },
|
||||||
changed: {
|
changed: {
|
||||||
textkey: expectedNumber.toString()
|
textkey: expectedNumber.toString()
|
||||||
}
|
}
|
||||||
@@ -882,7 +883,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
expect(itemUpdatedSpy).toHaveBeenCalledWith({
|
||||||
target: { ...component.property },
|
target: { ...component.property, isValidValue: true },
|
||||||
changed: {
|
changed: {
|
||||||
textkey: expectedNumber.toString()
|
textkey: expectedNumber.toString()
|
||||||
}
|
}
|
||||||
|
@@ -15,16 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { ChangeDetectorRef, Component, DestroyRef, inject, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
DestroyRef,
|
|
||||||
inject,
|
|
||||||
Input,
|
|
||||||
OnChanges,
|
|
||||||
SimpleChanges,
|
|
||||||
ViewEncapsulation
|
|
||||||
} from '@angular/core';
|
|
||||||
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
||||||
import { BaseCardView } from '../base-card-view';
|
import { BaseCardView } from '../base-card-view';
|
||||||
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
|
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
|
||||||
@@ -91,7 +82,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
errors: CardViewItemValidator[];
|
errors: CardViewItemValidator[];
|
||||||
templateType: string;
|
templateType: string;
|
||||||
textInput = new UntypedFormControl();
|
textInput = new UntypedFormControl();
|
||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
constructor(private clipboardService: ClipboardService, private translateService: TranslationService, private cd: ChangeDetectorRef) {
|
constructor(private clipboardService: ClipboardService, private translateService: TranslationService, private cd: ChangeDetectorRef) {
|
||||||
@@ -158,9 +149,10 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
this.resetErrorMessages();
|
this.resetErrorMessages();
|
||||||
if (this.property.isValid(this.editedValue)) {
|
if (this.property.isValid(this.editedValue)) {
|
||||||
this.property.value = this.prepareValueForUpload(this.property, this.editedValue);
|
this.property.value = this.prepareValueForUpload(this.property, this.editedValue);
|
||||||
this.cardViewUpdateService.update({ ...this.property } as CardViewTextItemModel, this.property.value);
|
this.cardViewUpdateService.update({ ...this.property, isValidValue: true } as CardViewTextItemModel, this.property.value);
|
||||||
} else {
|
} else {
|
||||||
this.errors = this.property.getValidationErrors(this.editedValue);
|
this.errors = this.property.getValidationErrors(this.editedValue);
|
||||||
|
this.cardViewUpdateService.update({ ...this.property, isValidValue: false } as CardViewTextItemModel, this.editedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ export abstract class CardViewBaseItemModel<T = any> {
|
|||||||
data?: any;
|
data?: any;
|
||||||
type?: string;
|
type?: string;
|
||||||
multivalued?: boolean;
|
multivalued?: boolean;
|
||||||
|
isValidValue?: boolean;
|
||||||
|
|
||||||
constructor(props: CardViewItemProperties) {
|
constructor(props: CardViewItemProperties) {
|
||||||
this.label = props.label || '';
|
this.label = props.label || '';
|
||||||
|
Reference in New Issue
Block a user