diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts index add51dee81..8221e0a4ff 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form-field.model.ts @@ -112,6 +112,10 @@ export class FormFieldModel extends FormWidgetModel { return this._isValid; } + markAsInvalid() { + this._isValid = false; + } + validate(): boolean { this.validationSummary = null; diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts b/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts index 4bf98f656a..12b6435e18 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/core/form.model.ts @@ -166,6 +166,10 @@ export class FormModel { return result; } + markAsInvalid() { + this._isValid = false; + } + /** * Validates entire form and all form fields. * diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.html b/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.html index 310e2b4321..2aa228c4cf 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.html +++ b/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.html @@ -17,8 +17,7 @@ placeholder="{{field.placeholder}}" [mdAutocomplete]="auto"> - +
{{getInitialUserName(user.firstName, user.lastName)}} diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.spec.ts b/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.spec.ts index 9947efd5b4..ee6c9f2797 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.spec.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.spec.ts @@ -15,12 +15,15 @@ * limitations under the License. */ +import { OverlayContainer } from '@angular/cdk/overlay'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { CoreModule, LightUserRepresentation } from 'ng2-alfresco-core'; import { Observable } from 'rxjs/Rx'; import { ActivitiAlfrescoContentService } from '../../../services/activiti-alfresco.service'; import { FormService } from '../../../services/form.service'; import { MaterialModule } from '../../material.module'; +import { FormFieldTypes } from '../core/form-field-types'; import { FormFieldModel } from '../core/form-field.model'; import { FormModel } from '../core/form.model'; import { ErrorWidgetComponent } from '../error/error.component'; @@ -33,6 +36,7 @@ describe('PeopleWidgetComponent', () => { let fixture: ComponentFixture; let element: HTMLElement; let formService: FormService; + let overlayContainerElement: HTMLElement; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -47,7 +51,19 @@ describe('PeopleWidgetComponent', () => { providers: [ FormService, EcmModelService, - ActivitiAlfrescoContentService + ActivitiAlfrescoContentService, + {provide: OverlayContainer, useFactory: () => { + overlayContainerElement = document.createElement('div'); + overlayContainerElement.classList.add('cdk-overlay-container'); + + document.body.appendChild(overlayContainerElement); + + // remove body padding to keep consistent cross-browser + document.body.style.padding = '0'; + document.body.style.margin = '0'; + + return {getContainerElement: () => overlayContainerElement}; + }} ] }).compileComponents(); })); @@ -101,20 +117,6 @@ describe('PeopleWidgetComponent', () => { expect(widget.value).toBe('John Doe'); }); - it('should prevent default behaviour on option item click', () => { - let event = jasmine.createSpyObj('event', ['preventDefault']); - widget.onItemClick(null, event); - expect(event.preventDefault).toHaveBeenCalled(); - }); - - it('should update values on item click', () => { - let item = new LightUserRepresentation({firstName: 'John', lastName: 'Doe'}); - - widget.onItemClick(item, null); - expect(widget.field.value).toBe(item); - expect(widget.value).toBe('John Doe'); - }); - it('should require form field to setup values on init', () => { widget.field = null; widget.ngOnInit(); @@ -204,42 +206,76 @@ describe('PeopleWidgetComponent', () => { expect(formService.getWorkflowUsers).not.toHaveBeenCalled(); }); - it('should update form on value flush', () => { - spyOn(widget.field, 'updateForm').and.callThrough(); - widget.flushValue(); - expect(widget.field.updateForm).toHaveBeenCalled(); + it('should reset users when the input field is blank string', () => { + let fakeUser = new LightUserRepresentation({id: '1', email: 'ffff@fff'}); + widget.users.push(fakeUser); + + let keyboardEvent = new KeyboardEvent('keypress'); + widget.value = ''; + widget.onKeyUp(keyboardEvent); + + expect(widget.users).toEqual([]); }); - it('should flush value and update field', () => { - widget.users = [ - new LightUserRepresentation({firstName: 'Tony', lastName: 'Stark'}), - new LightUserRepresentation({firstName: 'John', lastName: 'Doe'}) - ]; - widget.value = 'John Doe'; - widget.flushValue(); + describe('when template is ready', () => { + + let fakeUserResult = [ + { id: 1001, firstName: 'Test01', lastName: 'Test01', email: 'test' }, + { id: 1002, firstName: 'Test02', lastName: 'Test02', email: 'test2' }]; + + beforeEach(async(() => { + spyOn(formService, 'getWorkflowUsers').and.returnValue(Observable.create(observer => { + observer.next(fakeUserResult); + observer.complete(); + })); + widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), { + id: 'people-id', + name: 'people-name', + type: FormFieldTypes.PEOPLE, + readOnly: false + }); + fixture.detectChanges(); + element = fixture.nativeElement; + })); + + afterEach(() => { + fixture.destroy(); + TestBed.resetTestingModule(); + }); + + it('should render the people component', () => { + expect(element.querySelector('#people-widget-content')).not.toBeNull(); + }); + + it('should show an error message if the user is invalid', async(() => { + let peopleHTMLElement: HTMLInputElement = element.querySelector('#people-id'); + peopleHTMLElement.focus(); + widget.value = 'K'; + peopleHTMLElement.value = 'K'; + peopleHTMLElement.dispatchEvent(new Event('keyup')); + peopleHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(element.querySelector('.adf-error-text')).not.toBeNull(); + expect(element.querySelector('.adf-error-text').textContent).toContain('Invalid value provided'); + }); + })); + + it('should show the people if the typed result match', async(() => { + let peopleHTMLElement: HTMLInputElement = element.querySelector('#people-id'); + peopleHTMLElement.focus(); + widget.value = 'T'; + peopleHTMLElement.value = 'T'; + peopleHTMLElement.dispatchEvent(new Event('keyup')); + peopleHTMLElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('#adf-people-widget-user-0'))).not.toBeNull(); + expect(fixture.debugElement.query(By.css('#adf-people-widget-user-1'))).not.toBeNull(); + }); + })); - expect(widget.value).toBe('John Doe'); - expect(widget.field.value).toBe(widget.users[1]); }); - it('should be case insensitive when flushing field', () => { - widget.users = [ - new LightUserRepresentation({firstName: 'Tony', lastName: 'Stark'}), - new LightUserRepresentation({firstName: 'John', lastName: 'Doe'}) - ]; - widget.value = 'TONY sTaRk'; - widget.flushValue(); - - expect(widget.value).toBe('Tony Stark'); - expect(widget.field.value).toBe(widget.users[0]); - }); - - it('should reset value and field on flush', () => { - widget.value = 'Missing User'; - widget.field.value = {}; - widget.flushValue(); - - expect(widget.value).toBeNull(); - expect(widget.field.value).toBeNull(); - }); }); diff --git a/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.ts b/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.ts index a20f67300b..c46eb505af 100644 --- a/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.ts +++ b/ng2-components/ng2-activiti-form/src/components/widgets/people/people.widget.ts @@ -71,30 +71,41 @@ export class PeopleWidgetComponent extends WidgetComponent implements OnInit { } } } + if (this.isValueDefined() && this.value.trim().length === 0) { + this.oldValue = this.value; + this.field.validationSummary = ''; + this.users = []; + } + } + + isValueDefined() { + return this.value !== null && this.value !== undefined; } searchUsers() { this.formService.getWorkflowUsers(this.value, this.groupId) .subscribe((result: LightUserRepresentation[]) => { this.users = result || []; + this.validateValue(); }); } - flushValue() { - let option = this.users.find(item => { - let fullName = this.getDisplayName(item).toLocaleLowerCase(); - return (this.value && fullName === this.value.toLocaleLowerCase()); - }); - - if (option) { - this.field.value = option; - this.value = this.getDisplayName(option); + validateValue() { + let validUserName = this.getUserFromValue(); + if (validUserName) { + this.field.validationSummary = ''; + this.field.value = validUserName; + this.value = this.getDisplayName(validUserName); } else { - this.field.value = null; - this.value = null; - } + this.field.value = ''; + this.field.validationSummary = 'Invalid value provided'; + this.field.markAsInvalid(); + this.field.form.markAsInvalid(); + } + } - this.field.updateForm(); + getUserFromValue() { + return this.users.find((user) => this.getDisplayName(user).toLocaleLowerCase() === this.value.toLocaleLowerCase()); } getDisplayName(model: LightUserRepresentation) { @@ -102,20 +113,9 @@ export class PeopleWidgetComponent extends WidgetComponent implements OnInit { let displayName = `${model.firstName || ''} ${model.lastName || ''}`; return displayName.trim(); } - return ''; } - onItemClick(item: LightUserRepresentation, event: Event) { - if (item) { - this.field.value = item; - this.value = this.getDisplayName(item); - } - if (event) { - event.preventDefault(); - } - } - onItemSelect(item: LightUserRepresentation) { if (item) { this.field.value = item; diff --git a/ng2-components/ng2-activiti-form/src/services/form.service.ts b/ng2-components/ng2-activiti-form/src/services/form.service.ts index ff19922cab..aa83da6e06 100644 --- a/ng2-components/ng2-activiti-form/src/services/form.service.ts +++ b/ng2-components/ng2-activiti-form/src/services/form.service.ts @@ -387,6 +387,7 @@ export class FormService { return Observable.of(user); }) .combineAll() + .defaultIfEmpty([]) .catch(err => this.handleError(err)); }