mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
[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:
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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}"]`));
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user