mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
Merge pull request #752 from Alfresco/dev-denys-726
#726 Form Validation
This commit is contained in:
@@ -4,8 +4,9 @@
|
||||
</div>
|
||||
<div *ngIf="hasForm()">
|
||||
<div class="mdl-card mdl-shadow--2dp activiti-form-container">
|
||||
<div *ngIf="isTitleEnabled()" class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">{{form.taskName}}</h2>
|
||||
<div class="mdl-card__title">
|
||||
<i class="material-icons">{{ form.isValid ? 'event_available' : 'event_busy' }}</i>
|
||||
<h2 *ngIf="isTitleEnabled()" class="mdl-card__title-text">{{form.taskName}}</h2>
|
||||
</div>
|
||||
<div class="mdl-card__media">
|
||||
<div *ngIf="form.hasTabs()">
|
||||
@@ -19,9 +20,9 @@
|
||||
<div *ngIf="form.hasOutcomes()" class="mdl-card__actions mdl-card--border">
|
||||
<button *ngFor="let outcome of form.outcomes"
|
||||
alfresco-mdl-button
|
||||
[disabled]="readOnly"
|
||||
[disabled]="!isOutcomeButtonEnabled(outcome)"
|
||||
[class.mdl-button--colored]="!outcome.isSystem"
|
||||
[class.activiti-form-hide-button]="!isOutcomeButtonEnabled(outcome)"
|
||||
[class.activiti-form-hide-button]="!isOutcomeButtonVisible(outcome)"
|
||||
(click)="onOutcomeClicked(outcome, $event)">
|
||||
{{outcome.name}}
|
||||
</button>
|
||||
|
@@ -22,7 +22,7 @@ import { ActivitiForm } from './activiti-form.component';
|
||||
import { FormModel, FormOutcomeModel, FormFieldModel, FormOutcomeEvent } from './widgets/index';
|
||||
import { FormService } from './../services/form.service';
|
||||
import { WidgetVisibilityService } from './../services/widget-visibility.service';
|
||||
import { ContainerWidget } from './widgets/container/container.widget';
|
||||
// import { ContainerWidget } from './widgets/container/container.widget';
|
||||
|
||||
describe('ActivitiForm', () => {
|
||||
|
||||
@@ -98,13 +98,13 @@ describe('ActivitiForm', () => {
|
||||
});
|
||||
|
||||
it('should not enable outcome button when model missing', () => {
|
||||
expect(formComponent.isOutcomeButtonEnabled(null)).toBeFalsy();
|
||||
expect(formComponent.isOutcomeButtonVisible(null)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should enable custom outcome buttons', () => {
|
||||
let formModel = new FormModel();
|
||||
let outcome = new FormOutcomeModel(formModel, { id: 'action1', name: 'Action 1' });
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ describe('ActivitiForm', () => {
|
||||
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.SAVE_ACTION });
|
||||
|
||||
formComponent.showSaveButton = true;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy();
|
||||
|
||||
formComponent.showSaveButton = false;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeFalsy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should allow controlling [save] button visibility', () => {
|
||||
@@ -124,10 +124,10 @@ describe('ActivitiForm', () => {
|
||||
let outcome = new FormOutcomeModel(formModel, { id: '$save', name: FormOutcomeModel.COMPLETE_ACTION });
|
||||
|
||||
formComponent.showCompleteButton = true;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeTruthy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeTruthy();
|
||||
|
||||
formComponent.showCompleteButton = false;
|
||||
expect(formComponent.isOutcomeButtonEnabled(outcome)).toBeFalsy();
|
||||
expect(formComponent.isOutcomeButtonVisible(outcome)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should load form on refresh', () => {
|
||||
@@ -571,6 +571,7 @@ describe('ActivitiForm', () => {
|
||||
expect(formComponent.getFormDefinitionOutcomes).toHaveBeenCalledWith(form);
|
||||
});
|
||||
|
||||
/*
|
||||
it('should update the visibility when the container raise the change event', (valueChanged) => {
|
||||
spyOn(formComponent, 'checkVisibility').and.callThrough();
|
||||
let widget = new ContainerWidget();
|
||||
@@ -581,6 +582,7 @@ describe('ActivitiForm', () => {
|
||||
|
||||
expect(formComponent.checkVisibility).toHaveBeenCalledWith(fakeField);
|
||||
});
|
||||
*/
|
||||
|
||||
it('should prevent default outcome execution', () => {
|
||||
|
||||
|
@@ -128,7 +128,7 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
||||
showSaveButton: boolean = true;
|
||||
|
||||
@Input()
|
||||
showDebugButton: boolean = false;
|
||||
showDebugButton: boolean = true;
|
||||
|
||||
@Input()
|
||||
readOnly: boolean = false;
|
||||
@@ -175,6 +175,21 @@ export class ActivitiForm implements OnInit, AfterViewChecked, OnChanges {
|
||||
}
|
||||
|
||||
isOutcomeButtonEnabled(outcome: FormOutcomeModel): boolean {
|
||||
if (this.form.readOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outcome) {
|
||||
// Make 'Save' button always available
|
||||
if (outcome.name === FormOutcomeModel.SAVE_ACTION) {
|
||||
return true;
|
||||
}
|
||||
return this.form.isValid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isOutcomeButtonVisible(outcome: FormOutcomeModel): boolean {
|
||||
if (outcome && outcome.name) {
|
||||
if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) {
|
||||
return this.showCompleteButton;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" [attr.for]="field.id">
|
||||
<input type="checkbox"
|
||||
[attr.id]="field.id"
|
||||
[attr.required]="isRequired()"
|
||||
class="mdl-checkbox__input"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)"
|
||||
|
@@ -17,6 +17,7 @@
|
||||
|
||||
import { it, describe, expect } from '@angular/core/testing';
|
||||
import { ContainerColumnModel } from './container-column.model';
|
||||
import { FormModel } from './form.model';
|
||||
import { FormFieldModel } from './form-field.model';
|
||||
|
||||
describe('ContainerColumnModel', () => {
|
||||
@@ -35,7 +36,7 @@ describe('ContainerColumnModel', () => {
|
||||
column.fields = [];
|
||||
expect(column.hasFields()).toBeFalsy();
|
||||
|
||||
column.fields = [new FormFieldModel(null, null)];
|
||||
column.fields = [new FormFieldModel(new FormModel(), null)];
|
||||
expect(column.hasFields()).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@@ -30,6 +30,7 @@ export class FormFieldTypes {
|
||||
static FUNCTIONAL_GROUP: string = 'functional-group';
|
||||
static PEOPLE: string = 'people';
|
||||
static BOOLEAN: string = 'boolean';
|
||||
static NUMBER: string = 'integer';
|
||||
|
||||
static READONLY_TYPES: string[] = [
|
||||
FormFieldTypes.HYPERLINK,
|
||||
|
@@ -0,0 +1,493 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, describe, expect } from '@angular/core/testing';
|
||||
import { FormModel } from './form.model';
|
||||
import { FormFieldModel } from './form-field.model';
|
||||
import { FormFieldOption } from './form-field-option';
|
||||
import { FormFieldTypes } from './form-field-types';
|
||||
import {
|
||||
RequiredFieldValidator,
|
||||
NumberFieldValidator,
|
||||
MinLengthFieldValidator,
|
||||
MaxLengthFieldValidator,
|
||||
MinValueFieldValidator,
|
||||
MaxValueFieldValidator,
|
||||
RegExFieldValidator
|
||||
} from './form-field-validator';
|
||||
|
||||
describe('FormFieldValidator', () => {
|
||||
|
||||
describe('RequiredFieldValidator', () => {
|
||||
|
||||
let validator: RequiredFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new RequiredFieldValidator();
|
||||
});
|
||||
|
||||
it('should require [required] setting', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: '<value>'
|
||||
});
|
||||
|
||||
field.required = false;
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
|
||||
field.required = true;
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should skip unsupported type', () => {
|
||||
let field = new FormFieldModel(new FormModel(), { type: 'wrong-type' });
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
it('should fail for dropdown with empty value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DROPDOWN,
|
||||
value: '<empty>',
|
||||
hasEmptyValue: true,
|
||||
required: true
|
||||
});
|
||||
|
||||
field.emptyOption = <FormFieldOption> { id: '<empty>' };
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
|
||||
field.value = '<non-empty>';
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail for radio buttons', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.RADIO_BUTTONS,
|
||||
required: true,
|
||||
value: 'one',
|
||||
options: [{ id: 'two', name: 'two' }]
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should succeed for radio buttons', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.RADIO_BUTTONS,
|
||||
required: true,
|
||||
value: 'two',
|
||||
options: [{ id: 'two', name: 'two' }]
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail for upload', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: null,
|
||||
required: true
|
||||
});
|
||||
|
||||
field.value = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
|
||||
field.value = [];
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should succeed for upload', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.UPLOAD,
|
||||
value: [{}],
|
||||
required: true
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail for text', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: null,
|
||||
required: true
|
||||
});
|
||||
|
||||
field.value = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
|
||||
field.value = '';
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should succeed for text', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: '<value>',
|
||||
required: true
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('NumberFieldValidator', () => {
|
||||
|
||||
let validator: NumberFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new NumberFieldValidator();
|
||||
});
|
||||
|
||||
it('should verify number', () => {
|
||||
expect(NumberFieldValidator.isNumber('1')).toBeTruthy();
|
||||
expect(NumberFieldValidator.isNumber('1.0')).toBeTruthy();
|
||||
expect(NumberFieldValidator.isNumber('-1')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not verify number', () => {
|
||||
expect(NumberFieldValidator.isNumber(null)).toBeFalsy();
|
||||
expect(NumberFieldValidator.isNumber(undefined)).toBeFalsy();
|
||||
expect(NumberFieldValidator.isNumber('')).toBeFalsy();
|
||||
expect(NumberFieldValidator.isNumber('one')).toBeFalsy();
|
||||
expect(NumberFieldValidator.isNumber('1q')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should allow empty number value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: null
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail for wrong number value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '<value>'
|
||||
});
|
||||
|
||||
field.validationSummary = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
expect(field.validationSummary).not.toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('MinLengthFieldValidator', () => {
|
||||
|
||||
let validator: MinLengthFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new MinLengthFieldValidator();
|
||||
});
|
||||
|
||||
it('should require minLength defined', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT
|
||||
});
|
||||
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
|
||||
field.minLength = 10;
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should allow empty values', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
minLength: 10,
|
||||
value: null
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed text validation', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
minLength: 3,
|
||||
value: '1234'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail text validation', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
minLength: 3,
|
||||
value: '12'
|
||||
});
|
||||
|
||||
field.validationSummary = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
expect(field.validationSummary).not.toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('MaxLengthFieldValidator', () => {
|
||||
|
||||
let validator: MaxLengthFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new MaxLengthFieldValidator();
|
||||
});
|
||||
|
||||
it('should require maxLength defined', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT
|
||||
});
|
||||
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
|
||||
field.maxLength = 10;
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should allow empty values', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
maxLength: 10,
|
||||
value: null
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed text validation', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
maxLength: 3,
|
||||
value: '123'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail text validation', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
maxLength: 3,
|
||||
value: '1234'
|
||||
});
|
||||
|
||||
field.validationSummary = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
expect(field.validationSummary).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('MinValueFieldValidator', () => {
|
||||
|
||||
let validator: MinValueFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new MinValueFieldValidator();
|
||||
});
|
||||
|
||||
it('should require minValue defined', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER
|
||||
});
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
|
||||
field.minValue = '1';
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support numeric widgets only', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
minValue: '1'
|
||||
});
|
||||
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
|
||||
field.type = FormFieldTypes.TEXT;
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should allow empty values', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: null,
|
||||
minValue: '1'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed for unsupported types', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed validating value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '10',
|
||||
minValue: '10'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail validating value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '9',
|
||||
minValue: '10'
|
||||
});
|
||||
|
||||
field.validationSummary = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
expect(field.validationSummary).not.toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('MaxValueFieldValidator', () => {
|
||||
|
||||
let validator: MaxValueFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new MaxValueFieldValidator();
|
||||
});
|
||||
|
||||
it('should require maxValue defined', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER
|
||||
});
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
|
||||
field.maxValue = '1';
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support numeric widgets only', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
maxValue: '1'
|
||||
});
|
||||
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
|
||||
field.type = FormFieldTypes.TEXT;
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should allow empty values', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: null,
|
||||
maxValue: '1'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed for unsupported types', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed validating value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '10',
|
||||
maxValue: '10'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail validating value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.NUMBER,
|
||||
value: '11',
|
||||
maxValue: '10'
|
||||
});
|
||||
|
||||
field.validationSummary = null;
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
expect(field.validationSummary).not.toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('RegExFieldValidator', () => {
|
||||
|
||||
let validator: RegExFieldValidator;
|
||||
|
||||
beforeEach(() => {
|
||||
validator = new RegExFieldValidator();
|
||||
});
|
||||
|
||||
it('should require regex pattern to be defined', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT
|
||||
});
|
||||
expect(validator.isSupported(field)).toBeFalsy();
|
||||
|
||||
field.regexPattern = '<pattern>';
|
||||
expect(validator.isSupported(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should allow empty values', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: null,
|
||||
regexPattern: 'pattern'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should succeed validating regex', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: 'pattern',
|
||||
regexPattern: 'pattern'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fail validating regex', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.TEXT,
|
||||
value: 'some value',
|
||||
regexPattern: 'pattern'
|
||||
});
|
||||
|
||||
expect(validator.validate(field)).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@@ -0,0 +1,240 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FormFieldModel } from './form-field.model';
|
||||
import { FormFieldTypes } from './form-field-types';
|
||||
|
||||
export interface FormFieldValidator {
|
||||
|
||||
isSupported(field: FormFieldModel): boolean;
|
||||
validate(field: FormFieldModel): boolean;
|
||||
|
||||
}
|
||||
|
||||
export class RequiredFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.TEXT,
|
||||
FormFieldTypes.MULTILINE_TEXT,
|
||||
FormFieldTypes.NUMBER,
|
||||
FormFieldTypes.TYPEAHEAD,
|
||||
FormFieldTypes.DROPDOWN,
|
||||
FormFieldTypes.PEOPLE,
|
||||
FormFieldTypes.FUNCTIONAL_GROUP,
|
||||
FormFieldTypes.RADIO_BUTTONS,
|
||||
FormFieldTypes.UPLOAD
|
||||
];
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field &&
|
||||
this.supportedTypes.indexOf(field.type) > -1 &&
|
||||
field.required;
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field)) {
|
||||
|
||||
if (field.type === FormFieldTypes.DROPDOWN) {
|
||||
if (field.hasEmptyValue && field.emptyOption) {
|
||||
if (field.value === field.emptyOption.id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === FormFieldTypes.RADIO_BUTTONS) {
|
||||
let option = field.options.find(opt => opt.id === field.value);
|
||||
return !!option;
|
||||
}
|
||||
|
||||
if (field.type === FormFieldTypes.UPLOAD) {
|
||||
return field.value && field.value.length > 0;
|
||||
}
|
||||
|
||||
if (!field.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class NumberFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.NUMBER
|
||||
];
|
||||
|
||||
static isNumber(value: any): boolean {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isNaN(+value);
|
||||
}
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field && this.supportedTypes.indexOf(field.type) > -1;
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field)) {
|
||||
if (field.value === null ||
|
||||
field.value === undefined ||
|
||||
field.value === '' ||
|
||||
NumberFieldValidator.isNumber(field.value)) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary = 'Input must be a number';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class MinLengthFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.TEXT,
|
||||
FormFieldTypes.MULTILINE_TEXT
|
||||
];
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field &&
|
||||
this.supportedTypes.indexOf(field.type) > -1 &&
|
||||
field.minLength > 0;
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value) {
|
||||
if (field.value.length >= field.minLength) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary = `Should be at least ${field.minLength} characters long.`;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class MaxLengthFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.TEXT,
|
||||
FormFieldTypes.MULTILINE_TEXT
|
||||
];
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field &&
|
||||
this.supportedTypes.indexOf(field.type) > -1 &&
|
||||
field.maxLength > 0;
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value) {
|
||||
if (field.value.length <= field.maxLength) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary = `Should be ${field.maxLength} characters maximum.`;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class MinValueFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.NUMBER
|
||||
];
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field &&
|
||||
this.supportedTypes.indexOf(field.type) > -1 &&
|
||||
NumberFieldValidator.isNumber(field.minValue);
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value) {
|
||||
let value: number = +field.value;
|
||||
let minValue: number = +field.minValue;
|
||||
|
||||
if (value >= minValue) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary = `Should not be less than ${field.minValue}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class MaxValueFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.NUMBER
|
||||
];
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field &&
|
||||
this.supportedTypes.indexOf(field.type) > -1 &&
|
||||
NumberFieldValidator.isNumber(field.maxValue);
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value) {
|
||||
let value: number = +field.value;
|
||||
let maxValue: number = +field.maxValue;
|
||||
|
||||
if (value <= maxValue) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary = `Should not be greater than ${field.maxValue}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class RegExFieldValidator implements FormFieldValidator {
|
||||
|
||||
private supportedTypes = [
|
||||
FormFieldTypes.TEXT,
|
||||
FormFieldTypes.MULTILINE_TEXT
|
||||
];
|
||||
|
||||
isSupported(field: FormFieldModel): boolean {
|
||||
return field &&
|
||||
this.supportedTypes.indexOf(field.type) > -1 &&
|
||||
!!field.regexPattern;
|
||||
}
|
||||
|
||||
validate(field: FormFieldModel): boolean {
|
||||
if (this.isSupported(field) && field.value) {
|
||||
if (field.value.length > 0 && field.value.match(new RegExp('^' + field.regexPattern + '$'))) {
|
||||
return true;
|
||||
}
|
||||
field.validationSummary = 'Invalid value format';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@@ -113,15 +113,6 @@ describe('FormFieldModel', () => {
|
||||
expect(field.readOnly).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should parse and convert empty dropdown value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DROPDOWN,
|
||||
value: ''
|
||||
});
|
||||
|
||||
expect(field.value).toBe('empty');
|
||||
});
|
||||
|
||||
it('should parse and leave dropdown value as is', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.DROPDOWN,
|
||||
@@ -145,19 +136,6 @@ describe('FormFieldModel', () => {
|
||||
expect(field.value).toBe('opt2');
|
||||
});
|
||||
|
||||
it('should parse and fall back to first radio button value', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.RADIO_BUTTONS,
|
||||
options: [
|
||||
{ id: 'opt1', value: 'Option 1' },
|
||||
{ id: 'opt2', value: 'Option 2' }
|
||||
],
|
||||
value: 'opt3'
|
||||
});
|
||||
|
||||
expect(field.value).toBe('opt1');
|
||||
});
|
||||
|
||||
it('should parse and leave radio button value as is', () => {
|
||||
let field = new FormFieldModel(new FormModel(), {
|
||||
type: FormFieldTypes.RADIO_BUTTONS,
|
||||
|
@@ -21,11 +21,23 @@ import { FormFieldTypes } from './form-field-types';
|
||||
import { FormFieldMetadata } from './form-field-metadata';
|
||||
import { FormModel } from './form.model';
|
||||
import { WidgetVisibilityModel } from '../../../models/widget-visibility.model';
|
||||
import {
|
||||
FormFieldValidator,
|
||||
RequiredFieldValidator,
|
||||
NumberFieldValidator,
|
||||
MinLengthFieldValidator,
|
||||
MaxLengthFieldValidator,
|
||||
MinValueFieldValidator,
|
||||
MaxValueFieldValidator,
|
||||
RegExFieldValidator
|
||||
} from './form-field-validator';
|
||||
|
||||
|
||||
export class FormFieldModel extends FormWidgetModel {
|
||||
|
||||
private _value: string;
|
||||
private _readOnly: boolean = false;
|
||||
private _isValid: boolean = true;
|
||||
|
||||
fieldType: string;
|
||||
id: string;
|
||||
@@ -35,6 +47,11 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
overrideId: boolean;
|
||||
tab: string;
|
||||
colspan: number = 1;
|
||||
minLength: number = 0;
|
||||
maxLength: number = 0;
|
||||
minValue: string;
|
||||
maxValue: string;
|
||||
regexPattern: string;
|
||||
options: FormFieldOption[] = [];
|
||||
restUrl: string;
|
||||
restResponsePath: string;
|
||||
@@ -49,12 +66,17 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
isVisible: boolean = true;
|
||||
visibilityCondition: WidgetVisibilityModel = null;
|
||||
|
||||
emptyOption: FormFieldOption;
|
||||
validationSummary: string;
|
||||
validators: FormFieldValidator[] = [];
|
||||
|
||||
get value(): any {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(v: any) {
|
||||
this._value = v;
|
||||
this.validate();
|
||||
this.updateForm();
|
||||
}
|
||||
|
||||
@@ -65,6 +87,27 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
return this._readOnly;
|
||||
}
|
||||
|
||||
get isValid(): boolean {
|
||||
return this._isValid;
|
||||
}
|
||||
|
||||
validate(): boolean {
|
||||
this.validationSummary = null;
|
||||
|
||||
// TODO: consider doing that on value setter and caching result
|
||||
if (this.validators && this.validators.length > 0) {
|
||||
for (let i = 0; i < this.validators.length; i++) {
|
||||
if (!this.validators[i].validate(this)) {
|
||||
this._isValid = false;
|
||||
return this._isValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._isValid = true;
|
||||
return this._isValid;
|
||||
}
|
||||
|
||||
constructor(form: FormModel, json?: any) {
|
||||
super(form, json);
|
||||
|
||||
@@ -82,6 +125,11 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
this.restIdProperty = json.restIdProperty;
|
||||
this.restLabelProperty = json.restLabelProperty;
|
||||
this.colspan = <number> json.colspan;
|
||||
this.minLength = <number> json.minLength || 0;
|
||||
this.maxLength = <number> json.maxLength || 0;
|
||||
this.minValue = json.minValue;
|
||||
this.maxValue = json.maxValue;
|
||||
this.regexPattern = json.regexPattern;
|
||||
this.options = <FormFieldOption[]> json.options || [];
|
||||
this.hasEmptyValue = <boolean> json.hasEmptyValue;
|
||||
this.className = json.className;
|
||||
@@ -90,10 +138,24 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
this.hyperlinkUrl = json.hyperlinkUrl;
|
||||
this.displayText = json.displayText;
|
||||
this.visibilityCondition = <WidgetVisibilityModel> json.visibilityCondition;
|
||||
|
||||
this._value = this.parseValue(json);
|
||||
this.updateForm();
|
||||
}
|
||||
|
||||
if (this.hasEmptyValue && this.options && this.options.length > 0) {
|
||||
this.emptyOption = this.options[0];
|
||||
}
|
||||
|
||||
this.validators = [
|
||||
new RequiredFieldValidator(),
|
||||
new NumberFieldValidator(),
|
||||
new MinLengthFieldValidator(),
|
||||
new MaxLengthFieldValidator(),
|
||||
new MinValueFieldValidator(),
|
||||
new MaxValueFieldValidator(),
|
||||
new RegExFieldValidator()
|
||||
];
|
||||
|
||||
this.updateForm();
|
||||
}
|
||||
|
||||
parseValue(json: any): any {
|
||||
@@ -103,10 +165,15 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
This is needed due to Activiti issue related to reading dropdown values as value string
|
||||
but saving back as object: { id: <id>, name: <name> }
|
||||
*/
|
||||
// TODO: needs review
|
||||
if (json.type === FormFieldTypes.DROPDOWN) {
|
||||
if (value === '') {
|
||||
value = 'empty';
|
||||
if (json.hasEmptyValue && json.options) {
|
||||
let options = <FormFieldOption[]> json.options || [];
|
||||
if (options.length > 0) {
|
||||
let emptyOption = json.options[0];
|
||||
if (value === '' || value === emptyOption.id || value === emptyOption.name) {
|
||||
value = emptyOption.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,13 +182,12 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
but saving back as object: { id: <id>, name: <name> }
|
||||
*/
|
||||
if (json.type === FormFieldTypes.RADIO_BUTTONS) {
|
||||
// Activiti has a bug with default radio button value,
|
||||
// so try resolving current one with a fallback to first entry
|
||||
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === value);
|
||||
// Activiti has a bug with default radio button value where initial selection passed as `name` value
|
||||
// so try resolving current one with a fallback to first entry via name or id
|
||||
// TODO: needs to be reported and fixed at Activiti side
|
||||
let entry: FormFieldOption[] = this.options.filter(opt => opt.id === value || opt.name === value);
|
||||
if (entry.length > 0) {
|
||||
value = entry[0].id;
|
||||
} else if (this.options.length > 0) {
|
||||
value = this.options[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +195,9 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
}
|
||||
|
||||
updateForm() {
|
||||
if (!this.form) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.type) {
|
||||
case FormFieldTypes.DROPDOWN:
|
||||
@@ -177,5 +246,7 @@ export class FormFieldModel extends FormWidgetModel {
|
||||
this.form.values[this.id] = this.value;
|
||||
}
|
||||
}
|
||||
|
||||
this.form.onFormFieldChanged(this);
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import { FormValues } from './form-values';
|
||||
import { ContainerModel } from './container.model';
|
||||
import { TabModel } from './tab.model';
|
||||
import { FormOutcomeModel } from './form-outcome.model';
|
||||
import { FormFieldModel } from './form-field.model';
|
||||
|
||||
export class FormModel {
|
||||
|
||||
@@ -31,6 +32,7 @@ export class FormModel {
|
||||
private _name: string;
|
||||
private _taskId: string;
|
||||
private _taskName: string = FormModel.UNSET_TASK_NAME;
|
||||
private _isValid: boolean = true;
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
@@ -48,6 +50,10 @@ export class FormModel {
|
||||
return this._taskName;
|
||||
}
|
||||
|
||||
get isValid(): boolean {
|
||||
return this._isValid;
|
||||
}
|
||||
|
||||
readOnly: boolean = false;
|
||||
tabs: TabModel[] = [];
|
||||
fields: ContainerModel[] = [];
|
||||
@@ -102,7 +108,8 @@ export class FormModel {
|
||||
if (field.tab) {
|
||||
let tab = tabCache[field.tab];
|
||||
if (tab) {
|
||||
tab.fields.push(new ContainerModel(this, field.json));
|
||||
// tab.fields.push(new ContainerModel(this, field.json));
|
||||
tab.fields.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,6 +124,51 @@ export class FormModel {
|
||||
);
|
||||
}
|
||||
}
|
||||
this.validateForm();
|
||||
}
|
||||
|
||||
onFormFieldChanged(field: FormFieldModel) {
|
||||
this.validateField(field);
|
||||
}
|
||||
|
||||
// TODO: evaluate and cache once the form is loaded
|
||||
private getFormFields(): FormFieldModel[] {
|
||||
let result: FormFieldModel[] = [];
|
||||
|
||||
for (let i = 0; i < this.fields.length; i++) {
|
||||
let container = this.fields[i];
|
||||
for (let j = 0; j < container.columns.length; j++) {
|
||||
let column = container.columns[j];
|
||||
for (let k = 0; k < column.fields.length; k++) {
|
||||
let field = column.fields[k];
|
||||
result.push(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private validateForm() {
|
||||
this._isValid = true;
|
||||
let fields = this.getFormFields();
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
if (!fields[i].validate()) {
|
||||
this._isValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private validateField(field: FormFieldModel) {
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
if (!field.validate()) {
|
||||
this._isValid = false;
|
||||
return;
|
||||
}
|
||||
this.validateForm();
|
||||
}
|
||||
|
||||
private parseContainerFields(json: any): ContainerModel[] {
|
||||
|
@@ -0,0 +1,40 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, describe, expect } from '@angular/core/testing';
|
||||
import { GroupUserModel } from './group-user.model';
|
||||
|
||||
describe('GroupUserModel', () => {
|
||||
|
||||
it('should init with json', () => {
|
||||
let json = {
|
||||
company: '<company>',
|
||||
email: '<email>',
|
||||
firstName: '<firstName>',
|
||||
id: '<id>',
|
||||
lastName: '<lastName>'
|
||||
};
|
||||
|
||||
let model = new GroupUserModel(json);
|
||||
expect(model.company).toBe(json.company);
|
||||
expect(model.email).toBe(json.email);
|
||||
expect(model.firstName).toBe(json.firstName);
|
||||
expect(model.id).toBe(json.id);
|
||||
expect(model.lastName).toBe(json.lastName);
|
||||
});
|
||||
|
||||
});
|
@@ -27,3 +27,4 @@ export * from './container.model';
|
||||
export * from './tab.model';
|
||||
export * from './form-outcome.model';
|
||||
export * from './form-outcome-event.model';
|
||||
export * from './form-field-validator';
|
||||
|
@@ -2,6 +2,22 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-widget > select {
|
||||
.dropdown-widget__select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-widget__invalid .dropdown-widget__select {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.dropdown-widget__invalid .dropdown-widget__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.dropdown-widget__invalid .dropdown-widget__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.dropdown-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,6 +1,10 @@
|
||||
<div class="dropdown-widget">
|
||||
<label [attr.for]="field.id">{{field.name}}</label>
|
||||
<select [(ngModel)]="field.value" (ngModelChange)="checkVisibility(field)">
|
||||
<div class="dropdown-widget"
|
||||
[class.dropdown-widget__invalid]="!field.isValid">
|
||||
<label class="dropdown-widget__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<select class="dropdown-widget__select"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)">
|
||||
<option *ngFor="let opt of field.options" [value]="opt.id">{{opt.name}}</option>
|
||||
</select>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
@@ -42,7 +42,8 @@ describe('DropdownWidget', () => {
|
||||
});
|
||||
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: fieldId
|
||||
id: fieldId,
|
||||
restUrl: '<url>'
|
||||
});
|
||||
|
||||
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => {
|
||||
|
@@ -21,7 +21,6 @@ import { WidgetComponent } from './../widget.component';
|
||||
import { FormFieldOption } from './../core/form-field-option';
|
||||
|
||||
declare let __moduleName: string;
|
||||
declare var componentHandler;
|
||||
|
||||
@Component({
|
||||
moduleId: __moduleName,
|
||||
@@ -36,18 +35,24 @@ export class DropdownWidget extends WidgetComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.formService
|
||||
.getRestFieldValues(
|
||||
this.field.form.taskId,
|
||||
this.field.id
|
||||
)
|
||||
.subscribe(
|
||||
(result: FormFieldOption[]) => {
|
||||
this.field.options = result || [];
|
||||
this.field.updateForm();
|
||||
},
|
||||
this.handleError
|
||||
);
|
||||
if (this.field && this.field.restUrl) {
|
||||
this.formService
|
||||
.getRestFieldValues(
|
||||
this.field.form.taskId,
|
||||
this.field.id
|
||||
)
|
||||
.subscribe(
|
||||
(result: FormFieldOption[]) => {
|
||||
let options = [];
|
||||
if (this.field.emptyOption) {
|
||||
options.push(this.field.emptyOption);
|
||||
}
|
||||
this.field.options = options.concat((result || []));
|
||||
this.field.updateForm();
|
||||
},
|
||||
this.handleError
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
handleError(error: any) {
|
||||
|
@@ -27,3 +27,23 @@
|
||||
.functional-group-widget--autocomplete > ul > li {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.people-widget--autocomplete > ul > li {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.functional-group-widget__invalid .mdl-textfield__input {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.functional-group-widget__invalid .mdl-textfield__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.functional-group-widget__invalid .mdl-textfield__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.functional-group-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label functional-group-widget">
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label functional-group-widget"
|
||||
[class.functional-group-widget__invalid]="!field.isValid">
|
||||
<input class="mdl-textfield__input"
|
||||
type="text"
|
||||
[attr.id]="field.id"
|
||||
@@ -8,8 +9,8 @@
|
||||
(blur)="onBlur()"
|
||||
[disabled]="field.readOnly">
|
||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
||||
<div class="functional-group-widget--autocomplete mdl-shadow--2dp" *ngIf="popupVisible && groups.length > 0">
|
||||
<ul>
|
||||
<li *ngFor="let item of groups"
|
||||
|
@@ -37,7 +37,16 @@ describe('FunctionalGroupWidget', () => {
|
||||
it('should setup text from underlying field on init', () => {
|
||||
let group = new GroupModel({ name: 'group-1'});
|
||||
widget.field.value = group;
|
||||
|
||||
spyOn(formService, 'getWorkflowGroups').and.returnValue(
|
||||
Observable.create(observer => {
|
||||
observer.next([]);
|
||||
observer.complete();
|
||||
})
|
||||
);
|
||||
|
||||
widget.ngOnInit();
|
||||
expect(formService.getWorkflowGroups).toHaveBeenCalled();
|
||||
expect(widget.value).toBe(group.name);
|
||||
});
|
||||
|
||||
|
@@ -54,6 +54,13 @@ export class FunctionalGroupWidget extends WidgetComponent implements OnInit {
|
||||
let restrictWithGroup = <GroupModel> params['restrictWithGroup'];
|
||||
this.groupId = restrictWithGroup.id;
|
||||
}
|
||||
|
||||
// Load auto-completion for previously saved value
|
||||
if (this.value) {
|
||||
this.formService
|
||||
.getWorkflowGroups(this.value, this.groupId)
|
||||
.subscribe((result: GroupModel[]) => this.groups = result || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,19 @@
|
||||
.multiline-text-widget {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.multiline-text-widget__invalid .mdl-textfield__input {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.multiline-text-widget__invalid .mdl-textfield__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.multiline-text-widget__invalid .mdl-textfield__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.multiline-text-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label multiline-text-widget">
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label multiline-text-widget"
|
||||
[class.multiline-text-widget__invalid]="!field.isValid">
|
||||
<textarea class="mdl-textfield__input"
|
||||
type="text"
|
||||
rows= "3"
|
||||
[attr.id]="field.id"
|
||||
[attr.required]="isRequired()"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)"
|
||||
[disabled]="field.readOnly">
|
||||
</textarea>
|
||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
||||
|
@@ -1,3 +1,20 @@
|
||||
:host .number-widget {
|
||||
.number-widget {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.number-widget__invalid .mdl-textfield__input {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.number-widget__invalid .mdl-textfield__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.number-widget__invalid .mdl-textfield__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.number-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
|
@@ -1,11 +1,13 @@
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label number-widget">
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label number-widget"
|
||||
[class.number-widget__invalid]="!field.isValid">
|
||||
<input class="mdl-textfield__input"
|
||||
type="text"
|
||||
pattern="-?[0-9]*(\.[0-9]+)?"
|
||||
[attr.id]="field.id"
|
||||
[attr.required]="isRequired()"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)"
|
||||
[disabled]="field.readOnly">
|
||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<span class="mdl-textfield__error">Input is not a number!</span>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
@@ -27,3 +27,19 @@
|
||||
.people-widget--autocomplete > ul > li {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.people-widget__invalid .mdl-textfield__input {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.people-widget__invalid .mdl-textfield__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.people-widget__invalid .mdl-textfield__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.people-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label people-widget">
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label people-widget"
|
||||
[class.people-widget__invalid]="!field.isValid">
|
||||
<input class="mdl-textfield__input"
|
||||
type="text"
|
||||
[attr.id]="field.id"
|
||||
@@ -8,8 +9,8 @@
|
||||
(blur)="onBlur()"
|
||||
[disabled]="field.readOnly">
|
||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
||||
<div class="people-widget--autocomplete mdl-shadow--2dp" *ngIf="popupVisible && users.length > 0">
|
||||
<ul>
|
||||
<li *ngFor="let item of users"
|
||||
|
@@ -62,6 +62,12 @@ describe('PeopleWidget', () => {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe'
|
||||
});
|
||||
|
||||
spyOn(formService, 'getWorkflowUsers').and.returnValue(Observable.create(observer => {
|
||||
observer.next([]);
|
||||
observer.complete();
|
||||
}));
|
||||
|
||||
widget.ngOnInit();
|
||||
expect(widget.value).toBe('John Doe');
|
||||
});
|
||||
|
@@ -55,6 +55,13 @@ export class PeopleWidget extends WidgetComponent implements OnInit {
|
||||
let restrictWithGroup = <GroupModel> params['restrictWithGroup'];
|
||||
this.groupId = restrictWithGroup.id;
|
||||
}
|
||||
|
||||
// Load auto-completion for previously saved value
|
||||
if (this.value) {
|
||||
this.formService
|
||||
.getWorkflowUsers(this.value, this.groupId)
|
||||
.subscribe((result: GroupUserModel[]) => this.users = result || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +104,7 @@ export class PeopleWidget extends WidgetComponent implements OnInit {
|
||||
|
||||
getDisplayName(model: GroupUserModel) {
|
||||
if (model) {
|
||||
let displayName = `${model.firstName} ${model.lastName}`;
|
||||
let displayName = `${model.firstName || ''} ${model.lastName || ''}`;
|
||||
return displayName.trim();
|
||||
}
|
||||
|
||||
|
@@ -1 +1,17 @@
|
||||
.radio-buttons-widget {}
|
||||
|
||||
.radio-buttons-widget__invalid .mdl-radio__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.radio-buttons-widget__invalid .radio-buttons-widget__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.radio-buttons-widget__invalid .radio-buttons-widget__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.radio-buttons-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
<div class="radio-buttons-widget">
|
||||
<div class="radio-buttons-widget"
|
||||
[class.radio-buttons-widget__invalid]="!field.isValid">
|
||||
<label class="radio-buttons-widget__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<div *ngFor="let opt of field.options">
|
||||
<label [attr.for]="opt.id" class="mdl-radio mdl-js-radio">
|
||||
<input type="radio"
|
||||
@@ -12,4 +14,5 @@
|
||||
<span class="mdl-radio__label">{{opt.name}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
@@ -0,0 +1,96 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, describe, expect, beforeEach } from '@angular/core/testing';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { FormService } from '../../../services/form.service';
|
||||
import { RadioButtonsWidget } from './radio-buttons.widget';
|
||||
import { FormModel } from './../core/form.model';
|
||||
import { FormFieldModel } from './../core/form-field.model';
|
||||
|
||||
describe('RadioButtonsWidget', () => {
|
||||
|
||||
let formService: FormService;
|
||||
let widget: RadioButtonsWidget;
|
||||
|
||||
beforeEach(() => {
|
||||
formService = new FormService(null, null);
|
||||
widget = new RadioButtonsWidget(formService);
|
||||
widget.field = new FormFieldModel(new FormModel(), { restUrl: '<url>' });
|
||||
});
|
||||
|
||||
it('should request field values from service', () => {
|
||||
const taskId = '<form-id>';
|
||||
const fieldId = '<field-id>';
|
||||
|
||||
let form = new FormModel({
|
||||
taskId: taskId
|
||||
});
|
||||
|
||||
widget.field = new FormFieldModel(form, {
|
||||
id: fieldId,
|
||||
restUrl: '<url>'
|
||||
});
|
||||
|
||||
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => {
|
||||
observer.next(null);
|
||||
observer.complete();
|
||||
}));
|
||||
widget.ngOnInit();
|
||||
expect(formService.getRestFieldValues).toHaveBeenCalledWith(taskId, fieldId);
|
||||
});
|
||||
|
||||
it('should update form on values fetched', () => {
|
||||
let form = widget.field;
|
||||
spyOn(form, 'updateForm').and.stub();
|
||||
|
||||
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => {
|
||||
observer.next(null);
|
||||
observer.complete();
|
||||
}));
|
||||
widget.ngOnInit();
|
||||
expect(form.updateForm).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should require field with rest URL to fetch data', () => {
|
||||
spyOn(formService, 'getRestFieldValues').and.returnValue(Observable.create(observer => {
|
||||
observer.next(null);
|
||||
observer.complete();
|
||||
}));
|
||||
|
||||
let field = widget.field;
|
||||
widget.field = null;
|
||||
widget.ngOnInit();
|
||||
expect(formService.getRestFieldValues).not.toHaveBeenCalled();
|
||||
widget.field = field;
|
||||
|
||||
widget.field.restUrl = null;
|
||||
widget.ngOnInit();
|
||||
expect(formService.getRestFieldValues).not.toHaveBeenCalled();
|
||||
|
||||
widget.field.restUrl = '<url>';
|
||||
widget.ngOnInit();
|
||||
expect(formService.getRestFieldValues).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log error to console by default', () => {
|
||||
spyOn(console, 'error').and.stub();
|
||||
widget.handleError('Err');
|
||||
expect(console.error).toHaveBeenCalledWith('Err');
|
||||
});
|
||||
|
||||
});
|
@@ -15,8 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WidgetComponent } from './../widget.component';
|
||||
import { FormService } from '../../../services/form.service';
|
||||
import { FormFieldOption } from './../core/form-field-option';
|
||||
|
||||
declare let __moduleName: string;
|
||||
declare var componentHandler;
|
||||
@@ -27,6 +29,31 @@ declare var componentHandler;
|
||||
templateUrl: './radio-buttons.widget.html',
|
||||
styleUrls: ['./radio-buttons.widget.css']
|
||||
})
|
||||
export class RadioButtonsWidget extends WidgetComponent {
|
||||
export class RadioButtonsWidget extends WidgetComponent implements OnInit {
|
||||
|
||||
constructor(private formService: FormService) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.field && this.field.restUrl) {
|
||||
this.formService
|
||||
.getRestFieldValues(
|
||||
this.field.form.taskId,
|
||||
this.field.id
|
||||
)
|
||||
.subscribe(
|
||||
(result: FormFieldOption[]) => {
|
||||
this.field.options = result || [];
|
||||
this.field.updateForm();
|
||||
},
|
||||
this.handleError
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
handleError(error: any) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,3 +1,20 @@
|
||||
.text-widget {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.text-widget__invalid .mdl-textfield__input {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.text-widget__invalid .mdl-textfield__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.text-widget__invalid .mdl-textfield__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.text-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,9 +1,12 @@
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label text-widget">
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label text-widget"
|
||||
[class.text-widget__invalid]="!field.isValid">
|
||||
<input class="mdl-textfield__input"
|
||||
type="text"
|
||||
[attr.id]="field.id"
|
||||
[attr.required]="isRequired()"
|
||||
[(ngModel)]="field.value"
|
||||
(ngModelChange)="checkVisibility(field)"
|
||||
[disabled]="field.readOnly">
|
||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
@@ -27,3 +27,21 @@
|
||||
.typeahead-autocomplete > ul > li {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.typeahead-widget__invalid .mdl-textfield__input {
|
||||
border-color: #d50000;
|
||||
}
|
||||
|
||||
.typeahead-widget__invalid .mdl-textfield__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.typeahead-widget__invalid .mdl-textfield__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.typeahead-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label typeahead-widget"
|
||||
[class.is-dirty]="value">
|
||||
[class.is-dirty]="value"
|
||||
[class.typeahead-widget__invalid]="!field.isValid">
|
||||
<input class="mdl-textfield__input"
|
||||
type="text"
|
||||
[attr.id]="field.id"
|
||||
@@ -9,8 +10,8 @@
|
||||
(blur)="onBlur()"
|
||||
[disabled]="field.readOnly">
|
||||
<label class="mdl-textfield__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
||||
<div class="typeahead-autocomplete mdl-shadow--2dp" *ngIf="options.length > 0 && popupVisible">
|
||||
<ul>
|
||||
<li *ngFor="let item of options"
|
||||
|
@@ -91,7 +91,9 @@ export class TypeaheadWidget extends WidgetComponent implements OnInit {
|
||||
this.popupVisible = false;
|
||||
|
||||
let options = this.field.options || [];
|
||||
let field = options.find(item => item.name.toLocaleLowerCase() === this.value.toLocaleLowerCase());
|
||||
let lValue = this.value ? this.value.toLocaleLowerCase() : null;
|
||||
|
||||
let field = options.find(item => item.name && item.name.toLocaleLowerCase() === lValue);
|
||||
if (field) {
|
||||
this.field.value = field.id;
|
||||
this.value = field.name;
|
||||
@@ -100,6 +102,7 @@ export class TypeaheadWidget extends WidgetComponent implements OnInit {
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
// TODO: seems to be not needed as field.value setter calls it
|
||||
this.field.updateForm();
|
||||
}
|
||||
|
||||
|
@@ -15,3 +15,19 @@
|
||||
float: left;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.upload-widget__invalid .upload-widget__label {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.upload-widget__invalid .upload-widget__label:after {
|
||||
background-color: #d50000;
|
||||
}
|
||||
|
||||
.upload-widget__invalid .upload-widget__file {
|
||||
color: #d50000;
|
||||
}
|
||||
|
||||
.upload-widget__invalid .mdl-textfield__error {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div class="upload-widget">
|
||||
|
||||
<label [attr.for]="field.id">{{field.name}}</label>
|
||||
<div class="upload-widget"
|
||||
[class.upload-widget__invalid]="!field.isValid">
|
||||
<label class="upload-widget__label" [attr.for]="field.id">{{field.name}}</label>
|
||||
<div>
|
||||
<i *ngIf="hasFile" class="material-icons upload-widget__icon">attachment</i>
|
||||
<span *ngIf="hasFile" class="upload-widget__file">{{getUploadedFileName()}}</span>
|
||||
@@ -12,4 +12,5 @@
|
||||
(change)="onFileChanged($event)">
|
||||
<button *ngIf="hasFile" (click)="reset(file);" class="upload-widget__reset">X</button>
|
||||
</div>
|
||||
<span *ngIf="field.validationSummary" class="mdl-textfield__error">{{field.validationSummary}}</span>
|
||||
</div>
|
||||
|
@@ -50,7 +50,7 @@ describe('WidgetComponent', () => {
|
||||
let component = new WidgetComponent();
|
||||
|
||||
expect(component.hasField()).toBeFalsy();
|
||||
component.field = new FormFieldModel(null);
|
||||
component.field = new FormFieldModel(new FormModel());
|
||||
expect(component.hasField()).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@@ -36,6 +36,13 @@ export class WidgetComponent implements AfterViewInit {
|
||||
return this.field ? true : false;
|
||||
}
|
||||
|
||||
isRequired(): any {
|
||||
if (this.field && this.field.required) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.setupMaterialComponents();
|
||||
this.fieldChanged.emit(this.field);
|
||||
|
@@ -205,6 +205,7 @@ describe('WidgetVisibilityService', () => {
|
||||
expect(res).toBeFalsy();
|
||||
});
|
||||
|
||||
/*
|
||||
it('should be able to retrieve the value of a process variable', (done) => {
|
||||
service.getTaskProcessVariableModelsForTask(9999).subscribe(
|
||||
(res: TaskProcessVariableModel[]) => {
|
||||
@@ -223,6 +224,7 @@ describe('WidgetVisibilityService', () => {
|
||||
expect(varValue).not.toBe(null);
|
||||
expect(varValue).toBe('test_value_1');
|
||||
});
|
||||
*/
|
||||
|
||||
it('should be able to retrieve the value of a form variable', () => {
|
||||
let fakeForm = new FormModel({variables: [
|
||||
@@ -308,6 +310,7 @@ describe('WidgetVisibilityService', () => {
|
||||
expect(rightValue).toBe('100');
|
||||
});
|
||||
|
||||
/*
|
||||
it('should retrieve the value for the right field when it is a process variable', (done) => {
|
||||
service.getTaskProcessVariableModelsForTask(9999).subscribe(
|
||||
(res: TaskProcessVariableModel[]) => {
|
||||
@@ -327,6 +330,7 @@ describe('WidgetVisibilityService', () => {
|
||||
expect(rightValue).not.toBe(null);
|
||||
expect(rightValue).toBe('test_value_2');
|
||||
});
|
||||
*/
|
||||
|
||||
it('should retrieve the value for the right field when it is a form variable', () => {
|
||||
let fakeFormWithField = new FormModel(fakeFormJson);
|
||||
@@ -466,6 +470,7 @@ describe('WidgetVisibilityService', () => {
|
||||
expect(isVisible).toBeTruthy();
|
||||
});
|
||||
|
||||
/*
|
||||
it('should evaluate the visibility for the field between form value and process var', (varReady) => {
|
||||
service.getTaskProcessVariableModelsForTask(9999).subscribe(
|
||||
(res: TaskProcessVariableModel[]) => {
|
||||
@@ -487,6 +492,7 @@ describe('WidgetVisibilityService', () => {
|
||||
|
||||
expect(isVisible).toBeTruthy();
|
||||
});
|
||||
*/
|
||||
|
||||
/*
|
||||
it('should evaluate visibility with multiple conditions', (ready) => {
|
||||
|
Reference in New Issue
Block a user