mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACS-3744] Folder rules styling fixes (#2753)
* Folder rules styling fixes * Fix not showing isInheritable in read only
This commit is contained in:
parent
eee6feca1a
commit
e354ec3891
@ -8,7 +8,7 @@
|
|||||||
"CREATE": "Create",
|
"CREATE": "Create",
|
||||||
"CREATE_TITLE": "Create a rule",
|
"CREATE_TITLE": "Create a rule",
|
||||||
"UPDATE": "Update",
|
"UPDATE": "Update",
|
||||||
"UPDATE_TITLE": "Update a rule"
|
"UPDATE_TITLE": "Edit a rule"
|
||||||
},
|
},
|
||||||
"RULE_DETAILS": {
|
"RULE_DETAILS": {
|
||||||
"LABEL": {
|
"LABEL": {
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"IS_ASYNCHRONOUS": "Run rule in the background",
|
"IS_ASYNCHRONOUS": "Run rule in the background",
|
||||||
"DISABLE_RULE": "Disable rule",
|
"DISABLE_RULE": "Disable rule",
|
||||||
"ERROR_SCRIPT": "If errors occur run script",
|
"ERROR_SCRIPT": "If errors occur run script",
|
||||||
"SELECT_ACTION": "Select action"
|
"NO_SCRIPT": "None"
|
||||||
},
|
},
|
||||||
"COMPARATORS": {
|
"COMPARATORS": {
|
||||||
"EQUALS": "(=) Equals",
|
"EQUALS": "(=) Equals",
|
||||||
|
@ -37,7 +37,7 @@ import { RuleSimpleConditionUiComponent } from './rule-details/conditions/rule-s
|
|||||||
import { GenericErrorModule, PageLayoutModule } from '@alfresco/aca-shared';
|
import { GenericErrorModule, PageLayoutModule } from '@alfresco/aca-shared';
|
||||||
import { BreadcrumbModule, DocumentListModule } from '@alfresco/adf-content-services';
|
import { BreadcrumbModule, DocumentListModule } from '@alfresco/adf-content-services';
|
||||||
import { RuleListItemUiComponent } from './rules-list/rule/rule-list-item.ui-component';
|
import { RuleListItemUiComponent } from './rules-list/rule/rule-list-item.ui-component';
|
||||||
import { RulesListUiComponent } from './rules-list/rules-list.ui-component';
|
import { RuleListUiComponent } from './rules-list/rule-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 { RuleActionListUiComponent } from './rule-details/actions/rule-action-list.ui-component';
|
||||||
@ -70,7 +70,7 @@ const routes: Routes = [
|
|||||||
RuleCompositeConditionUiComponent,
|
RuleCompositeConditionUiComponent,
|
||||||
RuleDetailsUiComponent,
|
RuleDetailsUiComponent,
|
||||||
RuleSimpleConditionUiComponent,
|
RuleSimpleConditionUiComponent,
|
||||||
RulesListUiComponent,
|
RuleListUiComponent,
|
||||||
RuleListItemUiComponent,
|
RuleListItemUiComponent,
|
||||||
RuleTriggersUiComponent,
|
RuleTriggersUiComponent,
|
||||||
RuleOptionsUiComponent
|
RuleOptionsUiComponent
|
||||||
|
@ -30,14 +30,22 @@
|
|||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<div class="aca-manage-rules__container" *ngIf="(rules$ | async).length > 0 ; else emptyContent">
|
<div class="aca-manage-rules__container" *ngIf="(rules$ | async).length > 0 ; else emptyContent">
|
||||||
<aca-rules-list [rules]="rules$ | async" (ruleSelected)="onRuleSelected($event)"
|
<aca-rule-list [rules]="rules$ | async" (ruleSelected)="onRuleSelected($event)"
|
||||||
[selectedRule]="selectedRule" [nodeId]="nodeId"></aca-rules-list>
|
[selectedRule]="selectedRule" [nodeId]="nodeId"></aca-rule-list>
|
||||||
<div class="aca-manage-rules__container__rule-details">
|
<div class="aca-manage-rules__container__rule-details">
|
||||||
<div class="aca-manage-rules__container__preview">
|
|
||||||
<div class="aca-manage-rules__container__preview__toolbar">
|
<div class="aca-manage-rules__container__rule-details__header">
|
||||||
<span>{{ selectedRule.name }}</span>
|
<div class="aca-manage-rules__container__rule-details__header__title">
|
||||||
<div class="aca-manage-rules__container__preview__toolbar__buttons">
|
<div class="aca-manage-rules__container__rule-details__header__title__name">
|
||||||
<button mat-icon-button (click)="onRuleDelete()" id="delete-rule-btn">
|
{{ selectedRule.name }}
|
||||||
|
</div>
|
||||||
|
<div class="aca-manage-rules__container__rule-details__header__title__description">
|
||||||
|
{{ selectedRule.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="aca-manage-rules__container__rule-details__header__buttons">
|
||||||
|
<button mat-stroked-button (click)="onRuleDelete()" id="delete-rule-btn">
|
||||||
<mat-icon>delete_outline</mat-icon>
|
<mat-icon>delete_outline</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-stroked-button (click)="onRuleUpdate()" id="edit-rule-btn">
|
<button mat-stroked-button (click)="onRuleUpdate()" id="edit-rule-btn">
|
||||||
@ -45,8 +53,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{ selectedRule.description }}</p>
|
|
||||||
</div>
|
<div class="aca-manage-rules__container__rule-details__content">
|
||||||
<aca-rule-details
|
<aca-rule-details
|
||||||
[actionDefinitions]="actionDefinitions$ | async"
|
[actionDefinitions]="actionDefinitions$ | async"
|
||||||
[readOnly]="true"
|
[readOnly]="true"
|
||||||
@ -55,6 +63,7 @@
|
|||||||
</aca-rule-details>
|
</aca-rule-details>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-template #emptyContent>
|
<ng-template #emptyContent>
|
||||||
<adf-empty-content
|
<adf-empty-content
|
||||||
|
@ -17,40 +17,65 @@
|
|||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(200px,1fr) 3fr;
|
grid-template-columns: minmax(250px,1fr) 3fr;
|
||||||
padding: 32px;
|
padding: 20px;
|
||||||
overflow: scroll;
|
gap: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
&__preview {
|
&__rule-details {
|
||||||
padding: 0 20px;
|
border: 1px solid var(--theme-border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
&__toolbar {
|
&__header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-bottom: 1px solid var(--theme-border-color);
|
||||||
|
background-color: var(--theme-background-color);
|
||||||
|
|
||||||
span {
|
&__title {
|
||||||
font-style: normal;
|
display: flex;
|
||||||
font-weight: 700;
|
flex-direction: column;
|
||||||
font-size: 14px;
|
gap: 4px;
|
||||||
line-height: 20px;
|
|
||||||
|
&__name {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__buttons {
|
&__buttons {
|
||||||
display: inline-block;
|
display: flex;
|
||||||
}
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: var(--theme-text-color);
|
||||||
|
|
||||||
|
&#delete-rule-btn {
|
||||||
|
padding: 0 8px;
|
||||||
|
min-width: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.mat-icon {
|
||||||
font-style: normal;
|
// Something pops out of this button for some reason so this is necessary
|
||||||
font-weight: 400;
|
overflow: hidden !important;
|
||||||
font-size: 12px;
|
}
|
||||||
line-height: 16px;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__rule-details {
|
|
||||||
overflow-x: scroll;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
|
|
||||||
expect(folderRulesService.loadRules).toHaveBeenCalledOnceWith(component.nodeId);
|
expect(folderRulesService.loadRules).toHaveBeenCalledOnceWith(component.nodeId);
|
||||||
|
|
||||||
const rules = debugElement.queryAll(By.css('.aca-rule'));
|
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||||
const ruleDetails = debugElement.queryAll(By.css('aca-rule-details'));
|
const ruleDetails = debugElement.queryAll(By.css('aca-rule-details'));
|
||||||
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
|
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
|
||||||
|
|
||||||
|
@ -41,3 +41,20 @@ export interface Rule {
|
|||||||
conditions: RuleCompositeCondition;
|
conditions: RuleCompositeCondition;
|
||||||
actions: RuleAction[];
|
actions: RuleAction[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RuleOptions {
|
||||||
|
isEnabled: boolean;
|
||||||
|
isInheritable: boolean;
|
||||||
|
isAsynchronous: boolean;
|
||||||
|
errorScript: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RuleForForm {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
triggers: RuleTrigger[];
|
||||||
|
conditions: RuleCompositeCondition;
|
||||||
|
actions: RuleAction[];
|
||||||
|
options: RuleOptions;
|
||||||
|
}
|
||||||
|
@ -1,34 +1,36 @@
|
|||||||
<div class="options-list" [formGroup]="form">
|
<ng-container [formGroup]="form">
|
||||||
<div class="options-list__asynchronous">
|
<div class="aca-rule-options__option" *ngIf="!readOnly || isAsynchronousChecked">
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
formControlName="isAsynchronous"
|
formControlName="isAsynchronous"
|
||||||
(change)="toggleScriptSelector()"
|
(change)="toggleErrorScriptDropdown($event)"
|
||||||
[attr.data-automation-id]="'rule-option-checkbox-asynchronous'">
|
data-automation-id="rule-option-checkbox-asynchronous">
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.IS_ASYNCHRONOUS' | translate }}
|
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.IS_ASYNCHRONOUS' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
<div class="select-action" *ngIf="!preview">
|
|
||||||
<span>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.ERROR_SCRIPT' | translate}}:</span>
|
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select [disabled]="disableSelector" formControlName="errorScript"
|
<mat-select
|
||||||
placeholder="{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.SELECT_ACTION' | translate}}"
|
formControlName="errorScript"
|
||||||
[attr.data-automation-id]="'rule-option-select-errorScript'">
|
placeholder="{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.ERROR_SCRIPT' | translate}}"
|
||||||
<mat-option>{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.SELECT_ACTION' | translate}}</mat-option>
|
data-automation-id="rule-option-select-errorScript">
|
||||||
|
<mat-option value="">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.NO_SCRIPT' | translate }}</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="options-list__rest">
|
|
||||||
|
<div class="aca-rule-options__option" *ngIf="!readOnly || isInheritableChecked">
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
formControlName="isInheritable"
|
formControlName="isInheritable"
|
||||||
[attr.data-automation-id]="'rule-option-checkbox-inheritable'">
|
data-automation-id="rule-option-checkbox-inheritable">
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.IS_INHERITABLE' | translate }}
|
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.IS_INHERITABLE' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="aca-rule-options__option" *ngIf="!readOnly">
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
[attr.data-automation-id]="'rule-option-checkbox-enabled'"
|
formControlName="isDisabled"
|
||||||
[checked]="!form.get('isEnabled').value" *ngIf="!preview"
|
data-automation-id="rule-option-checkbox-disabled">
|
||||||
(change)="form.get('isEnabled').setValue(!$event.checked)">
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.DISABLE_RULE' | translate }}
|
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.OPTIONS.DISABLE_RULE' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
.options-list {
|
.aca-rule-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
column-gap: 25px;
|
flex-direction: row;
|
||||||
padding: 0.75em 0;
|
gap: 24px;
|
||||||
|
|
||||||
&__rest {
|
&__option {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-direction: column;
|
||||||
column-gap: 25px;
|
}
|
||||||
}
|
|
||||||
}
|
&.read-only .mat-checkbox-inner-container {
|
||||||
|
display: none;
|
||||||
.select-action {
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-top: 12px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,16 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed, inject, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule, FormBuilder } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { RuleOptionsUiComponent } from './rule-options.ui-component';
|
import { RuleOptionsUiComponent } from './rule-options.ui-component';
|
||||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
describe('RuleOptionsUiComponent', () => {
|
describe('RuleOptionsUiComponent', () => {
|
||||||
let component: RuleOptionsUiComponent;
|
|
||||||
let fixture: ComponentFixture<RuleOptionsUiComponent>;
|
let fixture: ComponentFixture<RuleOptionsUiComponent>;
|
||||||
|
let component: RuleOptionsUiComponent;
|
||||||
|
|
||||||
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
const getByDataAutomationId = (dataAutomationId: string): DebugElement =>
|
||||||
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
fixture.debugElement.query(By.css(`[data-automation-id="${dataAutomationId}"]`));
|
||||||
@ -41,54 +41,75 @@ describe('RuleOptionsUiComponent', () => {
|
|||||||
((getByDataAutomationId(dataAutomationId).nativeElement as HTMLElement).children[0] as HTMLElement).click();
|
((getByDataAutomationId(dataAutomationId).nativeElement as HTMLElement).children[0] as HTMLElement).click();
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(
|
beforeEach(() => {
|
||||||
waitForAsync(() => {
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
imports: [FormsModule, ReactiveFormsModule, CoreTestingModule],
|
imports: [FormsModule, ReactiveFormsModule, CoreTestingModule],
|
||||||
declarations: [RuleOptionsUiComponent]
|
declarations: [RuleOptionsUiComponent]
|
||||||
}).compileComponents();
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
beforeEach(inject([FormBuilder], (fb: FormBuilder) => {
|
|
||||||
fixture = TestBed.createComponent(RuleOptionsUiComponent);
|
fixture = TestBed.createComponent(RuleOptionsUiComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
component.form = fb.group({
|
component.writeValue({
|
||||||
isAsynchronous: [false],
|
isEnabled: true,
|
||||||
isInheritable: [false],
|
isInheritable: false,
|
||||||
isEnabled: [true],
|
isAsynchronous: false,
|
||||||
errorScript: ['']
|
errorScript: ''
|
||||||
|
});
|
||||||
});
|
});
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('checkboxes should be falsy by default, selector is disabled', () => {
|
it('should be able to write to the component', () => {
|
||||||
expect(component).toBeTruthy();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeFalsy();
|
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeFalsy();
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-inheritable').componentInstance.checked).toBeFalsy();
|
expect(getByDataAutomationId('rule-option-checkbox-inheritable').componentInstance.checked).toBeFalsy();
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-enabled').componentInstance.checked).toBeFalsy();
|
expect(getByDataAutomationId('rule-option-checkbox-disabled').componentInstance.checked).toBeFalsy();
|
||||||
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeTruthy();
|
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeTruthy();
|
||||||
|
|
||||||
|
component.writeValue({
|
||||||
|
isEnabled: false,
|
||||||
|
isInheritable: true,
|
||||||
|
isAsynchronous: true,
|
||||||
|
errorScript: ''
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeTruthy();
|
||||||
|
expect(getByDataAutomationId('rule-option-checkbox-inheritable').componentInstance.checked).toBeTruthy();
|
||||||
|
expect(getByDataAutomationId('rule-option-checkbox-disabled').componentInstance.checked).toBeTruthy();
|
||||||
|
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should enable selector when async checkbox is truthy', () => {
|
it('should enable selector when async checkbox is truthy', () => {
|
||||||
toggleMatCheckbox('rule-option-checkbox-asynchronous');
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
toggleMatCheckbox('rule-option-checkbox-asynchronous');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeTruthy();
|
expect(getByDataAutomationId('rule-option-checkbox-asynchronous').componentInstance.checked).toBeTruthy();
|
||||||
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeFalsy();
|
expect(getByDataAutomationId('rule-option-select-errorScript').componentInstance.disabled).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide some fields in preview mode', () => {
|
it('should hide disabled checkbox and unselected checkboxes in read-only mode', () => {
|
||||||
component.preview = true;
|
component.readOnly = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getByDataAutomationId('rule-option-checkbox-asynchronous')).toBeFalsy();
|
||||||
|
expect(getByDataAutomationId('rule-option-checkbox-inheritable')).toBeFalsy();
|
||||||
|
expect(getByDataAutomationId('rule-option-checkbox-enabled')).toBeFalsy();
|
||||||
|
expect(getByDataAutomationId('rule-option-select-errorScript')).toBeFalsy();
|
||||||
|
|
||||||
|
component.writeValue({
|
||||||
|
isEnabled: false,
|
||||||
|
isInheritable: true,
|
||||||
|
isAsynchronous: true,
|
||||||
|
errorScript: ''
|
||||||
|
});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous')).toBeTruthy();
|
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-asynchronous')).toBeTruthy();
|
expect(getByDataAutomationId('rule-option-checkbox-asynchronous')).toBeTruthy();
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-inheritable')).toBeTruthy();
|
expect(getByDataAutomationId('rule-option-checkbox-inheritable')).toBeTruthy();
|
||||||
expect(getByDataAutomationId('rule-option-checkbox-enabled')).toBeFalsy();
|
expect(getByDataAutomationId('rule-option-checkbox-enabled')).toBeFalsy();
|
||||||
expect(getByDataAutomationId('rule-option-select-errorScript')).toBeFalsy();
|
expect(getByDataAutomationId('rule-option-select-errorScript')).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -23,20 +23,98 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, forwardRef, HostBinding, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||||
import { FormGroup } from '@angular/forms';
|
import { AbstractControl, ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||||
|
import { RuleOptions } from '../../model/rule.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rule-options',
|
selector: 'aca-rule-options',
|
||||||
templateUrl: 'rule-options.ui-component.html',
|
templateUrl: 'rule-options.ui-component.html',
|
||||||
styleUrls: ['rule-options.ui-component.scss']
|
styleUrls: ['rule-options.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-options' },
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: forwardRef(() => RuleOptionsUiComponent)
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class RuleOptionsUiComponent {
|
export class RuleOptionsUiComponent implements ControlValueAccessor, OnDestroy {
|
||||||
@Input() form: FormGroup;
|
form = new FormGroup({
|
||||||
@Input() preview: boolean;
|
isDisabled: new FormControl(),
|
||||||
disableSelector = true;
|
isInheritable: new FormControl(),
|
||||||
|
isAsynchronous: new FormControl(),
|
||||||
|
errorScript: new FormControl()
|
||||||
|
});
|
||||||
|
|
||||||
toggleScriptSelector() {
|
formSubscription = this.form.valueChanges.subscribe((value: any) => {
|
||||||
this.disableSelector = !this.disableSelector;
|
this.onChange({
|
||||||
|
isEnabled: !value.isDisabled,
|
||||||
|
isInheritable: value.isInheritable,
|
||||||
|
isAsynchronous: value.isAsynchronous,
|
||||||
|
errorScript: value.errorScript ?? ''
|
||||||
|
});
|
||||||
|
this.onTouch();
|
||||||
|
});
|
||||||
|
|
||||||
|
@HostBinding('class.read-only')
|
||||||
|
readOnly = false;
|
||||||
|
|
||||||
|
onChange: (options: RuleOptions) => void = () => undefined;
|
||||||
|
onTouch: () => void = () => undefined;
|
||||||
|
|
||||||
|
get isAsynchronousChecked(): boolean {
|
||||||
|
return this.form.get('isAsynchronous').value;
|
||||||
|
}
|
||||||
|
get isInheritableChecked(): boolean {
|
||||||
|
return this.form.get('isInheritable').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(options: RuleOptions) {
|
||||||
|
const isAsynchronousFormControl = this.form.get('isAsynchronous');
|
||||||
|
const errorScriptFormControl = this.form.get('errorScript');
|
||||||
|
this.form.get('isDisabled').setValue(!options.isEnabled);
|
||||||
|
this.form.get('isInheritable').setValue(options.isInheritable);
|
||||||
|
this.form.get('isAsynchronous').setValue(options.isAsynchronous);
|
||||||
|
errorScriptFormControl.setValue(options.errorScript ?? '');
|
||||||
|
if (isAsynchronousFormControl.value) {
|
||||||
|
errorScriptFormControl.enable();
|
||||||
|
} else {
|
||||||
|
errorScriptFormControl.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: () => void) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => void) {
|
||||||
|
this.onTouch = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.form.disable();
|
||||||
|
this.readOnly = true;
|
||||||
|
} else {
|
||||||
|
this.form.enable();
|
||||||
|
this.readOnly = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.formSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleErrorScriptDropdown(value: MatCheckboxChange) {
|
||||||
|
const formControl: AbstractControl = this.form.get('errorScript');
|
||||||
|
if (value.checked) {
|
||||||
|
formControl.enable();
|
||||||
|
} else {
|
||||||
|
formControl.disable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<form class="aca-rule-details__form" [formGroup]="form">
|
<form class="aca-rule-details__form" [ngClass]="{ 'read-only': readOnly }" [formGroup]="form">
|
||||||
|
|
||||||
<ng-container *ngIf="!preview">
|
<ng-container *ngIf="!preview">
|
||||||
<div class="aca-rule-details__form__row">
|
<div class="aca-rule-details__form__row aca-rule-details__form__name">
|
||||||
<label for="rule-details-name-input">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.NAME' | translate }}</label>
|
<label for="rule-details-name-input">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.NAME' | translate }}</label>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field floatLabel='never'>
|
<mat-form-field floatLabel='never'>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="aca-rule-details__form__row">
|
<div class="aca-rule-details__form__row aca-rule-details__form__description">
|
||||||
<label for="rule-details-description-textarea">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.DESCRIPTION' | translate }}</label>
|
<label for="rule-details-description-textarea">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.DESCRIPTION' | translate }}</label>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field floatLabel='never'>
|
<mat-form-field floatLabel='never'>
|
||||||
@ -28,8 +28,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="aca-rule-details__form__row aca-rule-details__form__triggers">
|
<div class="aca-rule-details__form__row aca-rule-details__form__triggers">
|
||||||
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.WHEN' | translate }}</div>
|
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.WHEN' | translate }}</div>
|
||||||
<div>
|
<div>
|
||||||
@ -38,12 +36,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="aca-rule-details__form__conditions">
|
||||||
|
|
||||||
<aca-rule-composite-condition formControlName="conditions"></aca-rule-composite-condition>
|
<aca-rule-composite-condition formControlName="conditions"></aca-rule-composite-condition>
|
||||||
<mat-error class="rule-details-error">{{ getErrorMessage(conditions) | translate }}</mat-error>
|
<mat-error class="rule-details-error">{{ getErrorMessage(conditions) | translate }}</mat-error>
|
||||||
|
</div>
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="aca-rule-details__form__row aca-rule-details__form__actions">
|
<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>
|
<div class="label">{{ 'ACA_FOLDER_RULES.RULE_DETAILS.LABEL.PERFORM_ACTIONS' | translate }}</div>
|
||||||
@ -55,11 +51,9 @@
|
|||||||
</aca-rule-action-list>
|
</aca-rule-action-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="aca-rule-details__form__row" *ngIf="showOptionsSection">
|
||||||
|
|
||||||
<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 formControlName="options" data-automation-id="rule-details-options-component"></aca-rule-options>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,9 +1,31 @@
|
|||||||
.aca-rule-details {
|
.aca-rule-details {
|
||||||
&__form {
|
&__form {
|
||||||
padding: 20px;
|
position: relative;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
padding: 8px 20px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
&:not(:nth-child(1)) {
|
||||||
|
border-top: 1px solid var(--theme-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.aca-rule-details__form__name {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.aca-rule-details__form__description {
|
||||||
|
padding-top: 0;
|
||||||
|
border: none;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__row {
|
&__row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
& > label, & > .label {
|
& > label, & > .label {
|
||||||
@ -11,7 +33,7 @@
|
|||||||
width: 20%;
|
width: 20%;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
padding-top: 0.75em;
|
padding: 0.75em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
@ -19,6 +41,7 @@
|
|||||||
|
|
||||||
mat-form-field {
|
mat-form-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
|
||||||
.mat-form-field-infix {
|
.mat-form-field-infix {
|
||||||
@ -28,22 +51,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__triggers {
|
&__conditions {
|
||||||
padding: 0.75em 0;
|
width: 100%;
|
||||||
|
|
||||||
& > .label {
|
& > .rule-details-error {
|
||||||
padding: 0;
|
margin-left: 16px;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid var(--theme-border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
*:disabled, .mat-select-disabled .mat-select-value {
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
min-height: 4em;
|
min-height: 4em;
|
||||||
@ -54,15 +69,20 @@
|
|||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .aca-rule-composite-condition + .rule-details-error {
|
|
||||||
margin-left: 16px;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
.aca-rule-action-list {
|
.aca-rule-action-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.read-only, .mat-form-field-disabled {
|
||||||
|
.mat-form-field-underline, .mat-select-arrow-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:disabled, .mat-select-disabled .mat-select-value {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ 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';
|
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 { 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 { RuleActionListUiComponent } from './actions/rule-action-list.ui-component';
|
||||||
import { RuleActionUiComponent } from './actions/rule-action.ui-component';
|
import { RuleActionUiComponent } from './actions/rule-action.ui-component';
|
||||||
@ -46,7 +45,8 @@ describe('RuleDetailsUiComponent', () => {
|
|||||||
triggers: ['update', 'outbound'],
|
triggers: ['update', 'outbound'],
|
||||||
isAsynchronous: true,
|
isAsynchronous: true,
|
||||||
isInheritable: true,
|
isInheritable: true,
|
||||||
isEnabled: true
|
isEnabled: true,
|
||||||
|
errorScript: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHtmlElement = <T>(dataAutomationId: string) =>
|
const getHtmlElement = <T>(dataAutomationId: string) =>
|
||||||
@ -79,16 +79,17 @@ describe('RuleDetailsUiComponent', () => {
|
|||||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||||
const ruleOptionAsynchronous = getComponentInstance<MatCheckbox>('rule-option-checkbox-asynchronous');
|
const ruleOptionsComponent = getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component');
|
||||||
const ruleOptionInheritable = getComponentInstance<MatCheckbox>('rule-option-checkbox-inheritable');
|
|
||||||
const ruleOptionDisabled = getComponentInstance<MatCheckbox>('rule-option-checkbox-enabled');
|
|
||||||
|
|
||||||
expect(nameInput.value).toBe(testValue.name);
|
expect(nameInput.value).toBe(testValue.name);
|
||||||
expect(descriptionTextarea.value).toBe(testValue.description);
|
expect(descriptionTextarea.value).toBe(testValue.description);
|
||||||
expect(ruleTriggersComponent.value).toEqual(testValue.triggers);
|
expect(ruleTriggersComponent.value).toEqual(testValue.triggers);
|
||||||
expect(ruleOptionAsynchronous.checked).toBe(testValue.isAsynchronous);
|
expect(ruleOptionsComponent.form.value).toEqual({
|
||||||
expect(ruleOptionInheritable.checked).toBe(testValue.isInheritable);
|
isDisabled: !testValue.isEnabled,
|
||||||
expect(ruleOptionDisabled.checked).toBe(!testValue.isEnabled);
|
isInheritable: testValue.isInheritable,
|
||||||
|
isAsynchronous: testValue.isAsynchronous,
|
||||||
|
errorScript: testValue.errorScript
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should modify the form if the value input property is modified', () => {
|
it('should modify the form if the value input property is modified', () => {
|
||||||
@ -109,38 +110,46 @@ describe('RuleDetailsUiComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be editable if not read-only', () => {
|
it('should be editable if not read-only', () => {
|
||||||
|
component.value = testValue;
|
||||||
component.readOnly = false;
|
component.readOnly = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||||
const ruleOptionAsynchronous = getComponentInstance<MatCheckbox>('rule-option-checkbox-asynchronous');
|
const ruleOptionsComponent = getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component');
|
||||||
const ruleOptionInheritable = getComponentInstance<MatCheckbox>('rule-option-checkbox-inheritable');
|
|
||||||
const ruleOptionDisabled = getComponentInstance<MatCheckbox>('rule-option-checkbox-enabled');
|
|
||||||
|
|
||||||
expect(nameInput.disabled).toBeFalsy();
|
expect(nameInput.disabled).toBeFalsy();
|
||||||
expect(descriptionTextarea.disabled).toBeFalsy();
|
expect(descriptionTextarea.disabled).toBeFalsy();
|
||||||
expect(ruleTriggersComponent.readOnly).toBeFalsy();
|
expect(ruleTriggersComponent.readOnly).toBeFalsy();
|
||||||
expect(ruleOptionAsynchronous.disabled).toBeFalsy();
|
expect(ruleOptionsComponent.readOnly).toBeFalsy();
|
||||||
expect(ruleOptionInheritable.disabled).toBeFalsy();
|
|
||||||
expect(ruleOptionDisabled.disabled).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be editable if read-only', () => {
|
it('should not be editable if read-only', () => {
|
||||||
|
component.value = testValue;
|
||||||
component.readOnly = true;
|
component.readOnly = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
const nameInput = getHtmlElement<HTMLInputElement>('rule-details-name-input');
|
||||||
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
const descriptionTextarea = getHtmlElement<HTMLTextAreaElement>('rule-details-description-textarea');
|
||||||
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
const ruleTriggersComponent = getComponentInstance<RuleTriggersUiComponent>('rule-details-triggers-component');
|
||||||
const ruleOptionAsynchronous = getComponentInstance<MatCheckbox>('rule-option-checkbox-asynchronous');
|
const ruleOptionsComponent = getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component');
|
||||||
const ruleOptionInheritable = getComponentInstance<MatCheckbox>('rule-option-checkbox-inheritable');
|
|
||||||
|
|
||||||
expect(nameInput.disabled).toBeTruthy();
|
expect(nameInput.disabled).toBeTruthy();
|
||||||
expect(descriptionTextarea.disabled).toBeTruthy();
|
expect(descriptionTextarea.disabled).toBeTruthy();
|
||||||
expect(ruleTriggersComponent.readOnly).toBeTruthy();
|
expect(ruleTriggersComponent.readOnly).toBeTruthy();
|
||||||
expect(ruleOptionAsynchronous.disabled).toBeTruthy();
|
expect(ruleOptionsComponent.readOnly).toBeTruthy();
|
||||||
expect(ruleOptionInheritable.disabled).toBeTruthy();
|
});
|
||||||
|
|
||||||
|
it('should hide the options section entirely in read-only mode if it has no selected options', () => {
|
||||||
|
component.value = {
|
||||||
|
...testValue,
|
||||||
|
isInheritable: false,
|
||||||
|
isAsynchronous: false
|
||||||
|
};
|
||||||
|
component.readOnly = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getComponentInstance<RuleOptionsUiComponent>('rule-details-options-component')).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -27,7 +27,7 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewEncapsul
|
|||||||
import { AbstractControl, UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms';
|
import { AbstractControl, UntypedFormGroup, UntypedFormControl, 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, RuleForForm } 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';
|
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||||
@ -57,28 +57,38 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private _initialValue: Partial<Rule> = FolderRulesService.emptyRule;
|
private _initialValue: RuleForForm = FolderRulesService.emptyRuleForForm;
|
||||||
@Input()
|
@Input()
|
||||||
get value(): Partial<Rule> {
|
get value(): Partial<Rule> {
|
||||||
return this.form ? this.form.value : this._initialValue;
|
let value = this.form ? this.form.value : this._initialValue;
|
||||||
|
if (value.options) {
|
||||||
|
value = {
|
||||||
|
...value,
|
||||||
|
...(value.options ?? FolderRulesService.emptyRuleOptions)
|
||||||
|
};
|
||||||
|
delete value.options;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
set value(newValue: Partial<Rule>) {
|
set value(newValue: Partial<Rule>) {
|
||||||
newValue = {
|
const newValueForForm: RuleForForm = {
|
||||||
id: newValue.id || FolderRulesService.emptyRule.id,
|
id: newValue.id || FolderRulesService.emptyRule.id,
|
||||||
name: newValue.name || FolderRulesService.emptyRule.name,
|
name: newValue.name || FolderRulesService.emptyRule.name,
|
||||||
description: newValue.description || FolderRulesService.emptyRule.description,
|
description: newValue.description || FolderRulesService.emptyRule.description,
|
||||||
triggers: newValue.triggers || FolderRulesService.emptyRule.triggers,
|
triggers: newValue.triggers || FolderRulesService.emptyRule.triggers,
|
||||||
conditions: newValue.conditions || FolderRulesService.emptyRule.conditions,
|
conditions: newValue.conditions || FolderRulesService.emptyRule.conditions,
|
||||||
isAsynchronous: newValue.isAsynchronous || FolderRulesService.emptyRule.isAsynchronous,
|
actions: newValue.actions || FolderRulesService.emptyRule.actions,
|
||||||
errorScript: newValue.errorScript || FolderRulesService.emptyRule.errorScript,
|
options: {
|
||||||
isInheritable: newValue.isInheritable || FolderRulesService.emptyRule.isInheritable,
|
|
||||||
isEnabled: typeof newValue.isInheritable == 'boolean' ? newValue.isEnabled : FolderRulesService.emptyRule.isEnabled,
|
isEnabled: typeof newValue.isInheritable == 'boolean' ? newValue.isEnabled : FolderRulesService.emptyRule.isEnabled,
|
||||||
actions: newValue.actions || FolderRulesService.emptyRule.actions
|
isInheritable: newValue.isInheritable || FolderRulesService.emptyRule.isInheritable,
|
||||||
|
isAsynchronous: newValue.isAsynchronous || FolderRulesService.emptyRule.isAsynchronous,
|
||||||
|
errorScript: newValue.errorScript || FolderRulesService.emptyRule.errorScript
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (this.form) {
|
if (this.form) {
|
||||||
this.form.setValue(newValue);
|
this.form.setValue(newValueForForm);
|
||||||
} else {
|
} else {
|
||||||
this._initialValue = newValue;
|
this._initialValue = newValueForForm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Input()
|
@Input()
|
||||||
@ -108,17 +118,9 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
get conditions(): UntypedFormControl {
|
get conditions(): UntypedFormControl {
|
||||||
return this.form.get('conditions') as UntypedFormControl;
|
return this.form.get('conditions') as UntypedFormControl;
|
||||||
}
|
}
|
||||||
get isAsynchronous(): UntypedFormControl {
|
|
||||||
return this.form.get('isAsynchronous') as UntypedFormControl;
|
get showOptionsSection(): boolean {
|
||||||
}
|
return !this.readOnly || this.value.isAsynchronous || this.value.isInheritable;
|
||||||
get errorScript(): UntypedFormControl {
|
|
||||||
return this.form.get('errorScript') as UntypedFormControl;
|
|
||||||
}
|
|
||||||
get isInheritable(): UntypedFormControl {
|
|
||||||
return this.form.get('isInheritable') as UntypedFormControl;
|
|
||||||
}
|
|
||||||
get isEnabled(): UntypedFormControl {
|
|
||||||
return this.form.get('isEnabled') as UntypedFormControl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -136,11 +138,13 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
ruleCompositeConditionValidator()
|
ruleCompositeConditionValidator()
|
||||||
),
|
),
|
||||||
isAsynchronous: new UntypedFormControl(this.value.isAsynchronous),
|
actions: new UntypedFormControl(this.value.actions, [Validators.required, ruleActionsValidator(this.actionDefinitions)]),
|
||||||
errorScript: new UntypedFormControl(this.value.errorScript),
|
options: new UntypedFormControl({
|
||||||
isInheritable: new UntypedFormControl(this.value.isInheritable),
|
isEnabled: this.value.isEnabled,
|
||||||
isEnabled: new UntypedFormControl(this.value.isEnabled),
|
isInheritable: this.value.isInheritable,
|
||||||
actions: new UntypedFormControl(this.value.actions, [Validators.required, ruleActionsValidator(this.actionDefinitions)])
|
isAsynchronous: this.value.isAsynchronous,
|
||||||
|
errorScript: this.value.errorScript
|
||||||
|
})
|
||||||
});
|
});
|
||||||
this.readOnly = this._readOnly;
|
this.readOnly = this._readOnly;
|
||||||
|
|
||||||
@ -155,8 +159,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
this.formValidationChanged.emit(this.form.valid);
|
this.formValidationChanged.emit(this.form.valid);
|
||||||
|
|
||||||
this.form.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe((newFormValue: any) => {
|
this.form.valueChanges.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
|
||||||
this.formValueChanged.emit(newFormValue);
|
this.formValueChanged.emit(this.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
[attr.data-automation-id]="'rule-trigger-checkbox-' + trigger | lowercase"
|
[attr.data-automation-id]="'rule-trigger-checkbox-' + trigger | lowercase"
|
||||||
[checked]="isTriggerChecked(trigger)"
|
[checked]="isTriggerChecked(trigger)"
|
||||||
[disabled]="readOnly"
|
|
||||||
(change)="onTriggerChange(trigger, $event.checked)">
|
(change)="onTriggerChange(trigger, $event.checked)">
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.TRIGGERS.' + trigger | uppercase | translate }}
|
{{ 'ACA_FOLDER_RULES.RULE_DETAILS.TRIGGERS.' + trigger | uppercase | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
@ -57,7 +57,7 @@ export class RuleTriggersUiComponent implements ControlValueAccessor {
|
|||||||
this.onChange = fn;
|
this.onChange = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnTouched(fn: any) {
|
registerOnTouched(fn: () => void) {
|
||||||
this.onTouch = fn;
|
this.onTouch = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<div class="aca-rules-list" >
|
||||||
|
<aca-rule-list-item
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||||
|
*ngFor="let rule of rules"
|
||||||
|
[rule]="rule"
|
||||||
|
[isSelected]="isSelected(rule)"
|
||||||
|
[nodeId]="nodeId"
|
||||||
|
(click)="onRuleClicked(rule)">
|
||||||
|
</aca-rule-list-item>
|
||||||
|
</div>
|
@ -0,0 +1,5 @@
|
|||||||
|
.aca-rule-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
@ -24,25 +24,25 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { RulesListUiComponent } from './rules-list.ui-component';
|
import { RuleListUiComponent } from './rule-list.ui-component';
|
||||||
import { dummyRules } from '../mock/rules.mock';
|
import { dummyRules } from '../mock/rules.mock';
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
import { AcaFolderRulesModule } from '@alfresco/aca-folder-rules';
|
import { AcaFolderRulesModule } from '@alfresco/aca-folder-rules';
|
||||||
|
|
||||||
describe('RulesListComponent', () => {
|
describe('RuleListComponent', () => {
|
||||||
let component: RulesListUiComponent;
|
let component: RuleListUiComponent;
|
||||||
let fixture: ComponentFixture<RulesListUiComponent>;
|
let fixture: ComponentFixture<RuleListUiComponent>;
|
||||||
let debugElement: DebugElement;
|
let debugElement: DebugElement;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule, AcaFolderRulesModule],
|
imports: [CoreTestingModule, AcaFolderRulesModule],
|
||||||
declarations: [RulesListUiComponent]
|
declarations: [RuleListUiComponent]
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RulesListUiComponent);
|
fixture = TestBed.createComponent(RuleListUiComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
debugElement = fixture.debugElement;
|
debugElement = fixture.debugElement;
|
||||||
});
|
});
|
||||||
@ -54,17 +54,17 @@ describe('RulesListComponent', () => {
|
|||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const rules = debugElement.queryAll(By.css('.aca-rule'));
|
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||||
|
|
||||||
expect(rules).toBeTruthy('Could not find rules');
|
expect(rules).toBeTruthy('Could not find rules');
|
||||||
expect(rules.length).toBe(2, 'Unexpected number of rules');
|
expect(rules.length).toBe(2, 'Unexpected number of rules');
|
||||||
|
|
||||||
const rule = debugElement.query(By.css('.aca-rule:first-child'));
|
const rule = debugElement.query(By.css('.aca-rule-list-item:first-child'));
|
||||||
const title = rule.query(By.css('.rule-info__header__title'));
|
const name = rule.query(By.css('.aca-rule-list-item__header__name'));
|
||||||
const description = rule.query(By.css('p'));
|
const description = rule.query(By.css('.aca-rule-list-item__description'));
|
||||||
const toggleBtn = rule.query(By.css('mat-slide-toggle'));
|
const toggleBtn = rule.query(By.css('mat-slide-toggle'));
|
||||||
|
|
||||||
expect(title.nativeElement.textContent).toBe(dummyRules[0].name);
|
expect(name.nativeElement.textContent).toBe(dummyRules[0].name);
|
||||||
expect(toggleBtn).toBeTruthy();
|
expect(toggleBtn).toBeTruthy();
|
||||||
expect(description.nativeElement.textContent).toBe(dummyRules[0].description);
|
expect(description.nativeElement.textContent).toBe(dummyRules[0].description);
|
||||||
});
|
});
|
@ -23,21 +23,21 @@
|
|||||||
* 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, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rules-list',
|
selector: 'aca-rule-list',
|
||||||
templateUrl: 'rules-list.ui-component.html',
|
templateUrl: 'rule-list.ui-component.html',
|
||||||
styleUrls: ['rules-list.ui-component.scss']
|
styleUrls: ['rule-list.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-list' }
|
||||||
})
|
})
|
||||||
export class RulesListUiComponent {
|
export class RuleListUiComponent {
|
||||||
@Input()
|
@Input()
|
||||||
rules: Rule[];
|
rules: Rule[];
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
selectedRule: Rule;
|
selectedRule: Rule;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
|
|
@ -1,9 +1,5 @@
|
|||||||
<div class="aca-rule" [class.selected]="isSelected" >
|
<div class="aca-rule-list-item__header">
|
||||||
<div class="rule-info">
|
<span class="aca-rule-list-item__header__name">{{ rule.name }}</span>
|
||||||
<div class="rule-info__header">
|
|
||||||
<span class="rule-info__header__title">{{rule.name}}</span>
|
|
||||||
<mat-slide-toggle [(ngModel)]="rule.isEnabled" (click)="onToggleClick(!rule.isEnabled)"></mat-slide-toggle>
|
<mat-slide-toggle [(ngModel)]="rule.isEnabled" (click)="onToggleClick(!rule.isEnabled)"></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
<p>{{rule.description}}</p>
|
<div class="aca-rule-list-item__description">{{ rule.description }}</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
.aca-rule{
|
.aca-rule-list-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 16px 24px;
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 12px 20px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
p{
|
p {
|
||||||
margin: 6px 0 0 0;
|
margin: 6px 0 0 0;
|
||||||
color: rgba(33, 35, 40, 0.7);
|
color: rgba(33, 35, 40, 0.7);
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@ -13,24 +15,24 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.rule-info{
|
&__header {
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&__header{
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
&__title{
|
&__name {
|
||||||
font-weight: 900;
|
font-size: 1.2em;
|
||||||
font-size: 14px;
|
font-weight: bold;
|
||||||
color: #212121;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.selected{
|
&__description {
|
||||||
background: rgba(31, 116, 219, 0.24);
|
font-size: 0.8em;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: var(--theme-selected-button-bg-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,19 +23,25 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, HostBinding, Input, ViewEncapsulation } from '@angular/core';
|
||||||
import { Rule } from '../../model/rule.model';
|
import { Rule } from '../../model/rule.model';
|
||||||
import { FolderRulesService } from '../../services/folder-rules.service';
|
import { FolderRulesService } from '../../services/folder-rules.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rule',
|
selector: 'aca-rule-list-item',
|
||||||
templateUrl: 'rule-list-item.ui-component.html',
|
templateUrl: 'rule-list-item.ui-component.html',
|
||||||
styleUrls: ['rule-list-item.ui-component.scss']
|
styleUrls: ['rule-list-item.ui-component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: { class: 'aca-rule-list-item' }
|
||||||
})
|
})
|
||||||
export class RuleListItemUiComponent {
|
export class RuleListItemUiComponent {
|
||||||
@Input() rule: Rule;
|
@Input()
|
||||||
@Input() isSelected: boolean;
|
rule: Rule;
|
||||||
@Input() nodeId: string;
|
@Input()
|
||||||
|
nodeId: string;
|
||||||
|
@Input()
|
||||||
|
@HostBinding('class.selected')
|
||||||
|
isSelected: boolean;
|
||||||
|
|
||||||
constructor(private folderRulesService: FolderRulesService) {}
|
constructor(private folderRulesService: FolderRulesService) {}
|
||||||
|
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
<div class="aca-rules-list" >
|
|
||||||
<aca-rule *ngFor="let rule of rules" [rule]="rule" (click)="onRuleClicked(rule)" [isSelected]="isSelected(rule)"
|
|
||||||
[nodeId]="nodeId"></aca-rule>
|
|
||||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||||||
.aca-rules-list {
|
|
||||||
margin-right: 24px;
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
import { BehaviorSubject, forkJoin, from, Observable, of } from 'rxjs';
|
import { BehaviorSubject, forkJoin, from, Observable, of } from 'rxjs';
|
||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule, RuleForForm, RuleOptions } from '../model/rule.model';
|
||||||
import { ContentApiService } from '@alfresco/aca-shared';
|
import { ContentApiService } from '@alfresco/aca-shared';
|
||||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||||
import { RuleCompositeCondition } from '../model/rule-composite-condition.model';
|
import { RuleCompositeCondition } from '../model/rule-composite-condition.model';
|
||||||
@ -46,22 +46,39 @@ export class FolderRulesService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static get emptyRuleOptions(): RuleOptions {
|
||||||
|
return {
|
||||||
|
isEnabled: true,
|
||||||
|
isInheritable: false,
|
||||||
|
isAsynchronous: false,
|
||||||
|
errorScript: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static get emptyRule(): Rule {
|
public static get emptyRule(): Rule {
|
||||||
return {
|
return {
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
isEnabled: true,
|
|
||||||
isInheritable: false,
|
|
||||||
isAsynchronous: false,
|
|
||||||
errorScript: '',
|
|
||||||
isShared: false,
|
isShared: false,
|
||||||
triggers: ['inbound'],
|
triggers: ['inbound'],
|
||||||
conditions: FolderRulesService.emptyCompositeCondition,
|
conditions: FolderRulesService.emptyCompositeCondition,
|
||||||
actions: []
|
actions: [],
|
||||||
|
...FolderRulesService.emptyRuleOptions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static get emptyRuleForForm(): RuleForForm {
|
||||||
|
const value = {
|
||||||
|
...FolderRulesService.emptyRule,
|
||||||
|
options: FolderRulesService.emptyRuleOptions
|
||||||
|
};
|
||||||
|
Object.keys(value.options).forEach((key: string) => {
|
||||||
|
delete value[key];
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private rulesListingSource = new BehaviorSubject<Rule[]>([]);
|
private rulesListingSource = new BehaviorSubject<Rule[]>([]);
|
||||||
rulesListing$: Observable<Rule[]> = this.rulesListingSource.asObservable();
|
rulesListing$: Observable<Rule[]> = this.rulesListingSource.asObservable();
|
||||||
private folderInfoSource = new BehaviorSubject<NodeInfo>(null);
|
private folderInfoSource = new BehaviorSubject<NodeInfo>(null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user