[ADF-2255] added refresh visibility before validation check (#2965)

* [ADF-2225] added check visibility before validation and refactored the visibility template check

* [ADF-2255] added refresh visibility before validation check

* [ADF-2225] readded deprecation signatures
This commit is contained in:
Vito
2018-02-20 15:45:23 +00:00
committed by Eugenio Romano
parent 1ae5558126
commit 36836d0119
32 changed files with 184 additions and 353 deletions

View File

@@ -20,11 +20,13 @@ import { MaterialModule } from '../../../material.module';
import { ErrorWidgetComponent } from '../widgets/error/error.component';
import { FormRenderingService } from './../../services/form-rendering.service';
import { WidgetVisibilityService } from './../../services/widget-visibility.service';
import { CheckboxWidgetComponent } from './../widgets/checkbox/checkbox.widget';
import { FormFieldModel, FormFieldTypes, FormModel } from './../widgets/core/index';
import { InputMaskDirective } from './../widgets/text/text-mask.component';
import { TextWidgetComponent } from './../widgets/text/text.widget';
import { TextWidgetComponent, CheckboxWidgetComponent, WidgetComponent } from '../widgets/index';
import { FormFieldComponent } from './form-field.component';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { FormService } from '../../services/form.service';
import { EcmModelService } from '../../services/ecm-model.service';
describe('FormFieldComponent', () => {
@@ -35,22 +37,33 @@ describe('FormFieldComponent', () => {
let formRenderingService: FormRenderingService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MaterialModule
],
declarations: [
FormFieldComponent,
TextWidgetComponent,
CheckboxWidgetComponent,
InputMaskDirective,
ErrorWidgetComponent],
providers: [
FormRenderingService,
WidgetVisibilityService
]
})
.compileComponents();
imports: [
MaterialModule
],
declarations: [
FormFieldComponent,
WidgetComponent,
TextWidgetComponent,
CheckboxWidgetComponent,
InputMaskDirective,
ErrorWidgetComponent],
providers: [
FormRenderingService,
WidgetVisibilityService,
FormService,
EcmModelService
]
});
TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {
entryComponents: [WidgetComponent, TextWidgetComponent, CheckboxWidgetComponent]
}
});
TestBed.compileComponents();
}));
beforeEach(() => {
@@ -60,9 +73,14 @@ describe('FormFieldComponent', () => {
form = new FormModel();
});
xit('should create default component instance', () => {
afterEach(() => {
fixture.destroy();
});
it('should create default component instance', () => {
let field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT
type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET'
});
component.field = field;
@@ -72,9 +90,10 @@ describe('FormFieldComponent', () => {
expect(component.componentRef.componentType).toBe(TextWidgetComponent);
});
xit('should create custom component instance', () => {
it('should create custom component instance', () => {
let field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT
type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET'
});
formRenderingService.setComponentTypeResolver(FormFieldTypes.TEXT, () => CheckboxWidgetComponent, true);
@@ -87,7 +106,8 @@ describe('FormFieldComponent', () => {
it('should require component type to be resolved', () => {
let field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT
type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET'
});
spyOn(formRenderingService, 'resolveComponentType').and.returnValue(null);
@@ -98,4 +118,41 @@ describe('FormFieldComponent', () => {
expect(component.componentRef).toBeUndefined();
});
it('should hide the field when it is not visible', () => {
let field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET'
});
component.field = field;
component.field.isVisible = false;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').hidden).toBeTruthy();
});
it('should show the field when it is visible', () => {
let field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET'
});
component.field = field;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').hidden).toBeFalsy();
});
it('should hide a visible element', () => {
let field = new FormFieldModel(form, {
type: FormFieldTypes.TEXT,
id: 'FAKE-TXT-WIDGET'
});
component.field = field;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').hidden).toBeFalsy();
component.field.isVisible = false;
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('#field-FAKE-TXT-WIDGET-container').hidden).toBeTruthy();
});
});

View File

@@ -40,7 +40,8 @@ declare var adf: any;
@Component({
selector: 'adf-form-field, form-field',
template: `
<div [hidden]="!field?.isVisible"
<div [id]="'field-'+field?.id+'-container'"
[hidden]="!field?.isVisible"
[class.adf-focus]="focus"
(focusin)="focusToggle()"
(focusout)="focusToggle()">
@@ -96,7 +97,8 @@ export class FormFieldComponent implements OnInit, OnDestroy {
instance.field = this.field;
instance.fieldChanged.subscribe(field => {
if (field && this.field.form) {
this.visibilityService.refreshVisibility(this.field.form);
this.visibilityService.refreshVisibility(field.form);
field.form.onFormFieldChanged(field);
}
});
}

View File

@@ -19,7 +19,7 @@
</mat-card-header>
<mat-card-content>
<div *ngIf="form.hasTabs()">
<tabs-widget [tabs]="form.tabs" (formTabChanged)="checkVisibility($event);"></tabs-widget>
<tabs-widget [tabs]="form.tabs" (formTabChanged)="onFieldChanged($event);"></tabs-widget>
</div>
<div *ngIf="!form.hasTabs() && form.hasFields()">

View File

@@ -62,6 +62,10 @@ describe('FormComponent UI and visibiltiy', () => {
service = fixture.debugElement.injector.get(FormService);
});
afterEach(() => {
fixture.destroy();
});
it('should create instance of FormComponent', () => {
expect(fixture.componentInstance instanceof FormComponent).toBe(true, 'should create FormComponent');
});
@@ -124,12 +128,13 @@ describe('FormComponent UI and visibiltiy', () => {
component.ngOnChanges({ 'taskId': change });
fixture.detectChanges();
let firstEl = fixture.debugElement.query(By.css('#country'));
expect(firstEl).toBeNull();
let firstEl = fixture.debugElement.query(By.css('#field-country-container'));
expect(firstEl.nativeElement.hidden).toBeTruthy();
let secondEl = fixture.debugElement.query(By.css('#name'));
expect(secondEl).not.toBeNull();
expect(secondEl).toBeDefined();
expect(fixture.nativeElement.querySelector('#field-name-container').hidden).toBeFalsy();
});
it('should hide the field based on the previous one', () => {
@@ -143,9 +148,10 @@ describe('FormComponent UI and visibiltiy', () => {
let firstEl = fixture.debugElement.query(By.css('#name'));
expect(firstEl).not.toBeNull();
expect(firstEl).toBeDefined();
expect(fixture.nativeElement.querySelector('#field-name-container').hidden).toBeFalsy();
let secondEl = fixture.debugElement.query(By.css('#country'));
expect(secondEl).toBeNull();
let secondEl = fixture.debugElement.query(By.css('#field-country-container'));
expect(secondEl.nativeElement.hidden).toBeTruthy();
});
it('should show the hidden field when the visibility condition change to true', () => {
@@ -156,20 +162,19 @@ describe('FormComponent UI and visibiltiy', () => {
component.ngOnChanges({ 'taskId': change });
fixture.detectChanges();
let firstEl = fixture.debugElement.query(By.css('#country'));
expect(firstEl).toBeNull();
let firstEl = fixture.debugElement.query(By.css('#field-country-container'));
expect(firstEl.nativeElement.hidden).toBeTruthy();
const secondEl = fixture.debugElement.query(By.css('#name'));
expect(secondEl).not.toBeNull();
const secondEl = fixture.debugElement.query(By.css('#field-name-container'));
expect(secondEl.nativeElement.hidden).toBeFalsy();
let el = secondEl.nativeElement;
el.value = 'italy';
el.dispatchEvent(new Event('input'));
let inputElement = fixture.nativeElement.querySelector('#name');
inputElement.value = 'italy';
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
firstEl = fixture.debugElement.query(By.css('#country'));
expect(firstEl).not.toBeNull();
firstEl = fixture.debugElement.query(By.css('#field-country-container'));
expect(firstEl.nativeElement.hidden).toBeFalsy();
});
});

View File

@@ -9,7 +9,7 @@
[required]="isRequired()"
[value]="field.value"
[(ngModel)]="field.value"
(ngModelChange)="checkVisibility(field)"
(ngModelChange)="onFieldChanged(field)"
[disabled]="field.readOnly"
placeholder="{{field.placeholder}}">
</mat-form-field>

View File

@@ -5,7 +5,7 @@
[required]="field.required"
[disabled]="field.readOnly || readOnly"
[(ngModel)]="field.value"
(change)="onChange()">
(ngModelChange)="onFieldChanged(field)">
{{field.name}}
<span *ngIf="field.required">*</span>
</mat-checkbox>

View File

@@ -18,7 +18,6 @@
/* tslint:disable:component-selector no-input-rename */
import { Component, ViewEncapsulation } from '@angular/core';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { FormService } from './../../../services/form.service';
import { baseHost , WidgetComponent } from './../widget.component';
@@ -30,12 +29,8 @@ import { baseHost , WidgetComponent } from './../widget.component';
})
export class CheckboxWidgetComponent extends WidgetComponent {
constructor(private visibilityService: WidgetVisibilityService, public formService: FormService) {
constructor(public formService: FormService) {
super(formService);
}
onChange() {
this.visibilityService.refreshVisibility(this.field.form);
}
}

View File

@@ -1,17 +1,17 @@
<div [hidden]="!(content?.isGroup() && content?.isVisible)" class="container-widget__header">
<div [hidden]="!content?.isGroup()" class="container-widget__header">
<h4 class="container-widget__header-text" id="container-header"
[class.collapsible]="content?.isCollapsible()">
<button *ngIf="content?.isCollapsible()"
mat-icon-button
class="mdl-button--icon"
(click)="onExpanderClicked()">
<mat-icon>{{ content?.isExpanded ? 'expand_less' : 'expand_more' }}</mat-icon>
<mat-icon>{{ content?.isExpanded ? 'expand_more' : 'expand_less' }}</mat-icon>
</button>
<span (click)="onExpanderClicked()" id="container-header-label">{{content.name}}</span>
</h4>
</div>
<section class="grid-list" [ngClass]="{'hidden':!(content?.isVisible && content?.isExpanded)}">
<section class="grid-list" *ngIf="content?.isExpanded">
<div class="grid-list-item" *ngFor="let field of fields" [style.width]="getColumnWith(field)">
<form-field *ngIf="field" [field]="field"></form-field>
</div>

View File

@@ -17,7 +17,7 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivitiContentService } from '../../../services/activiti-alfresco.service';
import { fakeFormJson } from '../../../../mock';
import { MaterialModule } from '../../../../material.module';
import { WIDGET_DIRECTIVES } from '../index';
import { MASK_DIRECTIVE } from '../index';
@@ -36,7 +36,6 @@ describe('ContainerWidgetComponent', () => {
let widget: ContainerWidgetComponent;
let fixture: ComponentFixture<ContainerWidgetComponent>;
let element: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -54,11 +53,13 @@ describe('ContainerWidgetComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(ContainerWidgetComponent);
element = fixture.nativeElement;
widget = fixture.componentInstance;
});
afterEach(() => {
fixture.destroy();
});
it('should wrap field with model instance', () => {
let field = new FormFieldModel(null);
widget.field = field;
@@ -125,83 +126,6 @@ describe('ContainerWidgetComponent', () => {
widget.onFieldChanged(fakeField);
});
describe('when template is ready', () => {
let fakeContainerVisible;
let fakeContainerInvisible;
beforeEach(() => {
fakeContainerVisible = new ContainerWidgetComponentModel(new FormFieldModel(new FormModel(fakeFormJson), {
fieldType: FormFieldTypes.GROUP,
id: 'fake-cont-id-1',
name: 'fake-cont-1-name',
type: FormFieldTypes.GROUP
}));
fakeContainerInvisible = new ContainerWidgetComponentModel(new FormFieldModel(new FormModel(fakeFormJson), {
fieldType: FormFieldTypes.GROUP,
id: 'fake-cont-id-2',
name: 'fake-cont-2-name',
type: FormFieldTypes.GROUP
}));
fakeContainerVisible.field.isVisible = true;
fakeContainerInvisible.field.isVisible = false;
});
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
it('should show the container header when it is visible', () => {
widget.content = fakeContainerVisible;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('.container-widget__header').classList.contains('hidden')).toBe(false);
expect(element.querySelector('#container-header-label')).toBeDefined();
expect(element.querySelector('#container-header-label').innerHTML).toContain('fake-cont-1-name');
});
});
it('should not show the container header when it is not visible', () => {
widget.content = fakeContainerInvisible;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('.container-widget__header').getAttribute('hidden')).not.toBeNull();
});
});
it('should hide header when it becomes not visible', async(() => {
widget.content = fakeContainerVisible;
fixture.detectChanges();
widget.fieldChanged.subscribe((res) => {
widget.content.field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('.container-widget__header').getAttribute('hidden')).not.toBeNull();
});
});
widget.onFieldChanged(null);
}));
it('should show header when it becomes visible', async(() => {
widget.content = fakeContainerInvisible;
widget.fieldChanged.subscribe((res) => {
widget.content.field.isVisible = true;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#container-header')).toBeDefined();
expect(element.querySelector('#container-header')).not.toBeNull();
expect(element.querySelector('#container-header-label')).toBeDefined();
expect(element.querySelector('#container-header-label').innerHTML).toContain('fake-cont-2-name');
});
});
widget.onFieldChanged(null);
}));
});
describe('fields', () => {
it('should serializes the content fields', () => {

View File

@@ -85,7 +85,6 @@ export class FormFieldModel extends FormWidgetModel {
set value(v: any) {
this._value = v;
this.validate();
this.updateForm();
}

View File

@@ -1,4 +1,4 @@
<div class="{{field.className}}" *ngIf="field?.isVisible"
<div class="{{field.className}}"
id="data-time-widget" [class.adf-invalid]="!field.isValid || field.validationSummary.message">
<mat-form-field class="adf-date-time-widget">
<label class="adf-label" [attr.for]="field.id">{{field.name}} ({{field.dateDisplayFormat}})<span *ngIf="isRequired()">*</span></label>

View File

@@ -102,7 +102,7 @@ describe('DateTimeWidgetComponent', () => {
});
it('should eval visibility on date changed', () => {
spyOn(widget, 'checkVisibility').and.callThrough();
spyOn(widget, 'onFieldChanged').and.callThrough();
let field = new FormFieldModel(new FormModel(), {
id: 'date-field-id',
@@ -115,7 +115,7 @@ describe('DateTimeWidgetComponent', () => {
widget.field = field;
widget.onDateChanged({ value: moment('13-03-1982 10:00 AM') });
expect(widget.checkVisibility).toHaveBeenCalledWith(field);
expect(widget.onFieldChanged).toHaveBeenCalledWith(field);
});
describe('template check', () => {
@@ -173,6 +173,7 @@ describe('DateTimeWidgetComponent', () => {
fixture.detectChanges();
fixture.whenStable()
.then(() => {
fixture.detectChanges();
expect(element.querySelector('#date-field-id')).toBeDefined();
expect(element.querySelector('#date-field-id')).not.toBeNull();
let dateElement: any = element.querySelector('#date-field-id');
@@ -180,74 +181,6 @@ describe('DateTimeWidgetComponent', () => {
});
}));
it('should hide not visible date widget', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
id: 'date-field-id',
name: 'date-name',
value: '12-30-9999 10:30 AM',
dateDisplayFormat: 'MM-DD-YYYY HH:mm A',
type: 'datetime',
readOnly: 'false'
});
fixture.detectChanges();
expect(element.querySelector('#data-time-widget')).not.toBeNull();
widget.field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
fixture.detectChanges();
expect(element.querySelector('#data-time-widget')).toBeNull();
});
}));
it('should become visibile if the visibility change to true', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
id: 'date-field-id',
name: 'date-name',
value: '12-30-9999 10:30 AM',
dateDisplayFormat: 'MM-DD-YYYY HH:mm A',
type: 'datetime',
readOnly: 'false'
});
widget.field.isVisible = false;
fixture.detectChanges();
expect(element.querySelector('#data-time-widget')).toBeNull();
widget.fieldChanged.subscribe((field) => {
field.isVisible = true;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#data-time-widget')).toBeDefined();
expect(element.querySelector('#data-time-widget')).not.toBeNull();
let dateElement: any = element.querySelector('#date-field-id');
expect(dateElement.value).toContain('12-30-9999 10:30 AM');
});
});
widget.checkVisibility(widget.field);
}));
it('should be hided if the visibility change to false', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
id: 'date-field-id',
name: 'date-name',
value: '12-30-9999 10:30 AM',
dateDisplayFormat: 'MM-DD-YYYY HH:mm A',
type: 'datetime',
readOnly: 'false'
});
fixture.detectChanges();
expect(element.querySelector('#data-time-widget')).not.toBeNull();
widget.fieldChanged.subscribe((field) => {
field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#data-time-widget')).toBeNull();
});
});
widget.checkVisibility(widget.field);
}));
it('should disable date button when is readonly', async(() => {
widget.field = new FormFieldModel(new FormModel(), {
id: 'date-field-id',

View File

@@ -80,7 +80,7 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit {
} else {
this.field.value = null;
}
this.checkVisibility(this.field);
this.onFieldChanged(this.field);
}
}

View File

@@ -1,4 +1,4 @@
<div class="{{field.className}}" *ngIf="field?.isVisible" id="data-widget" [class.adf-invalid]="!field.isValid || field.validationSummary.message">
<div class="{{field.className}}" id="data-widget" [class.adf-invalid]="!field.isValid || field.validationSummary.message">
<mat-form-field class="adf-date-widget">
<label class="adf-label" [attr.for]="field.id">{{field.name}} ({{field.dateDisplayFormat}})<span *ngIf="isRequired()">*</span></label>
<input matInput

View File

@@ -94,7 +94,7 @@ describe('DateWidgetComponent', () => {
});
it('should eval visibility on date changed', () => {
spyOn(widget, 'checkVisibility').and.callThrough();
spyOn(widget, 'onFieldChanged').and.callThrough();
let field = new FormFieldModel(new FormModel(), {
id: 'date-field-id',
@@ -107,7 +107,7 @@ describe('DateWidgetComponent', () => {
widget.field = field;
widget.onDateChanged({ value: moment('12/12/2012') });
expect(widget.checkVisibility).toHaveBeenCalledWith(field);
expect(widget.onFieldChanged).toHaveBeenCalledWith(field);
});
describe('template check', () => {
@@ -169,45 +169,6 @@ describe('DateWidgetComponent', () => {
});
}));
it('should hide not visible date widget', async(() => {
widget.field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
fixture.detectChanges();
expect(element.querySelector('#data-widget')).toBeNull();
});
}));
it('should become visibile if the visibility change to true', async(() => {
widget.field.isVisible = false;
fixture.detectChanges();
widget.fieldChanged.subscribe((field) => {
field.isVisible = true;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#date-field-id')).toBeDefined();
expect(element.querySelector('#date-field-id')).not.toBeNull();
let dateElement: any = element.querySelector('#date-field-id');
expect(dateElement.value).toContain('9-9-9999');
});
});
widget.checkVisibility(widget.field);
}));
it('should be hided if the visibility change to false', async(() => {
widget.fieldChanged.subscribe((field) => {
field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#data-widget')).toBeNull();
});
});
widget.checkVisibility(widget.field);
}));
it('should disable date button when is readonly', async(() => {
widget.field.readOnly = false;
fixture.detectChanges();

View File

@@ -77,7 +77,7 @@ export class DateWidgetComponent extends WidgetComponent implements OnInit {
} else {
this.field.value = null;
}
this.checkVisibility(this.field);
this.onFieldChanged(this.field);
}
}

View File

@@ -1,12 +1,12 @@
<div class="adf-dropdown-widget {{field.className}}"
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly" *ngIf="field?.isVisible">
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly">
<label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label>
<mat-form-field>
<mat-select class="adf-select"
[id]="field.id"
[(ngModel)]="field.value"
[disabled]="field.readOnly"
(ngModelChange)="checkVisibility(field)">
(ngModelChange)="onFieldChanged(field)">
<mat-option *ngFor="let opt of field.options"
[value]="getOptionValue(opt, field.value)"
[id]="opt.id">{{opt.name}}

View File

@@ -188,27 +188,6 @@ describe('DropdownWidgetComponent', () => {
});
}));
it('should be not visible when isVisible is false', async(() => {
widget.field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
let dropDownElement: HTMLSelectElement = <HTMLSelectElement> element.querySelector('#dropdown-id');
expect(dropDownElement).toBeNull();
});
}));
it('should became visible when isVisible is true', async(() => {
widget.field.isVisible = false;
fixture.detectChanges();
expect(element.querySelector('#dropdown-id')).toBeNull();
widget.field.isVisible = true;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#dropdown-id')).not.toBeNull();
});
}));
});
describe('and dropdown is populated via processDefinitionId', () => {

View File

@@ -20,7 +20,6 @@
import { LogService } from '../../../../services/log.service';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FormService } from '../../../services/form.service';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { FormFieldOption } from './../core/form-field-option';
import { baseHost , WidgetComponent } from './../widget.component';
@@ -34,7 +33,6 @@ import { baseHost , WidgetComponent } from './../widget.component';
export class DropdownWidgetComponent extends WidgetComponent implements OnInit {
constructor(public formService: FormService,
private visibilityService: WidgetVisibilityService,
private logService: LogService) {
super(formService);
}
@@ -97,10 +95,6 @@ export class DropdownWidgetComponent extends WidgetComponent implements OnInit {
return optionValue;
}
checkVisibility() {
this.visibilityService.refreshVisibility(this.field.form);
}
handleError(error: any) {
this.logService.error(error);
}

View File

@@ -1,5 +1,5 @@
<div class="{{field.className}}"
[class.adf-invalid]="!isValid()" *ngIf="field?.isVisible">
[class.adf-invalid]="!isValid()">
<div class="adf-label">{{content.name}}<span *ngIf="isRequired()">*</span></div>
<div *ngIf="!editMode">

View File

@@ -118,7 +118,11 @@ describe('DynamicTableWidgetComponent', () => {
element = fixture.nativeElement;
widget = fixture.componentInstance;
widget.content = table;
widget.field = field;
});
afterEach(() => {
fixture.destroy();
});
it('should select row on click', () => {

View File

@@ -1,6 +1,6 @@
<div class="adf-group-widget {{field.className}}"
[class.is-dirty]="value"
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly" id="functional-group-div" *ngIf="field.isVisible">
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly" id="functional-group-div">
<mat-form-field>
<label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label>
<input matInput

View File

@@ -9,7 +9,7 @@
[id]="field.id"
[required]="isRequired()"
[(ngModel)]="field.value"
(ngModelChange)="checkVisibility(field)"
(ngModelChange)="onFieldChanged(field)"
[disabled]="field.readOnly || readOnly"
placeholder="{{field.placeholder}}">
</textarea>

View File

@@ -10,7 +10,7 @@
[required]="isRequired()"
[value]="field.value"
[(ngModel)]="field.value"
(ngModelChange)="checkVisibility(field)"
(ngModelChange)="onFieldChanged(field)"
[disabled]="field.readOnly"
placeholder="{{field.placeholder}}">
</mat-form-field>

View File

@@ -2,8 +2,7 @@
[class.is-dirty]="value"
[class.adf-invalid]="!field.isValid"
[class.adf-readonly]="field.readOnly"
id="people-widget-content"
*ngIf="field.isVisible">
id="people-widget-content">
<mat-form-field>
<label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label>
<input #inputValue

View File

@@ -1,5 +1,5 @@
<div class="adf-radio-buttons-widget {{field.className}}"
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly" [id]="field.id" *ngIf="field?.isVisible">
[class.adf-invalid]="!field.isValid" [class.adf-readonly]="field.readOnly" [id]="field.id">
<div class="adf-radio-button-container">
<label class="adf-label" [attr.for]="field.id">{{field.name}}<span *ngIf="isRequired()">*</span></label>
<mat-radio-group class="adf-radio-group" [(ngModel)]="field.value">

View File

@@ -20,7 +20,6 @@ import { Observable } from 'rxjs/Observable';
import { EcmModelService } from '../../../services/ecm-model.service';
import { FormService } from '../../../services/form.service';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { MaterialModule } from '../../../../material.module';
import { ContainerModel } from '../core/container.model';
import { FormFieldTypes } from '../core/form-field-types';
@@ -34,12 +33,10 @@ describe('RadioButtonsWidgetComponent', () => {
let formService: FormService;
let widget: RadioButtonsWidgetComponent;
let visibilityService: WidgetVisibilityService;
beforeEach(() => {
formService = new FormService(null, null, null);
visibilityService = new WidgetVisibilityService(null, null);
widget = new RadioButtonsWidgetComponent(formService, visibilityService, null);
widget = new RadioButtonsWidgetComponent(formService, null);
widget.field = new FormFieldModel(new FormModel(), { restUrl: '<url>' });
});
@@ -119,8 +116,8 @@ describe('RadioButtonsWidgetComponent', () => {
expect(formService.getRestFieldValues).toHaveBeenCalled();
});
xit('should update the field value when an option is selected', () => {
spyOn(widget, 'checkVisibility').and.stub();
it('should update the field value when an option is selected', () => {
spyOn(widget, 'onFieldChanged').and.returnValue(Observable.of({}));
widget.onOptionClick('fake-opt');
expect(widget.field.value).toEqual('fake-opt');
@@ -131,7 +128,6 @@ describe('RadioButtonsWidgetComponent', () => {
let fixture: ComponentFixture<RadioButtonsWidgetComponent>;
let element: HTMLElement;
let stubFormService: FormService;
let stubVisibilityService: WidgetVisibilityService;
let restOption: FormFieldOption[] = [{ id: 'opt-1', name: 'opt-name-1' }, {
id: 'opt-2',
name: 'opt-name-2'
@@ -141,7 +137,7 @@ describe('RadioButtonsWidgetComponent', () => {
TestBed.configureTestingModule({
imports: [ MaterialModule ],
declarations: [RadioButtonsWidgetComponent, ErrorWidgetComponent],
providers: [FormService, EcmModelService, WidgetVisibilityService]
providers: [FormService, EcmModelService]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(RadioButtonsWidgetComponent);
radioButtonWidget = fixture.componentInstance;
@@ -158,7 +154,6 @@ describe('RadioButtonsWidgetComponent', () => {
beforeEach(async(() => {
stubFormService = fixture.debugElement.injector.get(FormService);
stubVisibilityService = fixture.debugElement.injector.get(WidgetVisibilityService);
spyOn(stubFormService, 'getRestFieldValues').and.returnValue(Observable.of(restOption));
radioButtonWidget.field = new FormFieldModel(new FormModel({ taskId: 'task-id' }), {
id: 'radio-id',
@@ -172,7 +167,7 @@ describe('RadioButtonsWidgetComponent', () => {
fixture.detectChanges();
}));
it('should show visible radio buttons', async(() => {
it('should show radio buttons', async(() => {
expect(element.querySelector('#radio-id')).toBeDefined();
expect(element.querySelector('#radio-id-opt-1-input')).not.toBeNull();
expect(element.querySelector('#radio-id-opt-1')).not.toBeNull();
@@ -180,29 +175,15 @@ describe('RadioButtonsWidgetComponent', () => {
expect(element.querySelector('#radio-id-opt-2')).not.toBeNull();
}));
it('should not show invisible radio buttons', async(() => {
radioButtonWidget.field.isVisible = false;
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#radio-id')).toBeNull();
expect(element.querySelector('#radio-id-opt-1-input')).toBeNull();
expect(element.querySelector('#radio-id-opt-2-input')).toBeNull();
});
}));
it('should evaluate visibility on option click', async(() => {
spyOn(stubVisibilityService, 'evaluateVisibility').and.returnValue(false);
it('should trigger field changed event on click', async(() => {
let option: HTMLElement = <HTMLElement> element.querySelector('#radio-id-opt-1-input');
expect(element.querySelector('#radio-id')).not.toBeNull();
expect(option).not.toBeNull();
option.click();
fixture.detectChanges();
fixture.whenStable()
.then(() => {
expect(element.querySelector('#radio-id')).toBeNull();
expect(element.querySelector('#radio-id-opt-1-input')).toBeNull();
});
widget.fieldChanged.subscribe(field => {
expect(element.querySelector('#radio-id')).toBeNull();
expect(element.querySelector('#radio-id-opt-1-input')).toBeNull();
});
}));
});

View File

@@ -20,7 +20,6 @@
import { LogService } from '../../../../services/log.service';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FormService } from '../../../services/form.service';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { FormFieldOption } from './../core/form-field-option';
import { baseHost , WidgetComponent } from './../widget.component';
@@ -34,7 +33,6 @@ import { baseHost , WidgetComponent } from './../widget.component';
export class RadioButtonsWidgetComponent extends WidgetComponent implements OnInit {
constructor(public formService: FormService,
private visibilityService: WidgetVisibilityService,
private logService: LogService) {
super(formService);
}
@@ -81,11 +79,7 @@ export class RadioButtonsWidgetComponent extends WidgetComponent implements OnIn
onOptionClick(optionSelected: any) {
this.field.value = optionSelected;
this.checkVisibility();
}
checkVisibility() {
this.visibilityService.refreshVisibility(this.field.form);
this.fieldChanged.emit(this.field);
}
handleError(error: any) {

View File

@@ -3,7 +3,7 @@
[class.is-dirty]="value"
[class.adf-invalid]="!field.isValid"
[class.adf-readonly]="field.readOnly"
id="typehead-div" *ngIf="field.isVisible">
id="typehead-div">
<mat-form-field>
<label class="adf-label" [attr.for]="field.id">{{field.name}}</label>
<input matInput class="adf-input"

View File

@@ -21,7 +21,6 @@ import { Observable } from 'rxjs/Observable';
import { By } from '@angular/platform-browser';
import { EcmModelService } from '../../../services/ecm-model.service';
import { FormService } from '../../../services/form.service';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { MaterialModule } from '../../../../material.module';
import { FormFieldOption } from '../core/form-field-option';
import { FormFieldTypes } from '../core/form-field-types';
@@ -34,13 +33,12 @@ describe('TypeaheadWidgetComponent', () => {
let formService: FormService;
let widget: TypeaheadWidgetComponent;
let visibilityService: WidgetVisibilityService;
beforeEach(() => {
formService = new FormService(null, null, null);
visibilityService = new WidgetVisibilityService(null, null);
widget = new TypeaheadWidgetComponent(formService, visibilityService, null);
widget = new TypeaheadWidgetComponent(formService, null);
widget.field = new FormFieldModel(new FormModel({ taskId: 'task-id' }));
widget.field.restUrl = 'whateverURL';
});
it('should request field values from service', () => {
@@ -52,7 +50,8 @@ describe('TypeaheadWidgetComponent', () => {
});
widget.field = new FormFieldModel(form, {
id: fieldId
id: fieldId,
restUrl: 'whateverURL'
});
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => {
@@ -63,6 +62,23 @@ describe('TypeaheadWidgetComponent', () => {
expect(formService.getRestFieldValues).toHaveBeenCalledWith(taskId, fieldId);
});
it('should not perform any request if restUrl is not present', () => {
const taskId = '<form-id>';
const fieldId = '<field-id>';
let form = new FormModel({
taskId: taskId
});
widget.field = new FormFieldModel(form, {
id: fieldId
});
spyOn(formService, 'getRestFieldValues');
widget.ngOnInit();
expect(formService.getRestFieldValues).not.toHaveBeenCalled();
});
it('should handle error when requesting fields with task id', () => {
const taskId = '<form-id>';
const fieldId = '<field-id>';
@@ -72,7 +88,8 @@ describe('TypeaheadWidgetComponent', () => {
});
widget.field = new FormFieldModel(form, {
id: fieldId
id: fieldId,
restUrl: 'whateverURL'
});
const err = 'Error';
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.throw(err));
@@ -93,7 +110,8 @@ describe('TypeaheadWidgetComponent', () => {
});
widget.field = new FormFieldModel(form, {
id: fieldId
id: fieldId,
restUrl: 'whateverURL'
});
const err = 'Error';
spyOn(formService, 'getRestFieldValuesByProcessId').and.returnValue(Observable.throw(err));
@@ -114,6 +132,7 @@ describe('TypeaheadWidgetComponent', () => {
observer.complete();
}));
widget.field.value = '2';
widget.field.restUrl = 'whateverURL';
widget.ngOnInit();
expect(formService.getRestFieldValues).toHaveBeenCalled();
@@ -130,6 +149,7 @@ describe('TypeaheadWidgetComponent', () => {
}));
widget.field.value = '3';
widget.field.restUrl = 'whateverURL';
widget.ngOnInit();
expect(formService.getRestFieldValues).toHaveBeenCalled();
@@ -156,6 +176,7 @@ describe('TypeaheadWidgetComponent', () => {
observer.next([]);
observer.complete();
}));
widget.field.restUrl = 'whateverURL';
spyOn(widget.field, 'updateForm').and.callThrough();
widget.ngOnInit();
@@ -205,7 +226,7 @@ describe('TypeaheadWidgetComponent', () => {
TestBed.configureTestingModule({
imports: [MaterialModule],
declarations: [TypeaheadWidgetComponent, ErrorWidgetComponent],
providers: [ FormService, EcmModelService, WidgetVisibilityService ]
providers: [ FormService, EcmModelService]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(TypeaheadWidgetComponent);
typeaheadWidgetComponent = fixture.componentInstance;
@@ -249,14 +270,13 @@ describe('TypeaheadWidgetComponent', () => {
beforeEach(async(() => {
stubFormService = fixture.debugElement.injector.get(FormService);
visibilityService = fixture.debugElement.injector.get(WidgetVisibilityService);
spyOn(visibilityService, 'refreshVisibility').and.stub();
spyOn(stubFormService, 'getRestFieldValues').and.returnValue(Observable.of(fakeOptionList));
typeaheadWidgetComponent.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'typeahead-id',
name: 'typeahead-name',
type: FormFieldTypes.TYPEAHEAD,
readOnly: false
readOnly: false,
restUrl: 'whateverURL'
});
typeaheadWidgetComponent.field.isVisible = true;
fixture.detectChanges();
@@ -322,22 +342,12 @@ describe('TypeaheadWidgetComponent', () => {
expect(element.querySelector('.adf-error-text').textContent).toContain('FORM.FIELD.VALIDATOR.INVALID_VALUE');
});
}));
it('should hide not visible typeahead', async(() => {
typeaheadWidgetComponent.field.isVisible = false;
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(element.querySelector('#typeahead-id')).toBeNull();
});
}));
});
describe('and typeahead is populated via processDefinitionId', () => {
beforeEach(async(() => {
stubFormService = fixture.debugElement.injector.get(FormService);
visibilityService = fixture.debugElement.injector.get(WidgetVisibilityService);
spyOn(visibilityService, 'refreshVisibility').and.stub();
spyOn(stubFormService, 'getRestFieldValuesByProcessId').and.returnValue(Observable.of(fakeOptionList));
typeaheadWidgetComponent.field = new FormFieldModel(new FormModel({ processDefinitionId: 'fake-process-id' }), {
id: 'typeahead-id',

View File

@@ -20,7 +20,6 @@
import { LogService } from '../../../../services/log.service';
import { ENTER, ESCAPE } from '@angular/cdk/keycodes';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { WidgetVisibilityService } from '../../../services/widget-visibility.service';
import { FormService } from './../../../services/form.service';
import { FormFieldOption } from './../core/form-field-option';
import { baseHost, WidgetComponent } from './../widget.component';
@@ -40,15 +39,14 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit
options: FormFieldOption[] = [];
constructor(public formService: FormService,
private visibilityService: WidgetVisibilityService,
private logService: LogService) {
super(formService);
}
ngOnInit() {
if (this.field.form.taskId) {
if (this.field.form.taskId && this.field.restUrl) {
this.getValuesByTaskId();
} else if (this.field.form.processDefinitionId) {
} else if (this.field.form.processDefinitionId && this.field.restUrl) {
this.getValuesByProcessDefinitionId();
}
if (this.isReadOnlyType()) {
@@ -74,8 +72,8 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit
this.value = toSelect.name;
}
}
this.onFieldChanged(this.field);
this.field.updateForm();
this.visibilityService.refreshEntityVisibility(this.field);
},
err => this.handleError(err)
);
@@ -99,8 +97,8 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit
this.value = toSelect.name;
}
}
this.onFieldChanged(this.field);
this.field.updateForm();
this.visibilityService.refreshEntityVisibility(this.field);
},
err => this.handleError(err)
);
@@ -141,7 +139,7 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit
if (item) {
this.field.value = item.id;
this.value = item.name;
this.checkVisibility();
this.onFieldChanged(this.field);
}
}
@@ -157,10 +155,6 @@ export class TypeaheadWidgetComponent extends WidgetComponent implements OnInit
this.logService.error(error);
}
checkVisibility() {
this.visibilityService.refreshVisibility(this.field.form);
}
isReadOnlyType(): boolean {
return this.field.type === 'readonly' ? true : false;
}

View File

@@ -93,7 +93,7 @@ describe('WidgetComponent', () => {
done();
});
widget.checkVisibility(fakeField);
widget.onFieldChanged(fakeField);
});
it('should eval isRequired state of the field', () => {