[ACS-3744] Folder rules styling fixes (#2753)

* Folder rules styling fixes

* Fix not showing isInheritable in read only
This commit is contained in:
Thomas Hunter 2022-11-02 12:51:40 +00:00 committed by GitHub
parent eee6feca1a
commit e354ec3891
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 468 additions and 267 deletions

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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;
} }
} }
} }

View File

@ -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'));

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}
} }
.select-action { &.read-only .mat-checkbox-inner-container {
margin-left: 5px; display: none;
margin-top: 12px;
span {
display: block;
} }
} }

View File

@ -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();
}); });
}); });

View File

@ -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();
}
} }
} }

View File

@ -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>

View File

@ -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;
}
}
} }
} }

View File

@ -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();
}); });
}); });

View File

@ -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);
}); });
} }

View File

@ -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>

View File

@ -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;
} }

View File

@ -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>

View File

@ -0,0 +1,5 @@
.aca-rule-list {
display: flex;
flex-direction: column;
gap: 4px;
}

View File

@ -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);
}); });

View File

@ -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;

View File

@ -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>

View File

@ -1,6 +1,8 @@
.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;
@ -13,24 +15,24 @@
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;
} }
}
.rule-info{
width: 100%;
&__header { &__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);
}
} }

View File

@ -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) {}

View File

@ -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>

View File

@ -1,3 +0,0 @@
.aca-rules-list {
margin-right: 24px;
}

View File

@ -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);