mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-26 17:24:45 +00:00
[ACS-3257] Create / Update rules dialog condition section (#2585)
* First commit: simple and composite condition UI * Styling, readonly mode, etc... * Unit tests for RuleSimpleConditionUiComponent * Validation for composite condition * Add unit tests for composite conditions * Revert manage rules screen * Reset karma conf singleRun to true * Couple of small changes * Typo
This commit is contained in:
parent
b082aaa011
commit
9a650f5265
@ -21,8 +21,47 @@
|
|||||||
"NO_DESCRIPTION": "No description"
|
"NO_DESCRIPTION": "No description"
|
||||||
},
|
},
|
||||||
"ERROR": {
|
"ERROR": {
|
||||||
"REQUIRED": "This field is required"
|
"REQUIRED": "This field is required",
|
||||||
}
|
"RULE_COMPOSITE_CONDITION_INVALID": "One or more condition groups is empty"
|
||||||
|
},
|
||||||
|
"COMPARATORS": {
|
||||||
|
"EQUALS": "(=) Equals",
|
||||||
|
"CONTAINS": "Contains",
|
||||||
|
"STARTS_WITH": "Starts with",
|
||||||
|
"ENDS_WITH": "Ends with",
|
||||||
|
"GREATER_THAN": "(>) Greater than",
|
||||||
|
"LESS_THAN": "(<) Less than",
|
||||||
|
"GREATER_THAN_OR_EQUAL": "(>=) Greater than or equal",
|
||||||
|
"LESS_THAN_OR_EQUAL": "(<=) Less than or equal",
|
||||||
|
"ON": "(=) On",
|
||||||
|
"AFTER": "(>) After",
|
||||||
|
"BEFORE": "(<) Before",
|
||||||
|
"ON_OR_AFTER": "(>=) On or after",
|
||||||
|
"ON_OR_BEFORE": "(<=) On or before",
|
||||||
|
"INSTANCE_OF": "(=) Is"
|
||||||
|
},
|
||||||
|
"FIELDS": {
|
||||||
|
"NAME": "Name",
|
||||||
|
"SIZE": "Size",
|
||||||
|
"MIMETYPE": "Mimetype",
|
||||||
|
"ENCODING": "Encoding",
|
||||||
|
"HAS_CATEGORY": "Has category",
|
||||||
|
"HAS_TAG": "Has tag",
|
||||||
|
"HAS_ASPECT": "Has aspect"
|
||||||
|
},
|
||||||
|
"LOGIC_OPERATORS": {
|
||||||
|
"IF": "If",
|
||||||
|
"NOT_IF": "NOT If",
|
||||||
|
"AND": "And",
|
||||||
|
"OR": "Or"
|
||||||
|
},
|
||||||
|
"ACTIONS": {
|
||||||
|
"ADD_CONDITION": "Add condition",
|
||||||
|
"ADD_GROUP": "Add group",
|
||||||
|
"REMOVE": "Remove"
|
||||||
|
},
|
||||||
|
"NO_CONDITIONS": "No conditions",
|
||||||
|
"NO_CONDITIONS_IN_GROUP": "No conditions in the group"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,9 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
|
|
||||||
import { EditRuleDialogSmartComponent } from './rule-details/edit-rule-dialog.smart-component';
|
import { EditRuleDialogSmartComponent } from './rule-details/edit-rule-dialog.smart-component';
|
||||||
import { ManageRulesSmartComponent } from './manage-rules/manage-rules.smart-component';
|
import { ManageRulesSmartComponent } from './manage-rules/manage-rules.smart-component';
|
||||||
|
import { RuleCompositeConditionUiComponent } from './rule-details/conditions/rule-composite-condition.ui-component';
|
||||||
import { RuleDetailsUiComponent } from './rule-details/rule-details.ui-component';
|
import { RuleDetailsUiComponent } from './rule-details/rule-details.ui-component';
|
||||||
|
import { RuleSimpleConditionUiComponent } from './rule-details/conditions/rule-simple-condition.ui-component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -44,7 +46,13 @@ const routes: Routes = [
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
providers: [provideExtensionConfig(['folder-rules.plugin.json'])],
|
providers: [provideExtensionConfig(['folder-rules.plugin.json'])],
|
||||||
imports: [CommonModule, RouterModule.forChild(routes), CoreModule.forChild()],
|
imports: [CommonModule, RouterModule.forChild(routes), CoreModule.forChild()],
|
||||||
declarations: [EditRuleDialogSmartComponent, ManageRulesSmartComponent, RuleDetailsUiComponent]
|
declarations: [
|
||||||
|
EditRuleDialogSmartComponent,
|
||||||
|
ManageRulesSmartComponent,
|
||||||
|
RuleCompositeConditionUiComponent,
|
||||||
|
RuleDetailsUiComponent,
|
||||||
|
RuleSimpleConditionUiComponent
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class AcaFolderRulesModule {
|
export class AcaFolderRulesModule {
|
||||||
constructor(translation: TranslationService, extensions: ExtensionService) {
|
constructor(translation: TranslationService, extensions: ExtensionService) {
|
||||||
|
68
projects/aca-folder-rules/src/lib/mock/conditions.mock.ts
Normal file
68
projects/aca-folder-rules/src/lib/mock/conditions.mock.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { RuleCompositeCondition } from '../model/rule-composite-condition.model';
|
||||||
|
import { RuleSimpleCondition } from '../model/rule-simple-condition.model';
|
||||||
|
|
||||||
|
const simpleConditionMock: RuleSimpleCondition = {
|
||||||
|
field: 'cm:name',
|
||||||
|
comparator: 'equals',
|
||||||
|
parameter: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
const emptyCompositeConditionMock: RuleCompositeCondition = {
|
||||||
|
inverted: false,
|
||||||
|
booleanMode: 'and',
|
||||||
|
compositeConditions: [],
|
||||||
|
simpleConditions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const compositeConditionWithOneConditionMock: RuleCompositeCondition = {
|
||||||
|
...emptyCompositeConditionMock,
|
||||||
|
simpleConditions: [{ ...simpleConditionMock }]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compositeConditionWithOneGroupMock: RuleCompositeCondition = {
|
||||||
|
...emptyCompositeConditionMock,
|
||||||
|
compositeConditions: [{ ...compositeConditionWithOneConditionMock }]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compositeConditionWithNestedGroupsMock: RuleCompositeCondition = {
|
||||||
|
...emptyCompositeConditionMock,
|
||||||
|
compositeConditions: [
|
||||||
|
{
|
||||||
|
...emptyCompositeConditionMock,
|
||||||
|
compositeConditions: [{ ...compositeConditionWithOneConditionMock }]
|
||||||
|
},
|
||||||
|
{ ...compositeConditionWithOneConditionMock }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compositeConditionWithThreeConditionMock: RuleCompositeCondition = {
|
||||||
|
inverted: false,
|
||||||
|
booleanMode: 'and',
|
||||||
|
compositeConditions: [],
|
||||||
|
simpleConditions: [{ ...simpleConditionMock }, { ...simpleConditionMock }, { ...simpleConditionMock }]
|
||||||
|
};
|
@ -0,0 +1,68 @@
|
|||||||
|
<form class="aca-rule-composite-condition__form" [formGroup]="form">
|
||||||
|
<div *ngIf="hasNoConditions" class="aca-rule-composite-condition__form__no-conditions" data-automation-id="no-conditions">
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.' + (childCondition ? 'NO_CONDITIONS_IN_GROUP' : 'NO_CONDITIONS') | translate }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="aca-rule-composite-condition__form__row"
|
||||||
|
*ngFor="let control of conditionFormControls; let i = index">
|
||||||
|
|
||||||
|
<mat-form-field *ngIf="i === 0">
|
||||||
|
<mat-select
|
||||||
|
[value]="invertedControl.value"
|
||||||
|
[disabled]="readOnly"
|
||||||
|
(selectionChange)="setInverted($event.value)">
|
||||||
|
<mat-option [value]="false">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.IF' | translate }}</mat-option>
|
||||||
|
<mat-option [value]="true">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.NOT_IF' | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field *ngIf="i > 0">
|
||||||
|
<mat-select
|
||||||
|
[value]="booleanModeControl.value"
|
||||||
|
[disabled]="i > 1 || readOnly"
|
||||||
|
(selectionChange)="setBooleanMode($event.value)">
|
||||||
|
<mat-option value="and">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.AND' | translate }}</mat-option>
|
||||||
|
<mat-option value="or">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LOGIC_OPERATORS.OR' | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<aca-rule-composite-condition
|
||||||
|
*ngIf="!isFormControlSimpleCondition(control)"
|
||||||
|
[secondaryBackground]="!secondaryBackground"
|
||||||
|
[childCondition]="true"
|
||||||
|
[formControl]="control">
|
||||||
|
</aca-rule-composite-condition>
|
||||||
|
|
||||||
|
<aca-rule-simple-condition
|
||||||
|
*ngIf="isFormControlSimpleCondition(control)"
|
||||||
|
[formControl]="control">
|
||||||
|
</aca-rule-simple-condition>
|
||||||
|
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="menu" *ngIf="!readOnly" data-automation-id="condition-actions-button">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-menu #menu="matMenu">
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
[title]="'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.REMOVE' | translate"
|
||||||
|
(click)="removeCondition(control)">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.REMOVE' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.ADD_CONDITION' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
<button mat-button (click)="addCompositeCondition()" data-automation-id="add-group-button">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.ACTIONS.ADD_GROUP' | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,38 @@
|
|||||||
|
.aca-rule-composite-condition {
|
||||||
|
display: block;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: hsl(0,0%,100%);
|
||||||
|
|
||||||
|
&.childCompositeCondition {
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondaryBackground {
|
||||||
|
background-color: hsl(0,0%,95%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&__no-conditions {
|
||||||
|
color: var(--theme-disabled-text-color);
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
& > :nth-child(1) {
|
||||||
|
width: 5em;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > :nth-child(2) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { RuleCompositeConditionUiComponent } from './rule-composite-condition.ui-component';
|
||||||
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import {
|
||||||
|
compositeConditionWithNestedGroupsMock,
|
||||||
|
compositeConditionWithOneGroupMock,
|
||||||
|
compositeConditionWithThreeConditionMock
|
||||||
|
} from '../../mock/conditions.mock';
|
||||||
|
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
||||||
|
|
||||||
|
describe('RuleCompositeConditionUiComponent', () => {
|
||||||
|
let fixture: ComponentFixture<RuleCompositeConditionUiComponent>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CoreTestingModule],
|
||||||
|
declarations: [RuleCompositeConditionUiComponent, RuleSimpleConditionUiComponent]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RuleCompositeConditionUiComponent);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('No conditions', () => {
|
||||||
|
let noConditionsElement: DebugElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
noConditionsElement = fixture.debugElement.query(By.css(`[data-automation-id="no-conditions"]`));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default to no conditions', () => {
|
||||||
|
const rowElements = fixture.debugElement.queryAll(By.css(`.aca-rule-composite-condition__form__row`));
|
||||||
|
|
||||||
|
expect(rowElements.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show a message if there are no conditions', () => {
|
||||||
|
fixture.componentInstance.childCondition = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect((noConditionsElement.nativeElement as HTMLElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.NO_CONDITIONS');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show a different message if the component is not a root condition group', () => {
|
||||||
|
fixture.componentInstance.childCondition = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect((noConditionsElement.nativeElement as HTMLElement).innerText.trim()).toBe('ACA_FOLDER_RULES.RULE_DETAILS.NO_CONDITIONS_IN_GROUP');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Read only mode', () => {
|
||||||
|
it('should hide the add buttons in read only mode', () => {
|
||||||
|
fixture.componentInstance.setDisabledState(true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const actionsElement = fixture.debugElement.query(By.css(`[data-automation-id="add-actions"]`));
|
||||||
|
|
||||||
|
expect(actionsElement).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide the more actions button on the right side of the condition', () => {
|
||||||
|
fixture.componentInstance.writeValue(compositeConditionWithOneGroupMock);
|
||||||
|
fixture.componentInstance.setDisabledState(true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const actionsButtonElements = fixture.debugElement.queryAll(By.css(`[data-automation-id="condition-actions-button"]`));
|
||||||
|
|
||||||
|
expect(actionsButtonElements.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have as many simple condition components as defined in the simpleConditions array', () => {
|
||||||
|
fixture.componentInstance.writeValue(compositeConditionWithThreeConditionMock);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const simpleConditionComponents = fixture.debugElement.queryAll(By.css(`.aca-rule-simple-condition`));
|
||||||
|
|
||||||
|
expect(simpleConditionComponents.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have as many composite condition components as defined in the compositeConditions array, including nested', () => {
|
||||||
|
fixture.componentInstance.writeValue(compositeConditionWithNestedGroupsMock);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compositeConditionComponents = fixture.debugElement.queryAll(By.css(`.aca-rule-composite-condition`));
|
||||||
|
|
||||||
|
expect(compositeConditionComponents.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a simple condition component on click of add condition button', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const predicate = By.css(`.aca-rule-simple-condition`);
|
||||||
|
const simpleConditionComponentsBeforeClick = fixture.debugElement.queryAll(predicate);
|
||||||
|
|
||||||
|
expect(simpleConditionComponentsBeforeClick.length).toBe(0);
|
||||||
|
|
||||||
|
const addConditionButton = fixture.debugElement.query(By.css(`[data-automation-id="add-condition-button"]`));
|
||||||
|
(addConditionButton.nativeElement as HTMLButtonElement).click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const simpleConditionComponentsAfterClick = fixture.debugElement.queryAll(predicate);
|
||||||
|
|
||||||
|
expect(simpleConditionComponentsAfterClick.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a composite condition component on click of add group button', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const predicate = By.css(`.aca-rule-composite-condition`);
|
||||||
|
const compositeConditionComponentsBeforeClick = fixture.debugElement.queryAll(predicate);
|
||||||
|
|
||||||
|
expect(compositeConditionComponentsBeforeClick.length).toBe(0);
|
||||||
|
|
||||||
|
const addGroupButton = fixture.debugElement.query(By.css(`[data-automation-id="add-group-button"]`));
|
||||||
|
(addGroupButton.nativeElement as HTMLButtonElement).click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compositeConditionComponentsAfterClick = fixture.debugElement.queryAll(predicate);
|
||||||
|
|
||||||
|
expect(compositeConditionComponentsAfterClick.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,157 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { Component, forwardRef, HostBinding, Input, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { RuleCompositeCondition } from '../../model/rule-composite-condition.model';
|
||||||
|
import { ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { RuleSimpleCondition } from '../../model/rule-simple-condition.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aca-rule-composite-condition',
|
||||||
|
templateUrl: './rule-composite-condition.ui-component.html',
|
||||||
|
styleUrls: ['./rule-composite-condition.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-composite-condition' },
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: forwardRef(() => RuleCompositeConditionUiComponent)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RuleCompositeConditionUiComponent implements ControlValueAccessor, OnDestroy {
|
||||||
|
@HostBinding('class.secondaryBackground')
|
||||||
|
@Input()
|
||||||
|
secondaryBackground = false;
|
||||||
|
@HostBinding('class.childCompositeCondition')
|
||||||
|
@Input()
|
||||||
|
childCondition = false;
|
||||||
|
|
||||||
|
form = new FormGroup({
|
||||||
|
inverted: new FormControl(),
|
||||||
|
booleanMode: new FormControl(),
|
||||||
|
compositeConditions: new FormArray([]),
|
||||||
|
simpleConditions: new FormArray([])
|
||||||
|
});
|
||||||
|
|
||||||
|
private formSubscription = this.form.valueChanges.subscribe((value) => {
|
||||||
|
this.onChange(value);
|
||||||
|
this.onTouch();
|
||||||
|
});
|
||||||
|
|
||||||
|
get invertedControl(): FormControl {
|
||||||
|
return this.form.get('inverted') as FormControl;
|
||||||
|
}
|
||||||
|
get booleanModeControl(): FormControl {
|
||||||
|
return this.form.get('booleanMode') as FormControl;
|
||||||
|
}
|
||||||
|
get compositeConditionsFormArray(): FormArray {
|
||||||
|
return this.form.get('compositeConditions') as FormArray;
|
||||||
|
}
|
||||||
|
get simpleConditionsFormArray(): FormArray {
|
||||||
|
return this.form.get('simpleConditions') as FormArray;
|
||||||
|
}
|
||||||
|
get conditionFormControls(): FormControl[] {
|
||||||
|
return [...(this.compositeConditionsFormArray.controls as FormControl[]), ...(this.simpleConditionsFormArray.controls as FormControl[])];
|
||||||
|
}
|
||||||
|
get hasNoConditions(): boolean {
|
||||||
|
return this.conditionFormControls.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _readOnly = false;
|
||||||
|
get readOnly(): boolean {
|
||||||
|
return this._readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange: (condition: RuleCompositeCondition) => void = () => undefined;
|
||||||
|
onTouch: () => void = () => undefined;
|
||||||
|
|
||||||
|
writeValue(value: RuleCompositeCondition) {
|
||||||
|
this.form.get('inverted').setValue(value.inverted);
|
||||||
|
this.form.get('booleanMode').setValue(value.booleanMode);
|
||||||
|
this.form.setControl('compositeConditions', new FormArray(value.compositeConditions.map((condition) => new FormControl(condition))));
|
||||||
|
this.form.setControl('simpleConditions', new FormArray(value.simpleConditions.map((condition) => new FormControl(condition))));
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: () => void) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => void) {
|
||||||
|
this.onTouch = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this._readOnly = true;
|
||||||
|
this.form.disable();
|
||||||
|
} else {
|
||||||
|
this._readOnly = false;
|
||||||
|
this.form.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInverted(value: boolean) {
|
||||||
|
this.invertedControl.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
setBooleanMode(value: 'and' | 'or') {
|
||||||
|
this.booleanModeControl.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
isFormControlSimpleCondition(control: FormControl): boolean {
|
||||||
|
return control.value.hasOwnProperty('field');
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCondition(control: FormControl) {
|
||||||
|
const formArray = this.isFormControlSimpleCondition(control) ? this.simpleConditionsFormArray : this.compositeConditionsFormArray;
|
||||||
|
const index = (formArray.value as FormControl[]).indexOf(control.value);
|
||||||
|
formArray.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSimpleCondition() {
|
||||||
|
const newCondition: RuleSimpleCondition = {
|
||||||
|
field: 'cm:name',
|
||||||
|
comparator: 'equals',
|
||||||
|
parameter: ''
|
||||||
|
};
|
||||||
|
this.simpleConditionsFormArray.push(new FormControl(newCondition));
|
||||||
|
}
|
||||||
|
|
||||||
|
addCompositeCondition() {
|
||||||
|
const newCondition: RuleCompositeCondition = {
|
||||||
|
inverted: false,
|
||||||
|
booleanMode: 'and',
|
||||||
|
compositeConditions: [],
|
||||||
|
simpleConditions: []
|
||||||
|
};
|
||||||
|
this.compositeConditionsFormArray.push(new FormControl(newCondition));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.formSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { RuleCompositeCondition } from '../../model/rule-composite-condition.model';
|
||||||
|
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
|
||||||
|
|
||||||
|
const isCompositeConditionValid = (value: RuleCompositeCondition, isRootCondition = true): boolean => {
|
||||||
|
if (value.compositeConditions.length > 0) {
|
||||||
|
return value.compositeConditions.reduce(
|
||||||
|
(arrayValid: boolean, nestedCondition: RuleCompositeCondition) => arrayValid && isCompositeConditionValid(nestedCondition, false),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return !!value.simpleConditions.length || isRootCondition;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ruleCompositeConditionValidator =
|
||||||
|
(): ValidatorFn =>
|
||||||
|
(control: AbstractControl): ValidationErrors | null =>
|
||||||
|
isCompositeConditionValid(control.value) ? null : { ruleCompositeConditionInvalid: true };
|
@ -0,0 +1,95 @@
|
|||||||
|
/*!
|
||||||
|
* @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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface RuleConditionComparator {
|
||||||
|
name: string;
|
||||||
|
labels: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ruleConditionComparators: RuleConditionComparator[] = [
|
||||||
|
{
|
||||||
|
name: 'equals',
|
||||||
|
labels: {
|
||||||
|
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.EQUALS',
|
||||||
|
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.EQUALS',
|
||||||
|
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ON',
|
||||||
|
special: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'contains',
|
||||||
|
labels: {
|
||||||
|
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.CONTAINS'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'startsWith',
|
||||||
|
labels: {
|
||||||
|
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.STARTS_WITH'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'endsWith',
|
||||||
|
labels: {
|
||||||
|
string: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ENDS_WITH'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'greaterThan',
|
||||||
|
labels: {
|
||||||
|
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.GREATER_THAN',
|
||||||
|
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.AFTER'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lessThan',
|
||||||
|
labels: {
|
||||||
|
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.LESS_THAN',
|
||||||
|
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.BEFORE'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'greaterThanOrEqual',
|
||||||
|
labels: {
|
||||||
|
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.GREATER_THAN_OR_EQUAL',
|
||||||
|
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ON_OR_AFTER'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lessThanOrEqual',
|
||||||
|
labels: {
|
||||||
|
number: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.LESS_THAN_OR_EQUAL',
|
||||||
|
date: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.ON_OR_BEFORE'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'instanceOf',
|
||||||
|
labels: {
|
||||||
|
type: 'ACA_FOLDER_RULES.RULE_DETAILS.COMPARATORS.INSTANCE_OF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,70 @@
|
|||||||
|
/*!
|
||||||
|
* @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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type RuleConditionFieldType = 'string' | 'number' | 'date' | 'type' | 'special';
|
||||||
|
|
||||||
|
export interface RuleConditionField {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
type: RuleConditionFieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ruleConditionFields: RuleConditionField[] = [
|
||||||
|
{
|
||||||
|
name: 'cm:name',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.NAME',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'size',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.SIZE',
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mimetype',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.MIMETYPE',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'encoding',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.ENCODING',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'category',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.HAS_CATEGORY',
|
||||||
|
type: 'special'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tag',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.HAS_TAG',
|
||||||
|
type: 'special'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'aspect',
|
||||||
|
label: 'ACA_FOLDER_RULES.RULE_DETAILS.FIELDS.HAS_ASPECT',
|
||||||
|
type: 'special'
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,24 @@
|
|||||||
|
<form class="aca-rule-simple-condition__form" [formGroup]="form">
|
||||||
|
<mat-form-field class="aca-rule-simple-condition__form__field-input">
|
||||||
|
<mat-select formControlName="field" data-automation-id="field-select"
|
||||||
|
(selectionChange)="onChangeField()">
|
||||||
|
<mat-option *ngFor="let field of fields" [value]="field.name">
|
||||||
|
{{ field.label | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="aca-rule-simple-condition__form__comparator-input" [class]="{ hidden: isComparatorHidden }" data-automation-id="comparator-form-field">
|
||||||
|
<mat-select formControlName="comparator" data-automation-id="comparator-select">
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let comparator of selectedFieldComparators"
|
||||||
|
[value]="comparator.name">
|
||||||
|
{{ comparator.labels[this.selectedField?.type || 'equals'] | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="aca-rule-simple-condition__form__parameter-input">
|
||||||
|
<input matInput type="text" formControlName="parameter" data-automation-id="value-input">
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
@ -0,0 +1,18 @@
|
|||||||
|
.aca-rule-simple-condition {
|
||||||
|
&__form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
flex: 2;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__comparator-input {
|
||||||
|
&.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { RuleSimpleConditionUiComponent } from './rule-simple-condition.ui-component';
|
||||||
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
describe('RuleSimpleConditionUiComponent', () => {
|
||||||
|
let fixture: ComponentFixture<RuleSimpleConditionUiComponent>;
|
||||||
|
|
||||||
|
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],
|
||||||
|
declarations: [RuleSimpleConditionUiComponent]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RuleSimpleConditionUiComponent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default the field to the name, the comparator to equals and the value empty', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getByDataAutomationId('field-select').componentInstance.value).toBe('cm:name');
|
||||||
|
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals');
|
||||||
|
expect(getByDataAutomationId('value-input').nativeElement.value).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide the comparator select box if the type of the field is special', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const comparatorFormField = getByDataAutomationId('comparator-form-field').nativeElement;
|
||||||
|
|
||||||
|
expect(fixture.componentInstance.isComparatorHidden).toBeFalsy();
|
||||||
|
expect(getComputedStyle(comparatorFormField).display).not.toBe('none');
|
||||||
|
|
||||||
|
changeMatSelectValue('field-select', 'category');
|
||||||
|
|
||||||
|
expect(fixture.componentInstance.isComparatorHidden).toBeTruthy();
|
||||||
|
expect(getComputedStyle(comparatorFormField).display).toBe('none');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the comparator to equals if the field is set to a type with different comparators', () => {
|
||||||
|
const onChangeFieldSpy = spyOn(fixture.componentInstance, 'onChangeField').and.callThrough();
|
||||||
|
fixture.detectChanges();
|
||||||
|
changeMatSelectValue('comparator-select', 'contains');
|
||||||
|
|
||||||
|
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('contains');
|
||||||
|
changeMatSelectValue('field-select', 'mimetype');
|
||||||
|
|
||||||
|
expect(onChangeFieldSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('contains');
|
||||||
|
changeMatSelectValue('field-select', 'size');
|
||||||
|
|
||||||
|
expect(onChangeFieldSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(getByDataAutomationId('comparator-select').componentInstance.value).toBe('equals');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,105 @@
|
|||||||
|
/*!
|
||||||
|
* @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 { Component, forwardRef, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { AbstractControl, ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { RuleSimpleCondition } from '../../model/rule-simple-condition.model';
|
||||||
|
import { RuleConditionField, ruleConditionFields } from './rule-condition-fields';
|
||||||
|
import { RuleConditionComparator, ruleConditionComparators } from './rule-condition-comparators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aca-rule-simple-condition',
|
||||||
|
templateUrl: './rule-simple-condition.ui-component.html',
|
||||||
|
styleUrls: ['./rule-simple-condition.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-simple-condition' },
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: forwardRef(() => RuleSimpleConditionUiComponent)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class RuleSimpleConditionUiComponent implements ControlValueAccessor, OnDestroy {
|
||||||
|
readonly fields = ruleConditionFields;
|
||||||
|
|
||||||
|
form = new FormGroup({
|
||||||
|
field: new FormControl('cm:name'),
|
||||||
|
comparator: new FormControl('equals'),
|
||||||
|
parameter: new FormControl()
|
||||||
|
});
|
||||||
|
|
||||||
|
private formSubscription = this.form.valueChanges.subscribe((value) => {
|
||||||
|
this.onChange(value);
|
||||||
|
this.onTouch();
|
||||||
|
});
|
||||||
|
|
||||||
|
get selectedField(): RuleConditionField {
|
||||||
|
return this.fields.find((field) => field.name === this.form.get('field').value);
|
||||||
|
}
|
||||||
|
get selectedFieldComparators(): RuleConditionComparator[] {
|
||||||
|
return ruleConditionComparators.filter((comparator) => Object.keys(comparator.labels).includes(this.selectedField.type));
|
||||||
|
}
|
||||||
|
get isComparatorHidden(): boolean {
|
||||||
|
return this.selectedField?.type === 'special';
|
||||||
|
}
|
||||||
|
get comparatorControl(): AbstractControl {
|
||||||
|
return this.form.get('comparator');
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange: (condition: RuleSimpleCondition) => void = () => undefined;
|
||||||
|
onTouch: () => void = () => undefined;
|
||||||
|
|
||||||
|
writeValue(value: RuleSimpleCondition) {
|
||||||
|
this.form.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: () => void) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => void) {
|
||||||
|
this.onTouch = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.form.disable();
|
||||||
|
} else {
|
||||||
|
this.form.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeField() {
|
||||||
|
if (!this.selectedFieldComparators.find((comparator) => comparator.name === this.comparatorControl.value)) {
|
||||||
|
this.comparatorControl.setValue('equals');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.formSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ import { By } from '@angular/platform-browser';
|
|||||||
import { RuleDetailsUiComponent } from './rule-details.ui-component';
|
import { RuleDetailsUiComponent } from './rule-details.ui-component';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
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';
|
||||||
|
|
||||||
describe('EditRuleDialogComponent', () => {
|
describe('EditRuleDialogComponent', () => {
|
||||||
let fixture: ComponentFixture<EditRuleDialogSmartComponent>;
|
let fixture: ComponentFixture<EditRuleDialogSmartComponent>;
|
||||||
@ -41,7 +42,7 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
const setupBeforeEach = (dialogOptions: EditRuleDialogOptions = {}) => {
|
const setupBeforeEach = (dialogOptions: EditRuleDialogOptions = {}) => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule],
|
imports: [CoreTestingModule],
|
||||||
declarations: [EditRuleDialogSmartComponent, RuleDetailsUiComponent],
|
declarations: [EditRuleDialogSmartComponent, RuleCompositeConditionUiComponent, RuleDetailsUiComponent],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: MatDialogRef, useValue: dialogRef },
|
{ provide: MatDialogRef, useValue: dialogRef },
|
||||||
{ provide: MAT_DIALOG_DATA, useValue: dialogOptions }
|
{ provide: MAT_DIALOG_DATA, useValue: dialogOptions }
|
||||||
@ -105,7 +106,7 @@ describe('EditRuleDialogComponent', () => {
|
|||||||
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 a "create" label in the submit button', () => {
|
it('should show an "update" label in the submit button', () => {
|
||||||
fixture.detectChanges();
|
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;
|
||||||
|
|
||||||
|
@ -24,4 +24,9 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<aca-rule-composite-condition formControlName="conditions"></aca-rule-composite-condition>
|
||||||
|
<mat-error>{{ getErrorMessage(conditions) | translate }}</mat-error>
|
||||||
</form>
|
</form>
|
||||||
|
@ -33,12 +33,17 @@
|
|||||||
border-bottom: 1px solid var(--theme-border-color);
|
border-bottom: 1px solid var(--theme-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
*:disabled {
|
*:disabled, .mat-select-disabled .mat-select-value {
|
||||||
color: #000000;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
min-height: 4em;
|
min-height: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .aca-rule-composite-condition + .mat-error {
|
||||||
|
font-size: 75%;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import { CoreTestingModule } from '@alfresco/adf-core';
|
|||||||
import { RuleDetailsUiComponent } from './rule-details.ui-component';
|
import { RuleDetailsUiComponent } from './rule-details.ui-component';
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { RuleCompositeConditionUiComponent } from './conditions/rule-composite-condition.ui-component';
|
||||||
|
|
||||||
describe('RuleDetailsUiComponent', () => {
|
describe('RuleDetailsUiComponent', () => {
|
||||||
let fixture: ComponentFixture<RuleDetailsUiComponent>;
|
let fixture: ComponentFixture<RuleDetailsUiComponent>;
|
||||||
@ -45,7 +46,7 @@ describe('RuleDetailsUiComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule],
|
imports: [CoreTestingModule],
|
||||||
declarations: [RuleDetailsUiComponent]
|
declarations: [RuleCompositeConditionUiComponent, RuleDetailsUiComponent]
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RuleDetailsUiComponent);
|
fixture = TestBed.createComponent(RuleDetailsUiComponent);
|
||||||
|
@ -24,10 +24,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
|
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
|
import { ruleCompositeConditionValidator } from './conditions/rule-composite-condition.validators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rule-details',
|
selector: 'aca-rule-details',
|
||||||
@ -66,17 +67,26 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
get name(): AbstractControl {
|
get name(): AbstractControl {
|
||||||
return this.form.get('name');
|
return this.form.get('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
get description(): AbstractControl {
|
get description(): AbstractControl {
|
||||||
return this.form.get('description');
|
return this.form.get('description');
|
||||||
}
|
}
|
||||||
|
get conditions(): AbstractControl {
|
||||||
constructor(private formBuilder: FormBuilder) {}
|
return this.form.get('conditions');
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.form = this.formBuilder.group({
|
this.form = new FormGroup({
|
||||||
name: [this.initialValue.name || '', Validators.required],
|
name: new FormControl(this.initialValue.name || '', Validators.required),
|
||||||
description: [this.initialValue.description || '']
|
description: new FormControl(this.initialValue.description || ''),
|
||||||
|
conditions: new FormControl(
|
||||||
|
this.initialValue.conditions || {
|
||||||
|
inverted: false,
|
||||||
|
booleanMode: 'and',
|
||||||
|
compositeConditions: [],
|
||||||
|
simpleConditions: []
|
||||||
|
},
|
||||||
|
ruleCompositeConditionValidator()
|
||||||
|
)
|
||||||
});
|
});
|
||||||
this.readOnly = this._readOnly;
|
this.readOnly = this._readOnly;
|
||||||
|
|
||||||
@ -104,6 +114,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
getErrorMessage(control: AbstractControl): string {
|
getErrorMessage(control: AbstractControl): string {
|
||||||
if (control.hasError('required')) {
|
if (control.hasError('required')) {
|
||||||
return 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.REQUIRED';
|
return 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.REQUIRED';
|
||||||
|
} else if (control.hasError('ruleCompositeConditionInvalid')) {
|
||||||
|
return 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.RULE_COMPOSITE_CONDITION_INVALID';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user