[ADF-4900] Card View and Metadata Components refactoring (#5592)

* [ADF-4900] Card View and Metadata Components refactoring

* CSS linting

* Unit test excluded

* Rebase branch

* Fix unit tests

* Fix linting

* Fix e2e tests

* Fix 2e2 tests

* Fix process-services e2e tests

* More fixes

* Fix more e2e tests

* Fix unit test

* Improve flaky unit test

* Fix process services e2e tests

* Update Process Header Cloud Page

* Fix linting

* Fix timing issue

* Lintintg

* Fix selectors

* Fix e2e tests

* Fix timing issue

* Fix C260328

* Fix spellcheck

* save screenshot

* performance issue

* Fix unit tests and e2e tests

* fix e2e

* refactoring

* fix lint

* fix e2e

* Fix C309698

* fix other e2e

* fix lint

* increase timeout

Co-authored-by: Eugenio Romano <eugenio.romano@alfresco.com>
This commit is contained in:
davidcanonieto
2020-04-27 00:09:52 +01:00
committed by GitHub
parent ebfeb053ce
commit 8f68899ce0
65 changed files with 1211 additions and 1214 deletions

View File

@@ -1,9 +1,9 @@
<div class="adf-metadata-properties">
<mat-accordion displayMode="flat" [multi]="multi">
<mat-expansion-panel
*ngIf="displayDefaultProperties"
[expanded]="canExpandProperties()"
[attr.data-automation-id]="'adf-metadata-group-properties'" >
<mat-accordion displayMode="flat"
[multi]="multi">
<mat-expansion-panel *ngIf="displayDefaultProperties"
[expanded]="canExpandProperties()"
[attr.data-automation-id]="'adf-metadata-group-properties'">
<mat-expansion-panel-header>
<mat-panel-title role="heading">
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
@@ -23,10 +23,11 @@
<ng-container *ngIf="expanded">
<ng-container *ngIf="groupedProperties$ | async; else loading; let groupedProperties">
<div *ngFor="let group of groupedProperties; let first = first;" class="adf-metadata-grouped-properties-container">
<div *ngFor="let group of groupedProperties; let first = first;"
class="adf-metadata-grouped-properties-container">
<mat-expansion-panel *ngIf="showGroup(group) || editable"
[attr.data-automation-id]="'adf-metadata-group-' + group.title"
[expanded]="canExpandTheCard(group) || !displayDefaultProperties && first">
[attr.data-automation-id]="'adf-metadata-group-' + group.title"
[expanded]="canExpandTheCard(group) || !displayDefaultProperties && first">
<mat-expansion-panel-header>
<mat-panel-title>
{{ group.title | translate }}
@@ -51,4 +52,18 @@
</ng-template>
</ng-container>
</mat-accordion>
<div class="adf-metadata-action-buttons"
*ngIf="editable">
<button mat-button
(click)="cancelChanges()"
data-automation-id="reset-metadata"
[disabled]="!hasMetadataChanged">Cancel</button>
<button mat-raised-button
(click)="saveChanges()"
color="primary"
data-automation-id="save-metadata"
[disabled]="!hasMetadataChanged">Save</button>
</div>
</div>

View File

@@ -13,9 +13,15 @@
height: 64px;
}
.mat-expansion-panel:not([class*=mat-elevation-z]) {
.mat-expansion-panel:not([class*='mat-elevation-z']) {
box-shadow: none;
}
}
&-metadata-action-buttons {
display: flex;
justify-content: space-evenly;
margin: 10px;
}
}
}

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { Node } from '@alfresco/js-api';
@@ -108,49 +108,47 @@ describe('ContentMetadataComponent', () => {
});
describe('Saving', () => {
it('should save the node on itemUpdate', () => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
spyOn(nodesApiService, 'updateNode').and.callThrough();
it('itemUpdate', fakeAsync(() => {
spyOn(component, 'updateChanges').and.callThrough();
const property = <CardViewBaseItemModel> { key: 'properties.property-key', value: 'original-value' };
updateService.update(property, 'updated-value');
expect(nodesApiService.updateNode).toHaveBeenCalledWith('node-id', {
'property-key': 'updated-value'
});
});
tick(600);
expect(component.hasMetadataChanged).toEqual(true);
expect(component.updateChanges).toHaveBeenCalled();
expect(component.changedProperties).toEqual({ properties: { 'property-key': 'updated-value' } });
}));
it('should update the node on successful save', async(() => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
it('should save changedProperties on save click', fakeAsync(async () => {
component.editable = true;
const property = <CardViewBaseItemModel> { key: 'properties.property-key', value: 'original-value' };
const expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
return of(expectedNode);
});
updateService.update(property, 'updated-value');
tick(600);
fixture.whenStable().then(() => {
expect(component.node).toEqual(expectedNode);
});
fixture.detectChanges();
await fixture.whenStable();
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
saveButton.nativeElement.click();
await fixture.whenStable();
expect(component.node).toEqual(expectedNode);
expect(nodesApiService.updateNode).toHaveBeenCalled();
}));
it('should throw error on unsuccessful save', () => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
it('should throw error on unsuccessful save', fakeAsync(async (done) => {
const logService: LogService = TestBed.get(LogService);
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
return throwError(new Error('My bad'));
});
component.editable = true;
const property = <CardViewBaseItemModel> { key: 'properties.property-key', value: 'original-value' };
updateService.update(property, 'updated-value');
expect(logService.error).toHaveBeenCalledWith(new Error('My bad'));
});
it('should raise error message', (done) => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
tick(600);
const sub = contentMetadataService.error.subscribe((err) => {
expect(logService.error).toHaveBeenCalledWith(new Error('My bad'));
expect(err.statusCode).toBe(0);
expect(err.message).toBe('METADATA.ERRORS.GENERIC');
sub.unsubscribe();
@@ -161,8 +159,33 @@ describe('ContentMetadataComponent', () => {
return throwError(new Error('My bad'));
});
updateService.update(property, 'updated-value');
});
fixture.detectChanges();
await fixture.whenStable();
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
saveButton.nativeElement.click();
fixture.detectChanges();
}));
});
describe('Reseting', () => {
it('should reset changedProperties on reset click', async(async () => {
component.changedProperties = { properties: { 'property-key': 'updated-value' } };
component.hasMetadataChanged = true;
component.editable = true;
const expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
return of(expectedNode);
});
fixture.detectChanges();
await fixture.whenStable();
const resetButton = fixture.debugElement.query(By.css('[data-automation-id="reset-metadata"]'));
resetButton.nativeElement.click();
fixture.detectChanges();
expect(component.changedProperties).toEqual({});
expect(nodesApiService.updateNode).not.toHaveBeenCalled();
}));
});
describe('Properties loading', () => {
@@ -271,14 +294,16 @@ describe('ContentMetadataComponent', () => {
it('should display card views group when there is at least one property that is not empty', async(() => {
component.expanded = true;
fixture.detectChanges();
const cardViewGroup = {title: 'Group 1', properties: [{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}]};
const cardViewGroup = {
title: 'Group 1', properties: [{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}]
};
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of([{ properties: [cardViewGroup] }]));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
@@ -317,9 +342,9 @@ describe('ContentMetadataComponent', () => {
let expectedNode;
beforeEach(() => {
expectedNode = Object.assign({}, node, {name: 'some-modified-value'});
expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of(mockGroupProperties));
component.ngOnChanges({node: new SimpleChange(node, expectedNode, false)});
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
});
it('should open and update drawer with expand section dynamically', async(() => {
@@ -373,7 +398,7 @@ describe('ContentMetadataComponent', () => {
describe('events', () => {
it('should not propagate the event on left arrows press', () => {
fixture.detectChanges();
const event = { keyCode: 37, stopPropagation: () => {} };
const event = { keyCode: 37, stopPropagation: () => { } };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -382,7 +407,7 @@ describe('ContentMetadataComponent', () => {
it('should not propagate the event on right arrows press', () => {
fixture.detectChanges();
const event = { keyCode: 39, stopPropagation: () => {} };
const event = { keyCode: 39, stopPropagation: () => { } };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -391,7 +416,7 @@ describe('ContentMetadataComponent', () => {
it('should propagate the event on other keys press', () => {
fixture.detectChanges();
const event = { keyCode: 40, stopPropagation: () => {} };
const event = { keyCode: 40, stopPropagation: () => { } };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -401,5 +426,5 @@ describe('ContentMetadataComponent', () => {
});
function queryDom(fixture: ComponentFixture<ContentMetadataComponent>, properties: string = 'properties') {
return fixture.debugElement.query(By.css(`[data-automation-id="adf-metadata-group-${properties}"]`));
return fixture.debugElement.query(By.css(`[data-automation-id="adf-metadata-group-${properties}"]`));
}

View File

@@ -25,11 +25,12 @@ import {
CardViewUpdateService,
AlfrescoApiService,
TranslationService,
AppConfigService
AppConfigService,
CardViewBaseItemModel
} from '@alfresco/adf-core';
import { ContentMetadataService } from '../../services/content-metadata.service';
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
import { switchMap, takeUntil, catchError } from 'rxjs/operators';
import { takeUntil, debounceTime, catchError } from 'rxjs/operators';
@Component({
selector: 'adf-content-metadata',
@@ -90,6 +91,10 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
basicProperties$: Observable<CardViewItem[]>;
groupedProperties$: Observable<CardViewGroup[]>;
changedProperties = {};
hasMetadataChanged = false;
private targetProperty: CardViewBaseItemModel;
constructor(
private contentMetadataService: ContentMetadataService,
private cardViewUpdateService: CardViewUpdateService,
@@ -107,23 +112,13 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
ngOnInit() {
this.cardViewUpdateService.itemUpdated$
.pipe(
switchMap((changes) =>
this.saveNode(changes).pipe(
catchError((err) => {
this.cardViewUpdateService.updateElement(changes.target);
this.handleUpdateError(err);
return of(null);
})
)
),
takeUntil(this.onDestroy$)
)
debounceTime(500),
takeUntil(this.onDestroy$))
.subscribe(
(updatedNode) => {
if (updatedNode) {
Object.assign(this.node, updatedNode);
this.alfrescoApiService.nodeUpdated.next(this.node);
}
this.hasMetadataChanged = true;
this.targetProperty = updatedNode.target;
this.updateChanges(updatedNode.changed);
}
);
@@ -165,8 +160,43 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
}
}
private saveNode({ changed: nodeBody }): Observable<Node> {
return this.nodesApiService.updateNode(this.node.id, nodeBody);
updateChanges(updatedNodeChanges) {
Object.keys(updatedNodeChanges).map((propertyGroup: string) => {
if (typeof updatedNodeChanges[propertyGroup] === 'object') {
this.changedProperties[propertyGroup] = {
...this.changedProperties[propertyGroup],
...updatedNodeChanges[propertyGroup]
};
} else {
this.changedProperties[propertyGroup] = updatedNodeChanges[propertyGroup];
}
});
}
saveChanges() {
this.nodesApiService.updateNode(this.node.id, this.changedProperties).pipe(
catchError((err) => {
this.cardViewUpdateService.updateElement(this.targetProperty);
this.handleUpdateError(err);
return of(null);
}))
.subscribe((updatedNode) => {
if (updatedNode) {
this.revertChanges();
Object.assign(this.node, updatedNode);
this.alfrescoApiService.nodeUpdated.next(this.node);
}
});
}
revertChanges() {
this.changedProperties = {};
this.hasMetadataChanged = false;
}
cancelChanges() {
this.revertChanges();
this.loadProperties(this.node);
}
showGroup(group: CardViewGroup): boolean {
@@ -195,5 +225,4 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
event.stopPropagation();
}
}
}