mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-24 17:31:52 +00:00
[ACS-3219] Delete Rule (#2609)
* [ACS-3219] Delete Rule * rebased + added unsubscribe() * small fix * a couple more fixes * manage-rules css formatting fix
This commit is contained in:
committed by
GitHub
parent
69cb107dd9
commit
db47d862e3
@@ -83,6 +83,12 @@
|
||||
"TITLE": "The list is empty",
|
||||
"SUBTITLE": "There are no rules defined for this folder yet."
|
||||
}
|
||||
},
|
||||
"CONFIRMATION_DIALOG": {
|
||||
"DELETE_RULE": {
|
||||
"TITLE": "Delete rule",
|
||||
"MESSAGE": "Are you sure you want to delete this rule?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,18 @@
|
||||
<div class="aca-manage-rules__container" *ngIf="(rules$ | async).length > 0 ; else emptyContent">
|
||||
<aca-rules-list [rules]="rules$ | async" (ruleSelected)="onRuleSelected($event)" [selectedRule]="selectedRule"></aca-rules-list>
|
||||
<div class="aca-manage-rules__container__rule-details">
|
||||
<aca-rule-details [readOnly]="true" [value]="selectedRule"></aca-rule-details>
|
||||
<div class="aca-manage-rules__container__preview">
|
||||
<div class="aca-manage-rules__container__preview__toolbar">
|
||||
<span>{{ selectedRule.name }}</span>
|
||||
<div class="aca-manage-rules__container__preview__toolbar__buttons">
|
||||
<button mat-icon-button (click)="onRuleDelete()" id="delete-rule-btn">
|
||||
<mat-icon>delete_outline</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ selectedRule.description }}</p>
|
||||
</div>
|
||||
<aca-rule-details [readOnly]="true" [preview]="true" [value]="selectedRule"></aca-rule-details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -21,6 +21,34 @@
|
||||
padding: 32px;
|
||||
overflow: scroll;
|
||||
|
||||
&__preview {
|
||||
padding: 0 20px;
|
||||
|
||||
&__toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
span {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__rule-details {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ import { of } from 'rxjs';
|
||||
import { dummyRules } from '../mock/rules.mock';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { dummyNodeInfo } from '../mock/node.mock';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
describe('ManageRulesSmartComponent', () => {
|
||||
let fixture: ComponentFixture<ManageRulesSmartComponent>;
|
||||
@@ -42,7 +43,7 @@ describe('ManageRulesSmartComponent', () => {
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
const folderRulesServiceSpy = jasmine.createSpyObj('FolderRulesService', ['loadRules']);
|
||||
const folderRulesServiceSpy = jasmine.createSpyObj('FolderRulesService', ['loadRules', 'deleteRule']);
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule, AcaFolderRulesModule],
|
||||
providers: [
|
||||
@@ -61,6 +62,7 @@ describe('ManageRulesSmartComponent', () => {
|
||||
);
|
||||
|
||||
it('should display aca-rules-list and aca-rule-details', () => {
|
||||
folderRulesService.deletedRuleId$ = of(null);
|
||||
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
||||
folderRulesService.rulesListing$ = of(dummyRules);
|
||||
folderRulesService.loading$ = of(false);
|
||||
@@ -73,15 +75,18 @@ describe('ManageRulesSmartComponent', () => {
|
||||
|
||||
const rules = debugElement.queryAll(By.css('.aca-rule'));
|
||||
const ruleDetails = debugElement.queryAll(By.css('aca-rule-details'));
|
||||
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
|
||||
|
||||
expect(rules.length).toBe(2, 'Unexpected number of aca-rule');
|
||||
expect(rules.length).toBe(2, 'unexpected number of aca-rule');
|
||||
expect(ruleDetails.length).toBeTruthy('aca-rule-details was not rendered');
|
||||
expect(deleteRuleBtn).toBeTruthy('no delete rule button');
|
||||
});
|
||||
|
||||
it('should only show adf-empty-content if provided node has no rules defined yet', () => {
|
||||
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
||||
folderRulesService.rulesListing$ = of([]);
|
||||
folderRulesService.loading$ = of(false);
|
||||
folderRulesService.deletedRuleId$ = of(null);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -98,6 +103,7 @@ describe('ManageRulesSmartComponent', () => {
|
||||
|
||||
it('should only show aca-generic-error if the non-existing node was provided', () => {
|
||||
folderRulesService.folderInfo$ = of(null);
|
||||
folderRulesService.deletedRuleId$ = of(null);
|
||||
folderRulesService.rulesListing$ = of([]);
|
||||
folderRulesService.loading$ = of(false);
|
||||
|
||||
@@ -116,6 +122,7 @@ describe('ManageRulesSmartComponent', () => {
|
||||
|
||||
it('should only show progress bar while loading', () => {
|
||||
folderRulesService.folderInfo$ = of(null);
|
||||
folderRulesService.deletedRuleId$ = of(null);
|
||||
folderRulesService.rulesListing$ = of([]);
|
||||
folderRulesService.loading$ = of(true);
|
||||
|
||||
@@ -131,4 +138,57 @@ describe('ManageRulesSmartComponent', () => {
|
||||
expect(rules).toBeFalsy();
|
||||
expect(ruleDetails).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should call deleteRule() if confirmation dialog returns true', () => {
|
||||
const dialog = TestBed.inject(MatDialog);
|
||||
folderRulesService.deletedRuleId$ = of(null);
|
||||
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
||||
folderRulesService.rulesListing$ = of(dummyRules);
|
||||
folderRulesService.loading$ = of(false);
|
||||
|
||||
spyOn(component, 'onRuleDelete').and.callThrough();
|
||||
|
||||
const dialogResult: any = {
|
||||
afterClosed: () =>
|
||||
of(true).subscribe((res) => {
|
||||
if (res === true) {
|
||||
folderRulesService.deleteRule(component.nodeId, component.selectedRule.id);
|
||||
}
|
||||
})
|
||||
};
|
||||
spyOn(dialog, 'open').and.returnValue(dialogResult);
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(component).toBeTruthy('expected component');
|
||||
|
||||
const rules = debugElement.queryAll(By.css('.aca-rule'));
|
||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||
const deleteRuleBtn = fixture.debugElement.nativeElement.querySelector('#delete-rule-btn');
|
||||
|
||||
deleteRuleBtn.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
folderRulesService.deletedRuleId$ = of(component.selectedRule.id);
|
||||
|
||||
expect(component.onRuleDelete).toHaveBeenCalled();
|
||||
expect(dialog.open).toHaveBeenCalled();
|
||||
expect(folderRulesService.deleteRule).toHaveBeenCalled();
|
||||
expect(folderRulesService.loadRules).toHaveBeenCalledTimes(1);
|
||||
expect(rules).toBeTruthy('expected rules');
|
||||
expect(ruleDetails).toBeTruthy('expected ruleDetails');
|
||||
expect(deleteRuleBtn).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should run loadRules() when deletedRuleId$ emits new value', () => {
|
||||
folderRulesService.deletedRuleId$ = of('new-value');
|
||||
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
||||
folderRulesService.rulesListing$ = of(dummyRules);
|
||||
folderRulesService.loading$ = of(false);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component).toBeTruthy();
|
||||
|
||||
expect(folderRulesService.loadRules).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
@@ -23,16 +23,17 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { FolderRulesService } from '../services/folder-rules.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { Rule } from '../model/rule.model';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { EditRuleDialogSmartComponent } from '../rule-details/edit-rule-dialog.smart-component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ConfirmDialogComponent } from '@alfresco/adf-content-services';
|
||||
|
||||
@Component({
|
||||
selector: 'aca-manage-rules',
|
||||
@@ -41,12 +42,13 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'aca-manage-rules' }
|
||||
})
|
||||
export class ManageRulesSmartComponent implements OnInit {
|
||||
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
||||
rules$: Observable<Rule[]>;
|
||||
isLoading$: Observable<boolean>;
|
||||
folderInfo$: Observable<NodeInfo>;
|
||||
selectedRule: Rule = null;
|
||||
nodeId: string = null;
|
||||
deletedRuleSubscription$: Subscription;
|
||||
|
||||
constructor(
|
||||
private location: Location,
|
||||
@@ -63,6 +65,11 @@ export class ManageRulesSmartComponent implements OnInit {
|
||||
}
|
||||
})
|
||||
);
|
||||
this.deletedRuleSubscription$ = this.folderRulesService.deletedRuleId$.subscribe((deletedRuleId) => {
|
||||
if (deletedRuleId) {
|
||||
this.folderRulesService.loadRules(this.nodeId);
|
||||
}
|
||||
});
|
||||
this.isLoading$ = this.folderRulesService.loading$;
|
||||
this.folderInfo$ = this.folderRulesService.folderInfo$;
|
||||
this.route.params.subscribe((params) => {
|
||||
@@ -73,6 +80,10 @@ export class ManageRulesSmartComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.deletedRuleSubscription$.unsubscribe();
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
@@ -87,4 +98,21 @@ export class ManageRulesSmartComponent implements OnInit {
|
||||
panelClass: 'aca-edit-rule-dialog-container'
|
||||
});
|
||||
}
|
||||
|
||||
onRuleDelete(): void {
|
||||
this.matDialogService
|
||||
.open(ConfirmDialogComponent, {
|
||||
data: {
|
||||
title: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE.TITLE',
|
||||
message: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE.MESSAGE'
|
||||
},
|
||||
minWidth: '346px'
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((result) => {
|
||||
if (result) {
|
||||
this.folderRulesService.deleteRule(this.nodeId, this.selectedRule.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,32 @@
|
||||
<form class="aca-rule-details__form" [formGroup]="form">
|
||||
<div class="aca-rule-details__form__row">
|
||||
<label for="rule-details-name-input">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.NAME' | translate }}</label>
|
||||
<div>
|
||||
<mat-form-field floatLabel='never'>
|
||||
<input
|
||||
id="rule-details-name-input"
|
||||
matInput type="text" formControlName="name" data-automation-id="rule-details-name-input"
|
||||
[placeholder]="getPlaceholder('name') | translate">
|
||||
<mat-error>{{ getErrorMessage(name) | translate }}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-details__form__row">
|
||||
<label for="rule-details-description-textarea">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.DESCRIPTION' | translate }}</label>
|
||||
<div>
|
||||
<mat-form-field floatLabel='never'>
|
||||
<ng-container *ngIf="!preview">
|
||||
<div class="aca-rule-details__form__row">
|
||||
<label for="rule-details-name-input">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.NAME' | translate }}</label>
|
||||
<div>
|
||||
<mat-form-field floatLabel='never'>
|
||||
<input
|
||||
id="rule-details-name-input"
|
||||
matInput type="text" formControlName="name" data-automation-id="rule-details-name-input"
|
||||
[placeholder]="getPlaceholder('name') | translate">
|
||||
<mat-error>{{ getErrorMessage(name) | translate }}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aca-rule-details__form__row">
|
||||
<label for="rule-details-description-textarea">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.DESCRIPTION' | translate }}</label>
|
||||
<div>
|
||||
<mat-form-field floatLabel='never'>
|
||||
<textarea
|
||||
id="rule-details-description-textarea"
|
||||
matInput formControlName="description" data-automation-id="rule-details-description-textarea"
|
||||
[placeholder]="getPlaceholder('description') | translate">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<hr>
|
||||
|
||||
|
@@ -72,6 +72,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
||||
this._initialValue = newValue;
|
||||
}
|
||||
}
|
||||
@Input()
|
||||
preview: boolean;
|
||||
|
||||
@Output()
|
||||
formValidationChanged = new EventEmitter<boolean>();
|
||||
|
@@ -39,25 +39,33 @@ describe('FolderRulesService', () => {
|
||||
let contentApi: ContentApiService;
|
||||
let rulesPromise: Promise<Partial<Rule>[]>;
|
||||
let folderInfoPromise: Promise<NodeInfo>;
|
||||
let deletedRulePromise: Promise<string>;
|
||||
let rules: Partial<Rule>[];
|
||||
let folderInfo: NodeInfo;
|
||||
let deletedRule: string;
|
||||
let apiCallSpy;
|
||||
let getNodeSpy;
|
||||
|
||||
const nodeId = '********-fake-node-****-********';
|
||||
const ruleId = '********-fake-rule-****-********';
|
||||
const ruleSetId = '-default-';
|
||||
const params = [{}, {}, {}, {}, {}, ['application/json'], ['application/json']];
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [FolderRulesService, ContentApiService]
|
||||
});
|
||||
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
|
||||
});
|
||||
|
||||
describe('loadRules', () => {
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule],
|
||||
providers: [FolderRulesService, ContentApiService]
|
||||
});
|
||||
|
||||
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
|
||||
contentApi = TestBed.inject<ContentApiService>(ContentApiService);
|
||||
|
||||
apiCallSpy = spyOn<any>(folderRulesService, 'apiCall').and.returnValue(of(dummyResponse) as any);
|
||||
apiCallSpy = spyOn<any>(folderRulesService, 'apiCall')
|
||||
.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'GET', params)
|
||||
.and.returnValue(of(dummyResponse) as any);
|
||||
getNodeSpy = spyOn<any>(contentApi, 'getNode').and.returnValue(of(dummyGetNodeResponse) as any);
|
||||
|
||||
rulesPromise = folderRulesService.rulesListing$.pipe(take(2)).toPromise();
|
||||
@@ -76,7 +84,29 @@ describe('FolderRulesService', () => {
|
||||
expect(rules).toEqual(dummyRules, 'The list of rules is incorrectly formatted');
|
||||
expect(folderInfo).toEqual(dummyNodeInfo, 'The node info is wrong');
|
||||
expect(apiCallSpy).toHaveBeenCalledTimes(1);
|
||||
expect(apiCallSpy).toHaveBeenCalledWith(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'GET', params);
|
||||
expect(getNodeSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteRule', () => {
|
||||
beforeEach(async () => {
|
||||
apiCallSpy = spyOn<any>(folderRulesService, 'apiCall')
|
||||
.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE', params)
|
||||
.and.returnValue(ruleId);
|
||||
|
||||
deletedRulePromise = folderRulesService.deletedRuleId$.pipe(take(2)).toPromise();
|
||||
|
||||
folderRulesService.deleteRule(nodeId, ruleId, ruleSetId);
|
||||
|
||||
deletedRule = await deletedRulePromise;
|
||||
});
|
||||
|
||||
it('should delete a rule and return its id', async () => {
|
||||
expect(deletedRule).toBeTruthy('rule has not been deleted');
|
||||
expect(deletedRule).toBe(ruleId, 'wrong id of deleted rule');
|
||||
expect(apiCallSpy).toHaveBeenCalledTimes(1);
|
||||
expect(apiCallSpy).toHaveBeenCalledWith(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE', params);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -68,6 +68,8 @@ export class FolderRulesService {
|
||||
folderInfo$: Observable<NodeInfo> = this.folderInfoSource.asObservable();
|
||||
private loadingSource = new BehaviorSubject<boolean>(false);
|
||||
loading$ = this.loadingSource.asObservable();
|
||||
private deletedRuleIdSource = new BehaviorSubject<string>(null);
|
||||
deletedRuleId$: Observable<string> = this.deletedRuleIdSource.asObservable();
|
||||
|
||||
constructor(private apiService: AlfrescoApiService, private contentApi: ContentApiService) {}
|
||||
|
||||
@@ -107,6 +109,29 @@ export class FolderRulesService {
|
||||
);
|
||||
}
|
||||
|
||||
deleteRule(nodeId: string, ruleId: string, ruleSetId: string = '-default-'): void {
|
||||
this.loadingSource.next(true);
|
||||
from(
|
||||
this.apiCall(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE', [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
['application/json'],
|
||||
['application/json']
|
||||
])
|
||||
).subscribe(
|
||||
() => {
|
||||
this.deletedRuleIdSource.next(ruleId);
|
||||
},
|
||||
(error) => {
|
||||
this.deletedRuleIdSource.next(error);
|
||||
this.loadingSource.next(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private apiCall(path: string, httpMethod: string, params?: any[]): Promise<any> {
|
||||
return this.apiService.getInstance().contentClient.callApi(path, httpMethod, ...params);
|
||||
}
|
||||
|
Reference in New Issue
Block a user