mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACA-3258] Edit rule dialog actions section (#2692)
* Add actions service * Create components * Rebasing * Add card view component * Moved actions definition call outside of actions list component * Localisation of parameter and action labels + read only mode for components * Remove action option * Default to one item in array * Handle change of cardview * Linting * Add unit tests * Fix broken unit tests * Fix unknown word * Add private to property
This commit is contained in:
parent
08d4f46573
commit
59c7d68299
@ -15,6 +15,7 @@
|
|||||||
"NAME": "Name",
|
"NAME": "Name",
|
||||||
"DESCRIPTION": "Description",
|
"DESCRIPTION": "Description",
|
||||||
"WHEN": "When",
|
"WHEN": "When",
|
||||||
|
"PERFORM_ACTIONS": "Perform actions",
|
||||||
"OPTIONS": "Other options"
|
"OPTIONS": "Other options"
|
||||||
},
|
},
|
||||||
"PLACEHOLDER": {
|
"PLACEHOLDER": {
|
||||||
@ -70,21 +71,26 @@
|
|||||||
"AND": "And",
|
"AND": "And",
|
||||||
"OR": "Or"
|
"OR": "Or"
|
||||||
},
|
},
|
||||||
"ACTIONS": {
|
"CONDITION_BUTTONS": {
|
||||||
"ADD_CONDITION": "Add condition",
|
"ADD_CONDITION": "Add condition",
|
||||||
"ADD_GROUP": "Add group",
|
"ADD_GROUP": "Add condition group",
|
||||||
"REMOVE": "Remove"
|
"REMOVE": "Remove"
|
||||||
},
|
},
|
||||||
"NO_CONDITIONS": "No conditions",
|
"NO_CONDITIONS": "No conditions",
|
||||||
"NO_CONDITIONS_IN_GROUP": "No conditions in the group"
|
"NO_CONDITIONS_IN_GROUP": "No conditions in the group",
|
||||||
|
"ACTION_BUTTONS": {
|
||||||
|
"ADD_ACTION": "Add action",
|
||||||
|
"REMOVE": "Remove"
|
||||||
},
|
},
|
||||||
|
"ACTION_SELECT_PLACEHOLDER": "Select an action"
|
||||||
|
},
|
||||||
"MANAGE_RULES": {
|
"MANAGE_RULES": {
|
||||||
"TOOLBAR": {
|
"TOOLBAR": {
|
||||||
"BREADCRUMB": {
|
"BREADCRUMB": {
|
||||||
"RULES": "rules"
|
"RULES": "rules"
|
||||||
},
|
},
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"NEW_RULE": "New rule"
|
"CREATE_RULE": "Create rule"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EMPTY_RULES_LIST": {
|
"EMPTY_RULES_LIST": {
|
||||||
|
@ -40,6 +40,8 @@ import { RuleListItemUiComponent } from './rules-list/rule/rule-list-item.ui-com
|
|||||||
import { RulesListUiComponent } from './rules-list/rules-list.ui-component';
|
import { RulesListUiComponent } from './rules-list/rules-list.ui-component';
|
||||||
import { RuleTriggersUiComponent } from './rule-details/triggers/rule-triggers.ui-component';
|
import { RuleTriggersUiComponent } from './rule-details/triggers/rule-triggers.ui-component';
|
||||||
import { RuleOptionsUiComponent } from './rule-details/options/rule-options.ui-component';
|
import { RuleOptionsUiComponent } from './rule-details/options/rule-options.ui-component';
|
||||||
|
import { RuleActionListUiComponent } from './rule-details/actions/rule-action-list.ui-component';
|
||||||
|
import { RuleActionUiComponent } from './rule-details/actions/rule-action.ui-component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -63,6 +65,8 @@ const routes: Routes = [
|
|||||||
declarations: [
|
declarations: [
|
||||||
EditRuleDialogSmartComponent,
|
EditRuleDialogSmartComponent,
|
||||||
ManageRulesSmartComponent,
|
ManageRulesSmartComponent,
|
||||||
|
RuleActionListUiComponent,
|
||||||
|
RuleActionUiComponent,
|
||||||
RuleCompositeConditionUiComponent,
|
RuleCompositeConditionUiComponent,
|
||||||
RuleDetailsUiComponent,
|
RuleDetailsUiComponent,
|
||||||
RuleSimpleConditionUiComponent,
|
RuleSimpleConditionUiComponent,
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<aca-page-layout-content>
|
<aca-page-layout-content>
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
|
|
||||||
<ng-container *ngIf="isLoading$ | async; else onLoaded">
|
<ng-container *ngIf="(rulesLoading$ | async) || (actionsLoading$ | async); else onLoaded">
|
||||||
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
@ -25,7 +25,7 @@
|
|||||||
class="aca-manage-rules__actions-bar__title__breadcrumb"></adf-breadcrumb>
|
class="aca-manage-rules__actions-bar__title__breadcrumb"></adf-breadcrumb>
|
||||||
</adf-toolbar-title>
|
</adf-toolbar-title>
|
||||||
|
|
||||||
<button mat-flat-button color="primary" (click)="openNewRuleDialog()">{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.NEW_RULE' | translate }}</button>
|
<button mat-flat-button color="primary" (click)="openNewRuleDialog()">{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.CREATE_RULE' | translate }}</button>
|
||||||
</adf-toolbar>
|
</adf-toolbar>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
@ -44,7 +44,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<p>{{ selectedRule.description }}</p>
|
<p>{{ selectedRule.description }}</p>
|
||||||
</div>
|
</div>
|
||||||
<aca-rule-details [readOnly]="true" [preview]="true" [value]="selectedRule"></aca-rule-details>
|
<aca-rule-details
|
||||||
|
[actionDefinitions]="actionDefinitions$ | async"
|
||||||
|
[readOnly]="true"
|
||||||
|
[preview]="true"
|
||||||
|
[value]="selectedRule">
|
||||||
|
</aca-rule-details>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 33% 66%;
|
grid-template-columns: minmax(200px,1fr) 3fr;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
|
@ -34,12 +34,14 @@ import { dummyRules } from '../mock/rules.mock';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { dummyNodeInfo } from '../mock/node.mock';
|
import { dummyNodeInfo } from '../mock/node.mock';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ActionsService } from '../services/actions.service';
|
||||||
|
|
||||||
describe('ManageRulesSmartComponent', () => {
|
describe('ManageRulesSmartComponent', () => {
|
||||||
let fixture: ComponentFixture<ManageRulesSmartComponent>;
|
let fixture: ComponentFixture<ManageRulesSmartComponent>;
|
||||||
let component: ManageRulesSmartComponent;
|
let component: ManageRulesSmartComponent;
|
||||||
let debugElement: DebugElement;
|
let debugElement: DebugElement;
|
||||||
let folderRulesService: FolderRulesService;
|
let folderRulesService: FolderRulesService;
|
||||||
|
let actionsService: ActionsService;
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(
|
||||||
waitForAsync(() => {
|
waitForAsync(() => {
|
||||||
@ -57,6 +59,7 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
debugElement = fixture.debugElement;
|
debugElement = fixture.debugElement;
|
||||||
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
|
folderRulesService = TestBed.inject<FolderRulesService>(FolderRulesService);
|
||||||
|
actionsService = TestBed.inject<ActionsService>(ActionsService);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -66,6 +69,7 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
||||||
folderRulesService.rulesListing$ = of(dummyRules);
|
folderRulesService.rulesListing$ = of(dummyRules);
|
||||||
folderRulesService.loading$ = of(false);
|
folderRulesService.loading$ = of(false);
|
||||||
|
actionsService.loading$ = of(false);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@ -87,6 +91,7 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
folderRulesService.rulesListing$ = of([]);
|
folderRulesService.rulesListing$ = of([]);
|
||||||
folderRulesService.loading$ = of(false);
|
folderRulesService.loading$ = of(false);
|
||||||
folderRulesService.deletedRuleId$ = of(null);
|
folderRulesService.deletedRuleId$ = of(null);
|
||||||
|
actionsService.loading$ = of(false);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@ -106,6 +111,7 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
folderRulesService.deletedRuleId$ = of(null);
|
folderRulesService.deletedRuleId$ = of(null);
|
||||||
folderRulesService.rulesListing$ = of([]);
|
folderRulesService.rulesListing$ = of([]);
|
||||||
folderRulesService.loading$ = of(false);
|
folderRulesService.loading$ = of(false);
|
||||||
|
actionsService.loading$ = of(false);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@ -120,11 +126,12 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
expect(ruleDetails).toBeFalsy();
|
expect(ruleDetails).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only show progress bar while loading', () => {
|
it('should only show progress bar while loading', async () => {
|
||||||
folderRulesService.folderInfo$ = of(null);
|
folderRulesService.folderInfo$ = of(null);
|
||||||
folderRulesService.deletedRuleId$ = of(null);
|
folderRulesService.deletedRuleId$ = of(null);
|
||||||
folderRulesService.rulesListing$ = of([]);
|
folderRulesService.rulesListing$ = of([]);
|
||||||
folderRulesService.loading$ = of(true);
|
folderRulesService.loading$ = of(true);
|
||||||
|
actionsService.loading$ = of(true);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@ -145,6 +152,7 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
folderRulesService.folderInfo$ = of(dummyNodeInfo);
|
||||||
folderRulesService.rulesListing$ = of(dummyRules);
|
folderRulesService.rulesListing$ = of(dummyRules);
|
||||||
folderRulesService.loading$ = of(false);
|
folderRulesService.loading$ = of(false);
|
||||||
|
actionsService.loading$ = of(false);
|
||||||
|
|
||||||
spyOn(component, 'onRuleDelete').and.callThrough();
|
spyOn(component, 'onRuleDelete').and.callThrough();
|
||||||
|
|
||||||
|
@ -30,11 +30,13 @@ import { Observable, Subscription } from 'rxjs';
|
|||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||||
import { tap } from 'rxjs/operators';
|
import { delay, tap } from 'rxjs/operators';
|
||||||
import { EditRuleDialogSmartComponent } from '../rule-details/edit-rule-dialog.smart-component';
|
import { EditRuleDialogSmartComponent } from '../rule-details/edit-rule-dialog.smart-component';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ConfirmDialogComponent } from '@alfresco/adf-content-services';
|
import { ConfirmDialogComponent } from '@alfresco/adf-content-services';
|
||||||
import { NotificationService } from '@alfresco/adf-core';
|
import { NotificationService } from '@alfresco/adf-core';
|
||||||
|
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||||
|
import { ActionsService } from '../services/actions.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-manage-rules',
|
selector: 'aca-manage-rules',
|
||||||
@ -45,8 +47,10 @@ import { NotificationService } from '@alfresco/adf-core';
|
|||||||
})
|
})
|
||||||
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
||||||
rules$: Observable<Rule[]>;
|
rules$: Observable<Rule[]>;
|
||||||
isLoading$: Observable<boolean>;
|
rulesLoading$: Observable<boolean>;
|
||||||
|
actionsLoading$: Observable<boolean>;
|
||||||
folderInfo$: Observable<NodeInfo>;
|
folderInfo$: Observable<NodeInfo>;
|
||||||
|
actionDefinitions$: Observable<ActionDefinitionTransformed[]>;
|
||||||
selectedRule: Rule = null;
|
selectedRule: Rule = null;
|
||||||
nodeId: string = null;
|
nodeId: string = null;
|
||||||
deletedRuleSubscription$: Subscription;
|
deletedRuleSubscription$: Subscription;
|
||||||
@ -57,10 +61,12 @@ export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
|||||||
private folderRulesService: FolderRulesService,
|
private folderRulesService: FolderRulesService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private matDialogService: MatDialog,
|
private matDialogService: MatDialog,
|
||||||
private notificationService: NotificationService
|
private notificationService: NotificationService,
|
||||||
|
private actionsService: ActionsService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.actionDefinitions$ = this.actionsService.actionDefinitionsListing$;
|
||||||
this.rules$ = this.folderRulesService.rulesListing$.pipe(
|
this.rules$ = this.folderRulesService.rulesListing$.pipe(
|
||||||
tap((rules) => {
|
tap((rules) => {
|
||||||
if (!rules.includes(this.selectedRule)) {
|
if (!rules.includes(this.selectedRule)) {
|
||||||
@ -73,8 +79,10 @@ export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
|||||||
this.folderRulesService.loadRules(this.nodeId);
|
this.folderRulesService.loadRules(this.nodeId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.isLoading$ = this.folderRulesService.loading$;
|
this.rulesLoading$ = this.folderRulesService.loading$;
|
||||||
|
this.actionsLoading$ = this.actionsService.loading$.pipe(delay(0));
|
||||||
this.folderInfo$ = this.folderRulesService.folderInfo$;
|
this.folderInfo$ = this.folderRulesService.folderInfo$;
|
||||||
|
this.actionsService.loadActionDefinitions();
|
||||||
this.route.params.subscribe((params) => {
|
this.route.params.subscribe((params) => {
|
||||||
this.nodeId = params.nodeId;
|
this.nodeId = params.nodeId;
|
||||||
if (this.nodeId) {
|
if (this.nodeId) {
|
||||||
|
108
projects/aca-folder-rules/src/lib/mock/actions.mock.ts
Normal file
108
projects/aca-folder-rules/src/lib/mock/actions.mock.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ActionDefinitionList } from '@alfresco/js-api';
|
||||||
|
import { ActionDefinitionTransformed, ActionParameterDefinitionTransformed } from '../model/rule-action.model';
|
||||||
|
|
||||||
|
export const actionDefListMock: ActionDefinitionList = {
|
||||||
|
list: {
|
||||||
|
pagination: {
|
||||||
|
count: 2,
|
||||||
|
hasMoreItems: false,
|
||||||
|
totalItems: 2,
|
||||||
|
skipCount: 0,
|
||||||
|
maxItems: 100
|
||||||
|
},
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
applicableTypes: [],
|
||||||
|
parameterDefinitions: [
|
||||||
|
{
|
||||||
|
name: 'mock-action-parameter-text',
|
||||||
|
type: 'd:text',
|
||||||
|
multiValued: false,
|
||||||
|
mandatory: false,
|
||||||
|
displayLabel: 'Mock action parameter text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mock-action-parameter-boolean',
|
||||||
|
type: 'd:boolean',
|
||||||
|
multiValued: false,
|
||||||
|
mandatory: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name: 'mock-action-1-definition',
|
||||||
|
trackStatus: false,
|
||||||
|
description: 'This is a mock action',
|
||||||
|
id: 'mock-action-1-definition',
|
||||||
|
title: 'Action 1 title'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
applicableTypes: [],
|
||||||
|
name: 'mock-action-2-definition',
|
||||||
|
trackStatus: false,
|
||||||
|
id: 'mock-action-2-definition'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionParam1TransformedMock: ActionParameterDefinitionTransformed = {
|
||||||
|
name: 'mock-action-parameter-text',
|
||||||
|
type: 'd:text',
|
||||||
|
multiValued: false,
|
||||||
|
mandatory: false,
|
||||||
|
displayLabel: 'Mock action parameter text'
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionParam2TransformedMock: ActionParameterDefinitionTransformed = {
|
||||||
|
name: 'mock-action-parameter-boolean',
|
||||||
|
type: 'd:boolean',
|
||||||
|
multiValued: false,
|
||||||
|
mandatory: false,
|
||||||
|
displayLabel: 'mock-action-parameter-boolean'
|
||||||
|
};
|
||||||
|
|
||||||
|
const action1TransformedMock: ActionDefinitionTransformed = {
|
||||||
|
id: 'mock-action-1-definition',
|
||||||
|
name: 'mock-action-1-definition',
|
||||||
|
description: 'This is a mock action',
|
||||||
|
title: 'Action 1 title',
|
||||||
|
applicableTypes: [],
|
||||||
|
trackStatus: false,
|
||||||
|
parameterDefinitions: [actionParam1TransformedMock, actionParam2TransformedMock]
|
||||||
|
};
|
||||||
|
|
||||||
|
const action2TransformedMock: ActionDefinitionTransformed = {
|
||||||
|
id: 'mock-action-2-definition',
|
||||||
|
name: 'mock-action-2-definition',
|
||||||
|
description: '',
|
||||||
|
title: 'mock-action-2-definition',
|
||||||
|
applicableTypes: [],
|
||||||
|
trackStatus: false,
|
||||||
|
parameterDefinitions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actionsTransformedListMock: ActionDefinitionTransformed[] = [action1TransformedMock, action2TransformedMock];
|
@ -25,11 +25,23 @@
|
|||||||
|
|
||||||
export interface RuleAction {
|
export interface RuleAction {
|
||||||
actionDefinitionId: string;
|
actionDefinitionId: string;
|
||||||
params: RuleActionParams;
|
params: { [key: string]: unknown };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuleActionParams {
|
export interface ActionDefinitionTransformed {
|
||||||
'deep-copy'?: boolean;
|
id: string;
|
||||||
'destination-folder'?: string;
|
name: string;
|
||||||
actionContext?: string;
|
description: string;
|
||||||
|
title: string;
|
||||||
|
applicableTypes: string[];
|
||||||
|
trackStatus: boolean;
|
||||||
|
parameterDefinitions: ActionParameterDefinitionTransformed[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionParameterDefinitionTransformed {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
multiValued: boolean;
|
||||||
|
mandatory: boolean;
|
||||||
|
displayLabel: string;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<div class="aca-rule-action-list__item " *ngFor="let control of formControls">
|
||||||
|
<aca-rule-action
|
||||||
|
[actionDefinitions]="actionDefinitions"
|
||||||
|
[readOnly]="readOnly"
|
||||||
|
[formControl]="control">
|
||||||
|
</aca-rule-action>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
data-automation-id="rule-action-list-action-menu"
|
||||||
|
*ngIf="!readOnly" [matMenuTriggerFor]="menu">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-menu #menu>
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
data-automation-id="rule-action-list-remove-action-button"
|
||||||
|
[title]="'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_BUTTONS.REMOVE' | translate"
|
||||||
|
[disabled]="formArray.controls.length <= 1"
|
||||||
|
(click)="removeAction(control)">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_BUTTONS.REMOVE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button mat-button data-automation-id="rule-action-list-add-action-button" (click)="addAction()" *ngIf="!readOnly">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_BUTTONS.ADD_ACTION' | translate }}</span>
|
||||||
|
</button>
|
@ -0,0 +1,15 @@
|
|||||||
|
.aca-rule-action-list {
|
||||||
|
&__item {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& > .aca-rule-action {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2n) {
|
||||||
|
background-color: hsl(0,0%,95%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { RuleActionListUiComponent } from './rule-action-list.ui-component';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { RuleActionUiComponent } from './rule-action.ui-component';
|
||||||
|
|
||||||
|
describe('RuleActionListUiComponent', () => {
|
||||||
|
let fixture: ComponentFixture<RuleActionListUiComponent>;
|
||||||
|
let component: RuleActionListUiComponent;
|
||||||
|
|
||||||
|
const getByDataAutomationId = (dataAutomationId: string, index = 0): DebugElement =>
|
||||||
|
fixture.debugElement.queryAll(By.css(`[data-automation-id="${dataAutomationId}"]`))[index];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CoreTestingModule]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RuleActionListUiComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
component.writeValue([]);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default to 1 empty action when an empty array of actions is written', () => {
|
||||||
|
const acaRuleActions = fixture.debugElement.queryAll(By.directive(RuleActionUiComponent));
|
||||||
|
expect(acaRuleActions.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a new action when the "add action" button is clicked', () => {
|
||||||
|
const addActionButton = getByDataAutomationId('rule-action-list-add-action-button').nativeElement as HTMLButtonElement;
|
||||||
|
addActionButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const acaRuleActions = fixture.debugElement.queryAll(By.directive(RuleActionUiComponent));
|
||||||
|
expect(acaRuleActions.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable the remove button if there is only one action', () => {
|
||||||
|
const menuButton = getByDataAutomationId('rule-action-list-action-menu', 0).nativeElement as HTMLButtonElement;
|
||||||
|
menuButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const removeActionButton = getByDataAutomationId('rule-action-list-remove-action-button').nativeElement as HTMLButtonElement;
|
||||||
|
expect(removeActionButton.disabled).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable the remove button if there is more than one action', () => {
|
||||||
|
const addActionButton = getByDataAutomationId('rule-action-list-add-action-button').nativeElement as HTMLButtonElement;
|
||||||
|
addActionButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const menuButton = getByDataAutomationId('rule-action-list-action-menu', 0).nativeElement as HTMLButtonElement;
|
||||||
|
menuButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const removeActionButton = getByDataAutomationId('rule-action-list-remove-action-button').nativeElement as HTMLButtonElement;
|
||||||
|
expect(removeActionButton.disabled).toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove an action when the remove action button is clicked', () => {
|
||||||
|
const addActionButton = getByDataAutomationId('rule-action-list-add-action-button').nativeElement as HTMLButtonElement;
|
||||||
|
addActionButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const menuButton = getByDataAutomationId('rule-action-list-action-menu', 0).nativeElement as HTMLButtonElement;
|
||||||
|
menuButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const removeActionButton = getByDataAutomationId('rule-action-list-remove-action-button').nativeElement as HTMLButtonElement;
|
||||||
|
removeActionButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const acaRuleActions = fixture.debugElement.queryAll(By.directive(RuleActionUiComponent));
|
||||||
|
expect(acaRuleActions.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,77 @@
|
|||||||
|
import { Component, forwardRef, Input, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, FormArray, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { ActionDefinitionTransformed, RuleAction } from '../../model/rule-action.model';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aca-rule-action-list',
|
||||||
|
templateUrl: './rule-action-list.ui-component.html',
|
||||||
|
styleUrls: ['./rule-action-list.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-action-list' },
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: forwardRef(() => RuleActionListUiComponent)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RuleActionListUiComponent implements ControlValueAccessor, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
actionDefinitions: ActionDefinitionTransformed[] = [];
|
||||||
|
@Input()
|
||||||
|
readOnly = false;
|
||||||
|
|
||||||
|
formArray = new FormArray([]);
|
||||||
|
private formArraySubscription: Subscription;
|
||||||
|
|
||||||
|
get formControls(): FormControl[] {
|
||||||
|
return this.formArray.controls as FormControl[];
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange: (actions: RuleAction[]) => void = () => undefined;
|
||||||
|
onTouch: () => void = () => undefined;
|
||||||
|
|
||||||
|
writeValue(actions: RuleAction[]) {
|
||||||
|
if (actions.length === 0) {
|
||||||
|
actions = [
|
||||||
|
{
|
||||||
|
actionDefinitionId: null,
|
||||||
|
params: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
this.formArray = new FormArray(actions.map((action: RuleAction) => new FormControl(action)));
|
||||||
|
this.formArraySubscription?.unsubscribe();
|
||||||
|
this.formArraySubscription = this.formArray.valueChanges.subscribe((value: any) => {
|
||||||
|
this.onChange(value);
|
||||||
|
this.onTouch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: (actions: RuleAction[]) => void) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => void) {
|
||||||
|
this.onTouch = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAction() {
|
||||||
|
const newAction: RuleAction = {
|
||||||
|
actionDefinitionId: null,
|
||||||
|
params: {}
|
||||||
|
};
|
||||||
|
this.formArray.push(new FormControl(newAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.formArraySubscription?.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAction(control: FormControl) {
|
||||||
|
const index = this.formArray.value.indexOf(control.value);
|
||||||
|
this.formArray.removeAt(index);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
<form class="aca-rule-action__form" [formGroup]="form">
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select
|
||||||
|
formControlName="actionDefinitionId"
|
||||||
|
data-automation-id="rule-action-select"
|
||||||
|
[placeholder]="'ACA_FOLDER_RULES.RULE_DETAILS.ACTION_SELECT_PLACEHOLDER' | translate">
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let actionDefinition of actionDefinitions"
|
||||||
|
[value]="actionDefinition.id">
|
||||||
|
{{ actionDefinition.title }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<adf-card-view
|
||||||
|
data-automation-id="rule-action-card-view"
|
||||||
|
[properties]="cardViewItems"
|
||||||
|
[editable]="!readOnly">
|
||||||
|
</adf-card-view>
|
||||||
|
|
||||||
|
</form>
|
@ -0,0 +1,7 @@
|
|||||||
|
.aca-rule-action {
|
||||||
|
&__form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { CardViewBoolItemModel, CardViewComponent, CardViewTextItemModel, CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { RuleActionUiComponent } from './rule-action.ui-component';
|
||||||
|
import { actionsTransformedListMock } from '../../mock/actions.mock';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
describe('RuleActionUiComponent', () => {
|
||||||
|
let fixture: ComponentFixture<RuleActionUiComponent>;
|
||||||
|
let component: RuleActionUiComponent;
|
||||||
|
|
||||||
|
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
||||||
|
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
||||||
|
|
||||||
|
const changeMatSelectValue = (dataAutomationId: string, value: string) => {
|
||||||
|
const matSelect = getByDataAutomationId(dataAutomationId).nativeElement;
|
||||||
|
matSelect.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const matOption = fixture.debugElement.query(By.css(`.mat-option[ng-reflect-value="${value}"]`)).nativeElement;
|
||||||
|
matOption.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CoreTestingModule]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RuleActionUiComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should populate the dropdown selector with the action definitions', () => {
|
||||||
|
component.actionDefinitions = actionsTransformedListMock;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const matSelect = getByDataAutomationId('rule-action-select').nativeElement;
|
||||||
|
matSelect.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const matOptions = fixture.debugElement.queryAll(By.css(`mat-option`));
|
||||||
|
expect(matOptions.length).toBe(2);
|
||||||
|
expect(matOptions[0].nativeElement.innerText).toBe('Action 1 title');
|
||||||
|
expect(matOptions[1].nativeElement.innerText).toBe('mock-action-2-definition');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should populate the card view with parameters when an action is selected', () => {
|
||||||
|
component.actionDefinitions = actionsTransformedListMock;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const cardView = getByDataAutomationId('rule-action-card-view').componentInstance as CardViewComponent;
|
||||||
|
expect(cardView.properties.length).toBe(0);
|
||||||
|
|
||||||
|
changeMatSelectValue('rule-action-select', 'mock-action-1-definition');
|
||||||
|
expect(cardView.properties.length).toBe(2);
|
||||||
|
expect(cardView.properties[0]).toBeInstanceOf(CardViewTextItemModel);
|
||||||
|
expect(cardView.properties[1]).toBeInstanceOf(CardViewBoolItemModel);
|
||||||
|
|
||||||
|
changeMatSelectValue('rule-action-select', 'mock-action-2-definition');
|
||||||
|
expect(cardView.properties.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,153 @@
|
|||||||
|
import { Component, forwardRef, Input, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { ActionDefinitionTransformed, RuleAction } from '../../model/rule-action.model';
|
||||||
|
import { CardViewItem } from '@alfresco/adf-core/lib/card-view/interfaces/card-view-item.interface';
|
||||||
|
import { CardViewBoolItemModel, CardViewTextItemModel, CardViewUpdateService, UpdateNotification } from '@alfresco/adf-core';
|
||||||
|
import { ActionParameterDefinition } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aca-rule-action',
|
||||||
|
templateUrl: './rule-action.ui-component.html',
|
||||||
|
styleUrls: ['./rule-action.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-action' },
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: forwardRef(() => RuleActionUiComponent)
|
||||||
|
},
|
||||||
|
CardViewUpdateService
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RuleActionUiComponent implements ControlValueAccessor, OnDestroy {
|
||||||
|
private _actionDefinitions: ActionDefinitionTransformed[];
|
||||||
|
@Input()
|
||||||
|
get actionDefinitions(): ActionDefinitionTransformed[] {
|
||||||
|
return this._actionDefinitions;
|
||||||
|
}
|
||||||
|
set actionDefinitions(value: ActionDefinitionTransformed[]) {
|
||||||
|
this._actionDefinitions = value.sort((a, b) => a.title.localeCompare(b.title));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readOnly = false;
|
||||||
|
@Input()
|
||||||
|
get readOnly(): boolean {
|
||||||
|
return this._readOnly;
|
||||||
|
}
|
||||||
|
set readOnly(isReadOnly: boolean) {
|
||||||
|
this.setDisabledState(isReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
form = new FormGroup({
|
||||||
|
actionDefinitionId: new FormControl('copy')
|
||||||
|
});
|
||||||
|
|
||||||
|
cardViewItems: CardViewItem[] = [];
|
||||||
|
|
||||||
|
parameters: { [key: string]: unknown } = {};
|
||||||
|
|
||||||
|
get selectedActionDefinitionId(): string {
|
||||||
|
return this.form.get('actionDefinitionId').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedActionDefinition(): ActionDefinitionTransformed {
|
||||||
|
return this.actionDefinitions.find((actionDefinition: ActionDefinitionTransformed) => actionDefinition.id === this.selectedActionDefinitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private formSubscription = this.form.valueChanges.subscribe(() => {
|
||||||
|
this.setDefaultParameters();
|
||||||
|
this.setCardViewProperties();
|
||||||
|
this.onChange({
|
||||||
|
actionDefinitionId: this.selectedActionDefinitionId,
|
||||||
|
params: this.parameters
|
||||||
|
});
|
||||||
|
this.onTouch();
|
||||||
|
});
|
||||||
|
|
||||||
|
private updateServiceSubscription = this.cardViewUpdateService.itemUpdated$.subscribe((updateNotification: UpdateNotification) => {
|
||||||
|
this.parameters = {
|
||||||
|
...this.parameters,
|
||||||
|
...updateNotification.changed
|
||||||
|
};
|
||||||
|
this.onChange({
|
||||||
|
actionDefinitionId: this.selectedActionDefinitionId,
|
||||||
|
params: this.parameters
|
||||||
|
});
|
||||||
|
this.onTouch();
|
||||||
|
});
|
||||||
|
|
||||||
|
onChange: (action: RuleAction) => void = () => undefined;
|
||||||
|
onTouch: () => void = () => undefined;
|
||||||
|
|
||||||
|
constructor(private cardViewUpdateService: CardViewUpdateService) {}
|
||||||
|
|
||||||
|
writeValue(action: RuleAction) {
|
||||||
|
this.form.setValue({
|
||||||
|
actionDefinitionId: action.actionDefinitionId
|
||||||
|
});
|
||||||
|
this.parameters = {
|
||||||
|
...this.parameters,
|
||||||
|
...action.params
|
||||||
|
};
|
||||||
|
this.setCardViewProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: (action: RuleAction) => void) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any) {
|
||||||
|
this.onTouch = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.formSubscription.unsubscribe();
|
||||||
|
this.updateServiceSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
setCardViewProperties() {
|
||||||
|
this.cardViewItems = (this.selectedActionDefinition?.parameterDefinitions ?? []).map((paramDef) => {
|
||||||
|
const cardViewPropertiesModel = {
|
||||||
|
label: paramDef.displayLabel,
|
||||||
|
key: paramDef.name,
|
||||||
|
editable: true
|
||||||
|
};
|
||||||
|
switch (paramDef.type) {
|
||||||
|
case 'd:boolean':
|
||||||
|
return new CardViewBoolItemModel({
|
||||||
|
...cardViewPropertiesModel,
|
||||||
|
value: this.parameters[paramDef.name] ?? false
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return new CardViewTextItemModel({
|
||||||
|
...cardViewPropertiesModel,
|
||||||
|
value: this.parameters[paramDef.name] ?? ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDefaultParameters() {
|
||||||
|
this.parameters = {};
|
||||||
|
(this.selectedActionDefinition?.parameterDefinitions ?? []).forEach((paramDef: ActionParameterDefinition) => {
|
||||||
|
switch (paramDef.type) {
|
||||||
|
case 'd:boolean':
|
||||||
|
this.parameters[paramDef.name] = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.parameters[paramDef.name] = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this._readOnly = true;
|
||||||
|
this.form.disable();
|
||||||
|
} else {
|
||||||
|
this._readOnly = false;
|
||||||
|
this.form.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -48,10 +48,10 @@
|
|||||||
<mat-menu #menu="matMenu">
|
<mat-menu #menu="matMenu">
|
||||||
<button
|
<button
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
[title]="'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.REMOVE' | translate"
|
[title]="'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.REMOVE' | translate"
|
||||||
(click)="removeCondition(control)">
|
(click)="removeCondition(control)">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.REMOVE' | translate }}</span>
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.REMOVE' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
@ -60,11 +60,11 @@
|
|||||||
<div class="aca-rule-composite-condition__form__actions" *ngIf="!readOnly" data-automation-id="add-actions">
|
<div class="aca-rule-composite-condition__form__actions" *ngIf="!readOnly" data-automation-id="add-actions">
|
||||||
<button mat-button (click)="addSimpleCondition()" data-automation-id="add-condition-button">
|
<button mat-button (click)="addSimpleCondition()" data-automation-id="add-condition-button">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.ADD_CONDITION' | translate }}</span>
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.ADD_CONDITION' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-button (click)="addCompositeCondition()" data-automation-id="add-group-button">
|
<button mat-button (click)="addCompositeCondition()" data-automation-id="add-group-button">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.ADD_GROUP' | translate }}</span>
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.CONDITION_BUTTONS.ADD_GROUP' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -8,7 +8,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-dialog-content class="aca-edit-rule-dialog__content">
|
<mat-dialog-content class="aca-edit-rule-dialog__content">
|
||||||
<aca-rule-details (formValidationChanged)="formValid = $event" (formValueChanged)="formValue = $event" [value]="model"></aca-rule-details>
|
<div class="aca-edit-rule-dialog__content__spinner" *ngIf="loading$ | async; else ruleDetails">
|
||||||
|
<mat-progress-spinner
|
||||||
|
color="primary"
|
||||||
|
mode="indeterminate">
|
||||||
|
</mat-progress-spinner>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #ruleDetails>
|
||||||
|
<aca-rule-details
|
||||||
|
[actionDefinitions]="actionDefinitions$ | async"
|
||||||
|
[value]="model"
|
||||||
|
(formValueChanged)="formValue = $event"
|
||||||
|
(formValidationChanged)="formValid = $event">
|
||||||
|
</aca-rule-details>
|
||||||
|
</ng-template>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions align="end" class="aca-edit-rule-dialog__footer">
|
<mat-dialog-actions align="end" class="aca-edit-rule-dialog__footer">
|
||||||
|
@ -31,6 +31,13 @@
|
|||||||
&__content {
|
&__content {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
&__spinner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 20px 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
|
@ -31,9 +31,14 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|||||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
import { RuleCompositeConditionUiComponent } from './conditions/rule-composite-condition.ui-component';
|
import { RuleCompositeConditionUiComponent } from './conditions/rule-composite-condition.ui-component';
|
||||||
import { RuleTriggersUiComponent } from './triggers/rule-triggers.ui-component';
|
import { RuleTriggersUiComponent } from './triggers/rule-triggers.ui-component';
|
||||||
|
import { RuleActionListUiComponent } from './actions/rule-action-list.ui-component';
|
||||||
|
import { RuleActionUiComponent } from './actions/rule-action.ui-component';
|
||||||
|
import { ActionsService } from '../services/actions.service';
|
||||||
|
import { RuleOptionsUiComponent } from './options/rule-options.ui-component';
|
||||||
|
|
||||||
describe('EditRuleDialogComponent', () => {
|
describe('EditRuleDialogComponent', () => {
|
||||||
let fixture: ComponentFixture<EditRuleDialogSmartComponent>;
|
let fixture: ComponentFixture<EditRuleDialogSmartComponent>;
|
||||||
|
let actionsService: ActionsService;
|
||||||
|
|
||||||
const dialogRef = {
|
const dialogRef = {
|
||||||
close: jasmine.createSpy('close'),
|
close: jasmine.createSpy('close'),
|
||||||
@ -43,14 +48,26 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
const setupBeforeEach = (dialogOptions: EditRuleDialogOptions = {}) => {
|
const setupBeforeEach = (dialogOptions: EditRuleDialogOptions = {}) => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule],
|
imports: [CoreTestingModule],
|
||||||
declarations: [EditRuleDialogSmartComponent, RuleCompositeConditionUiComponent, RuleDetailsUiComponent, RuleTriggersUiComponent],
|
declarations: [
|
||||||
|
EditRuleDialogSmartComponent,
|
||||||
|
RuleCompositeConditionUiComponent,
|
||||||
|
RuleDetailsUiComponent,
|
||||||
|
RuleTriggersUiComponent,
|
||||||
|
RuleActionListUiComponent,
|
||||||
|
RuleActionUiComponent,
|
||||||
|
RuleOptionsUiComponent
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: MatDialogRef, useValue: dialogRef },
|
{ provide: MatDialogRef, useValue: dialogRef },
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: dialogOptions }
|
{ provide: MAT_DIALOG_DATA, useValue: dialogOptions }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
actionsService = TestBed.inject(ActionsService);
|
||||||
|
spyOn(actionsService, 'loadActionDefinitions').and.stub();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(EditRuleDialogSmartComponent);
|
fixture = TestBed.createComponent(EditRuleDialogSmartComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('No dialog options passed / indifferent', () => {
|
describe('No dialog options passed / indifferent', () => {
|
||||||
@ -59,7 +76,6 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should activate the submit button only when a valid state is received', () => {
|
it('should activate the submit button only when a valid state is received', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
const submitButton = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
const submitButton = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
||||||
const ruleDetails = fixture.debugElement.query(By.directive(RuleDetailsUiComponent)).componentInstance as RuleDetailsUiComponent;
|
const ruleDetails = fixture.debugElement.query(By.directive(RuleDetailsUiComponent)).componentInstance as RuleDetailsUiComponent;
|
||||||
ruleDetails.formValidationChanged.emit(true);
|
ruleDetails.formValidationChanged.emit(true);
|
||||||
@ -73,14 +89,12 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should show a "create" label in the title', () => {
|
it('should show a "create" label in the title', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-title"]')).nativeElement as HTMLDivElement;
|
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-title"]')).nativeElement as HTMLDivElement;
|
||||||
|
|
||||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CREATE_TITLE');
|
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CREATE_TITLE');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show a "create" label in the submit button', () => {
|
it('should show a "create" label in the submit button', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
||||||
|
|
||||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CREATE');
|
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.CREATE');
|
||||||
@ -90,9 +104,7 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
describe('With dialog options passed', () => {
|
describe('With dialog options passed', () => {
|
||||||
const dialogOptions: EditRuleDialogOptions = {
|
const dialogOptions: EditRuleDialogOptions = {
|
||||||
model: {
|
model: {
|
||||||
id: 'rule-id',
|
id: 'rule-id'
|
||||||
name: 'Rule name',
|
|
||||||
description: 'This is the description of the rule'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -101,14 +113,12 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should show an "update" label in the title', () => {
|
it('should show an "update" label in the title', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-title"]')).nativeElement as HTMLDivElement;
|
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-title"]')).nativeElement as HTMLDivElement;
|
||||||
|
|
||||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.UPDATE_TITLE');
|
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.UPDATE_TITLE');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show an "update" label in the submit button', () => {
|
it('should show an "update" label in the submit button', () => {
|
||||||
fixture.detectChanges();
|
|
||||||
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="edit-rule-dialog-submit"]')).nativeElement as HTMLButtonElement;
|
||||||
|
|
||||||
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.UPDATE');
|
expect(titleElement.innerText.trim()).toBe('ACA_FOLDER_RULES.EDIT_RULE_DIALOG.UPDATE');
|
||||||
|
@ -23,9 +23,10 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, EventEmitter, Inject, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, Inject, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
|
import { ActionsService } from '../services/actions.service';
|
||||||
|
|
||||||
export interface EditRuleDialogOptions {
|
export interface EditRuleDialogOptions {
|
||||||
model?: Partial<Rule>;
|
model?: Partial<Rule>;
|
||||||
@ -38,13 +39,15 @@ export interface EditRuleDialogOptions {
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'aca-edit-rule-dialog' }
|
host: { class: 'aca-edit-rule-dialog' }
|
||||||
})
|
})
|
||||||
export class EditRuleDialogSmartComponent {
|
export class EditRuleDialogSmartComponent implements OnInit {
|
||||||
formValid = false;
|
formValid = false;
|
||||||
model: Partial<Rule>;
|
model: Partial<Rule>;
|
||||||
formValue: Partial<Rule>;
|
formValue: Partial<Rule>;
|
||||||
@Output() submitted = new EventEmitter<Partial<Rule>>();
|
@Output() submitted = new EventEmitter<Partial<Rule>>();
|
||||||
|
actionDefinitions$ = this.actionsService.actionDefinitionsListing$;
|
||||||
|
loading$ = this.actionsService.loading$;
|
||||||
|
|
||||||
constructor(@Inject(MAT_DIALOG_DATA) public options: EditRuleDialogOptions) {
|
constructor(@Inject(MAT_DIALOG_DATA) public options: EditRuleDialogOptions, private actionsService: ActionsService) {
|
||||||
this.model = this.options?.model || {};
|
this.model = this.options?.model || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,4 +66,8 @@ export class EditRuleDialogSmartComponent {
|
|||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.submitted.emit(this.formValue);
|
this.submitted.emit(this.formValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.actionsService.loadActionDefinitions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,17 @@
|
|||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
<div class="aca-rule-details__form__row aca-rule-details__form__actions">
|
||||||
|
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.PERFORM_ACTIONS' | translate }}</div>
|
||||||
|
<aca-rule-action-list
|
||||||
|
formControlName="actions"
|
||||||
|
[actionDefinitions]="actionDefinitions"
|
||||||
|
[readOnly]="readOnly">
|
||||||
|
</aca-rule-action-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
<div class="aca-rule-details__form__row">
|
<div class="aca-rule-details__form__row">
|
||||||
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.OPTIONS' | translate }}</div>
|
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.OPTIONS' | translate }}</div>
|
||||||
<aca-rule-options [form]="form" [preview]="preview" data-automation-id="rule-details-options-component"></aca-rule-options>
|
<aca-rule-options [form]="form" [preview]="preview" data-automation-id="rule-details-options-component"></aca-rule-options>
|
||||||
|
@ -58,5 +58,11 @@
|
|||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
.aca-rule-action-list {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ import { RuleCompositeConditionUiComponent } from './conditions/rule-composite-c
|
|||||||
import { RuleTriggersUiComponent } from './triggers/rule-triggers.ui-component';
|
import { RuleTriggersUiComponent } from './triggers/rule-triggers.ui-component';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { RuleOptionsUiComponent } from './options/rule-options.ui-component';
|
import { RuleOptionsUiComponent } from './options/rule-options.ui-component';
|
||||||
|
import { RuleActionListUiComponent } from './actions/rule-action-list.ui-component';
|
||||||
|
import { RuleActionUiComponent } from './actions/rule-action.ui-component';
|
||||||
|
|
||||||
describe('RuleDetailsUiComponent', () => {
|
describe('RuleDetailsUiComponent', () => {
|
||||||
let fixture: ComponentFixture<RuleDetailsUiComponent>;
|
let fixture: ComponentFixture<RuleDetailsUiComponent>;
|
||||||
@ -56,7 +58,14 @@ describe('RuleDetailsUiComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule],
|
imports: [CoreTestingModule],
|
||||||
declarations: [RuleCompositeConditionUiComponent, RuleDetailsUiComponent, RuleTriggersUiComponent, RuleOptionsUiComponent]
|
declarations: [
|
||||||
|
RuleCompositeConditionUiComponent,
|
||||||
|
RuleDetailsUiComponent,
|
||||||
|
RuleTriggersUiComponent,
|
||||||
|
RuleOptionsUiComponent,
|
||||||
|
RuleActionListUiComponent,
|
||||||
|
RuleActionUiComponent
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RuleDetailsUiComponent);
|
fixture = TestBed.createComponent(RuleDetailsUiComponent);
|
||||||
|
@ -30,6 +30,7 @@ import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
|
|||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
import { ruleCompositeConditionValidator } from './validators/rule-composite-condition.validator';
|
import { ruleCompositeConditionValidator } from './validators/rule-composite-condition.validator';
|
||||||
import { FolderRulesService } from '../services/folder-rules.service';
|
import { FolderRulesService } from '../services/folder-rules.service';
|
||||||
|
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rule-details',
|
selector: 'aca-rule-details',
|
||||||
@ -68,7 +69,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
isAsynchronous: newValue.isAsynchronous || FolderRulesService.emptyRule.isAsynchronous,
|
isAsynchronous: newValue.isAsynchronous || FolderRulesService.emptyRule.isAsynchronous,
|
||||||
errorScript: newValue.errorScript || FolderRulesService.emptyRule.errorScript,
|
errorScript: newValue.errorScript || FolderRulesService.emptyRule.errorScript,
|
||||||
isInheritable: newValue.isInheritable || FolderRulesService.emptyRule.isInheritable,
|
isInheritable: newValue.isInheritable || FolderRulesService.emptyRule.isInheritable,
|
||||||
isEnabled: newValue.isEnabled || FolderRulesService.emptyRule.isEnabled
|
isEnabled: newValue.isEnabled || FolderRulesService.emptyRule.isEnabled,
|
||||||
|
actions: newValue.actions || FolderRulesService.emptyRule.actions
|
||||||
};
|
};
|
||||||
if (this.form) {
|
if (this.form) {
|
||||||
this.form.setValue(newValue);
|
this.form.setValue(newValue);
|
||||||
@ -78,6 +80,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
@Input()
|
@Input()
|
||||||
preview: boolean;
|
preview: boolean;
|
||||||
|
@Input()
|
||||||
|
actionDefinitions: ActionDefinitionTransformed[] = [];
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
formValidationChanged = new EventEmitter<boolean>();
|
formValidationChanged = new EventEmitter<boolean>();
|
||||||
@ -129,7 +133,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
isAsynchronous: new UntypedFormControl(this.value.isAsynchronous),
|
isAsynchronous: new UntypedFormControl(this.value.isAsynchronous),
|
||||||
errorScript: new UntypedFormControl(this.value.errorScript),
|
errorScript: new UntypedFormControl(this.value.errorScript),
|
||||||
isInheritable: new UntypedFormControl(this.value.isInheritable),
|
isInheritable: new UntypedFormControl(this.value.isInheritable),
|
||||||
isEnabled: new UntypedFormControl(this.value.isEnabled)
|
isEnabled: new UntypedFormControl(this.value.isEnabled),
|
||||||
|
actions: new UntypedFormControl(this.value.actions)
|
||||||
});
|
});
|
||||||
this.readOnly = this._readOnly;
|
this.readOnly = this._readOnly;
|
||||||
|
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ActionsService } from './actions.service';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { ActionsApi } from '@alfresco/js-api';
|
||||||
|
import { actionDefListMock, actionsTransformedListMock } from '../mock/actions.mock';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
|
describe('ActionsService', () => {
|
||||||
|
let actionsService: ActionsService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CoreTestingModule],
|
||||||
|
providers: [ActionsService]
|
||||||
|
});
|
||||||
|
|
||||||
|
actionsService = TestBed.inject(ActionsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load the data into the observable', async () => {
|
||||||
|
spyOn(ActionsApi.prototype, 'listActions').and.returnValue(Promise.resolve(actionDefListMock));
|
||||||
|
const actionsPromise = actionsService.actionDefinitionsListing$.pipe(take(2)).toPromise();
|
||||||
|
|
||||||
|
actionsService.loadActionDefinitions();
|
||||||
|
|
||||||
|
const actionsList = await actionsPromise;
|
||||||
|
expect(actionsList).toEqual(actionsTransformedListMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set loading to true while the request is being sent', async () => {
|
||||||
|
spyOn(ActionsApi.prototype, 'listActions').and.returnValue(Promise.resolve(actionDefListMock));
|
||||||
|
const loadingTruePromise = actionsService.loading$.pipe(take(2)).toPromise();
|
||||||
|
const loadingFalsePromise = actionsService.loading$.pipe(take(3)).toPromise();
|
||||||
|
|
||||||
|
actionsService.loadActionDefinitions();
|
||||||
|
|
||||||
|
expect(await loadingTruePromise).toBeTrue();
|
||||||
|
expect(await loadingFalsePromise).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,92 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActionDefinition, ActionDefinitionEntry, ActionDefinitionList, ActionParameterDefinition, ActionsApi } from '@alfresco/js-api';
|
||||||
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
|
import { BehaviorSubject, from } from 'rxjs';
|
||||||
|
import { ActionDefinitionTransformed, ActionParameterDefinitionTransformed } from '../model/rule-action.model';
|
||||||
|
import { finalize, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ActionsService {
|
||||||
|
private actionDefinitionsListingSource = new BehaviorSubject<ActionDefinitionTransformed[]>([]);
|
||||||
|
actionDefinitionsListing$ = this.actionDefinitionsListingSource.asObservable();
|
||||||
|
private loadingSource = new BehaviorSubject<boolean>(false);
|
||||||
|
loading$ = this.loadingSource.asObservable();
|
||||||
|
|
||||||
|
private _actionsApi: ActionsApi;
|
||||||
|
get actionsApi(): ActionsApi {
|
||||||
|
if (!this._actionsApi) {
|
||||||
|
this._actionsApi = new ActionsApi(this.apiService.getInstance());
|
||||||
|
}
|
||||||
|
return this._actionsApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private apiService: AlfrescoApiService) {}
|
||||||
|
|
||||||
|
loadActionDefinitions() {
|
||||||
|
this.loadingSource.next(true);
|
||||||
|
from(this.actionsApi.listActions())
|
||||||
|
.pipe(
|
||||||
|
map((list: ActionDefinitionList) => list.list.entries.map((entry) => this.transformActionDefinition(entry))),
|
||||||
|
finalize(() => this.loadingSource.next(false))
|
||||||
|
)
|
||||||
|
.subscribe((obj: ActionDefinitionTransformed[]) => {
|
||||||
|
this.actionDefinitionsListingSource.next(obj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private transformActionDefinition(obj: ActionDefinition | ActionDefinitionEntry): ActionDefinitionTransformed {
|
||||||
|
if (this.isActionDefinitionEntry(obj)) {
|
||||||
|
obj = obj.entry;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: obj.id,
|
||||||
|
name: obj.name ?? '',
|
||||||
|
description: obj.description ?? '',
|
||||||
|
title: obj.title ?? obj.name ?? '',
|
||||||
|
applicableTypes: obj.applicableTypes ?? [],
|
||||||
|
trackStatus: obj.trackStatus ?? false,
|
||||||
|
parameterDefinitions: (obj.parameterDefinitions ?? []).map((paramDef: ActionParameterDefinition) =>
|
||||||
|
this.transformActionParameterDefinition(paramDef)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private transformActionParameterDefinition(obj: ActionParameterDefinition): ActionParameterDefinitionTransformed {
|
||||||
|
return {
|
||||||
|
name: obj.name ?? '',
|
||||||
|
type: obj.type ?? '',
|
||||||
|
multiValued: obj.multiValued ?? false,
|
||||||
|
mandatory: obj.mandatory ?? false,
|
||||||
|
displayLabel: obj.displayLabel ?? obj.name ?? ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private isActionDefinitionEntry(obj): obj is ActionDefinitionEntry {
|
||||||
|
return typeof obj.entry !== 'undefined';
|
||||||
|
}
|
||||||
|
}
|
@ -146,11 +146,9 @@ describe('FolderRulesService', () => {
|
|||||||
.and.returnValue(Promise.resolve(dummyRules[0]));
|
.and.returnValue(Promise.resolve(dummyRules[0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send correct POST request and return created rule', function () {
|
it('should send correct POST request and return created rule', async () => {
|
||||||
folderRulesService.createRule(nodeId, dummyRules[0]).then((result) => {
|
const result = await folderRulesService.createRule(nodeId, dummyRules[0]);
|
||||||
expect(folderRulesService.createRule).toHaveBeenCalledWith(nodeId, dummyRules[0]);
|
expect(result).toEqual(dummyRules[0]);
|
||||||
expect(result).toEqual(dummyRules[0]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user