Create a new widget for decimal type (#9335)

* [AAE-19481] Create a new widget for decimal type

* update

* CR

* unit for decimal widgets

* added test for positibe validator

* added decimal validator tests

* cr
This commit is contained in:
Bartosz Sekula 2024-02-15 14:23:55 +01:00 committed by GitHub
parent 300d46dc7e
commit 16005ef298
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 533 additions and 17 deletions

View File

@ -91,10 +91,9 @@ export class CardViewItemDispatcherComponent implements OnChanges {
this.loaded = true; this.loaded = true;
} }
Object.keys(changes) Object.entries(changes)
.map((changeName) => [changeName, changes[changeName]]) .forEach(([changeName, change]: [string, SimpleChange]) => {
.forEach(([inputParamName, simpleChange]: [string, SimpleChange]) => { this.componentReference.instance[changeName] = change.currentValue;
this.componentReference.instance[inputParamName] = simpleChange.currentValue;
}); });
this.proxy('ngOnChanges', changes); this.proxy('ngOnChanges', changes);

View File

@ -24,3 +24,11 @@ export interface CardViewTextItemProperties extends CardViewItemProperties {
pipes?: CardViewTextItemPipeProperty[]; pipes?: CardViewTextItemPipeProperty[];
clickCallBack?: any; clickCallBack?: any;
} }
export interface CardViewIntItemProperties extends CardViewTextItemProperties {
allowOnlyPositiveNumbers?: boolean;
}
export interface CardViewFloatItemProperties extends CardViewTextItemProperties {
precision?: number;
}

View File

@ -48,7 +48,8 @@ export abstract class CardViewBaseItemModel<T = any> {
if (props?.constraints?.length ?? 0) { if (props?.constraints?.length ?? 0) {
for (const constraint of props.constraints) { for (const constraint of props.constraints) {
if (constraint.type !== 'LIST') { if (constraint.type !== 'LIST') {
this.validators.push(validatorsMap[constraint.type.toLowerCase()](constraint.parameters)); const validatorFactory = validatorsMap[constraint.type.toLowerCase()];
this.validators.push(validatorFactory(constraint.parameters));
} }
} }
} }

View File

@ -29,6 +29,7 @@ export class CardViewFloatItemModel extends CardViewTextItemModel implements Car
super(cardViewTextItemProperties); super(cardViewTextItemProperties);
this.validators.push(new CardViewItemFloatValidator()); this.validators.push(new CardViewItemFloatValidator());
if (cardViewTextItemProperties.value && !cardViewTextItemProperties.multivalued) { if (cardViewTextItemProperties.value && !cardViewTextItemProperties.multivalued) {
this.value = parseFloat(cardViewTextItemProperties.value); this.value = parseFloat(cardViewTextItemProperties.value);
} }

View File

@ -18,19 +18,24 @@
import { CardViewItem } from '../interfaces/card-view-item.interface'; import { CardViewItem } from '../interfaces/card-view-item.interface';
import { DynamicComponentModel } from '../../common/services/dynamic-component-mapper.service'; import { DynamicComponentModel } from '../../common/services/dynamic-component-mapper.service';
import { CardViewTextItemModel } from './card-view-textitem.model'; import { CardViewTextItemModel } from './card-view-textitem.model';
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces'; import { CardViewIntItemProperties } from '../interfaces/card-view.interfaces';
import { CardViewItemIntValidator } from '../validators/card-view.validators'; import { CardViewItemIntValidator, CardViewItemPositiveIntValidator } from '../validators/card-view.validators';
export class CardViewIntItemModel extends CardViewTextItemModel implements CardViewItem, DynamicComponentModel { export class CardViewIntItemModel extends CardViewTextItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'int'; type: string = 'int';
inputType: string = 'number'; inputType: string = 'number';
constructor(cardViewTextItemProperties: CardViewTextItemProperties) { constructor(cardViewIntItemProperties: CardViewIntItemProperties) {
super(cardViewTextItemProperties); super(cardViewIntItemProperties);
this.validators.push(new CardViewItemIntValidator()); this.validators.push(new CardViewItemIntValidator());
if (cardViewTextItemProperties.value && !cardViewTextItemProperties.multivalued) {
this.value = parseInt(cardViewTextItemProperties.value, 10); if (cardViewIntItemProperties.allowOnlyPositiveNumbers) {
this.validators.push(new CardViewItemPositiveIntValidator());
}
if (cardViewIntItemProperties.value && !cardViewIntItemProperties.multivalued) {
this.value = parseInt(cardViewIntItemProperties.value, 10);
} }
} }

View File

@ -0,0 +1,50 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { CardViewItemPositiveIntValidator } from './card-view-item-only-positive-int.validator';
describe('CardViewItemPositiveIntValidator', () => {
let validator: CardViewItemPositiveIntValidator;
beforeEach(() => {
validator = new CardViewItemPositiveIntValidator();
});
it('should return false for invalid integer value', () => {
expect(validator.isValid('a')).toBeFalse();
});
it('should return false for negative value', () => {
expect(validator.isValid(-1)).toBeFalse();
});
it('should return true for positive value', () => {
expect(validator.isValid(1)).toBeTrue();
});
it('should return true for empty value', () => {
expect(validator.isValid('')).toBeTrue();
});
it('should work for negative string value', () => {
expect(validator.isValid('-1')).toBeFalse();
});
it('should work for positive string value', () => {
expect(validator.isValid('1')).toBeTrue();
});
});

View File

@ -0,0 +1,40 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { CardViewItemValidator } from '../interfaces/card-view.interfaces';
export class CardViewItemPositiveIntValidator implements CardViewItemValidator {
message = 'CORE.CARDVIEW.VALIDATORS.ONLY_POSITIVE_NUMBER';
isValid(value: any | any[]): boolean {
if (Array.isArray(value)) {
return value.every(this.isPositiveNumber);
}
const valueIsNotSet = value === '';
return valueIsNotSet ||
(
!isNaN(value) &&
this.isPositiveNumber(value)
);
}
private isPositiveNumber(value: any): boolean {
return parseInt(value, 10) >= 0;
}
}

View File

@ -16,6 +16,7 @@
*/ */
export * from './card-view-item-int.validator'; export * from './card-view-item-int.validator';
export * from './card-view-item-only-positive-int.validator';
export * from './card-view-item-float.validator'; export * from './card-view-item-float.validator';
export * from './card-view-item-match.valiator'; export * from './card-view-item-match.valiator';
export * from './card-view-item-minmax.valiator'; export * from './card-view-item-minmax.valiator';

View File

@ -24,6 +24,7 @@ export class FormFieldTypes {
static TEXT: string = 'text'; static TEXT: string = 'text';
static STRING: string = 'string'; static STRING: string = 'string';
static INTEGER: string = 'integer'; static INTEGER: string = 'integer';
static DECIMAL: string = 'bigdecimal';
static MULTILINE_TEXT: string = 'multi-line-text'; static MULTILINE_TEXT: string = 'multi-line-text';
static DROPDOWN: string = 'dropdown'; static DROPDOWN: string = 'dropdown';
static HYPERLINK: string = 'hyperlink'; static HYPERLINK: string = 'hyperlink';

View File

@ -31,7 +31,8 @@ import {
MinDateTimeFieldValidator, MinDateTimeFieldValidator,
MaxDateFieldValidator, MaxDateFieldValidator,
MinDateFieldValidator, MinDateFieldValidator,
DateTimeFieldValidator DateTimeFieldValidator,
DecimalFieldValidator
} from './form-field-validator'; } from './form-field-validator';
import { FormFieldModel } from './form-field.model'; import { FormFieldModel } from './form-field.model';
import { FormModel } from './form.model'; import { FormModel } from './form.model';
@ -1117,4 +1118,73 @@ describe('FormFieldValidator', () => {
expect(validator.validate(field)).toBeFalse(); expect(validator.validate(field)).toBeFalse();
}); });
}); });
describe('DecimalFieldValidator', () => {
let decimalValidator: DecimalFieldValidator;
beforeEach(() => {
decimalValidator = new DecimalFieldValidator();
});
it('should validate decimal with correct precision', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DECIMAL,
value: 1.22,
precision: 2
});
expect(decimalValidator.validate(field)).toBeTrue();
});
it('should return true when value is of lower precision', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DECIMAL,
value: 1.2,
precision: 2
});
expect(decimalValidator.validate(field)).toBeTrue();
});
it('should return false when value is of higher precision', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DECIMAL,
value: 1.22,
precision: 1
});
expect(decimalValidator.validate(field)).toBeFalse();
});
it('should validate decimal of wrong precision when value is of type string', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DECIMAL,
value: '1.22',
precision: 1
});
expect(decimalValidator.validate(field)).toBeFalse();
});
it('should return false, when value is a negative number and of correct precission', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DECIMAL,
value: -1.22,
precision: 1
});
expect(decimalValidator.validate(field)).toBeFalse();
});
it('should return true, when value is a positive number and of correct precission', () => {
const field = new FormFieldModel(new FormModel(), {
type: FormFieldTypes.DECIMAL,
value: -1.22,
precision: 3
});
expect(decimalValidator.validate(field)).toBeTrue();
});
});
}); });

View File

@ -48,7 +48,8 @@ export class RequiredFieldValidator implements FormFieldValidator {
FormFieldTypes.DYNAMIC_TABLE, FormFieldTypes.DYNAMIC_TABLE,
FormFieldTypes.DATE, FormFieldTypes.DATE,
FormFieldTypes.DATETIME, FormFieldTypes.DATETIME,
FormFieldTypes.ATTACH_FOLDER FormFieldTypes.ATTACH_FOLDER,
FormFieldTypes.DECIMAL
]; ];
isSupported(field: FormFieldModel): boolean { isSupported(field: FormFieldModel): boolean {
@ -431,6 +432,7 @@ export class MinValueFieldValidator implements FormFieldValidator {
private supportedTypes = [ private supportedTypes = [
FormFieldTypes.NUMBER, FormFieldTypes.NUMBER,
FormFieldTypes.DECIMAL,
FormFieldTypes.AMOUNT FormFieldTypes.AMOUNT
]; ];
@ -461,6 +463,7 @@ export class MaxValueFieldValidator implements FormFieldValidator {
private supportedTypes = [ private supportedTypes = [
FormFieldTypes.NUMBER, FormFieldTypes.NUMBER,
FormFieldTypes.DECIMAL,
FormFieldTypes.AMOUNT FormFieldTypes.AMOUNT
]; ];
@ -553,6 +556,50 @@ export class FixedValueFieldValidator implements FormFieldValidator {
} }
} }
export class DecimalFieldValidator implements FormFieldValidator {
private supportedTypes = [
FormFieldTypes.DECIMAL
];
isSupported(field: FormFieldModel): boolean {
return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.value;
}
validate(field: FormFieldModel): boolean {
const shouldValidateField = this.isSupported(field) && field.isVisible;
if (!shouldValidateField) {
return true;
}
const precision = field.precision;
const fieldValue = field.value;
if (!isNumberValue(fieldValue)) {
field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DECIMAL_NUMBER';
return false;
}
const value = typeof fieldValue === 'string' ? fieldValue : fieldValue.toString();
const valueParts = value.split('.');
const decimalPart = valueParts[1];
if (decimalPart === undefined) {
return true;
}
if (decimalPart.length > precision) {
field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DECIMAL_PRECISION';
field.validationSummary.attributes.set('precision', precision.toString());
return false;
}
return true;
}
}
export const FORM_FIELD_VALIDATORS = [ export const FORM_FIELD_VALIDATORS = [
new RequiredFieldValidator(), new RequiredFieldValidator(),
new NumberFieldValidator(), new NumberFieldValidator(),
@ -567,5 +614,6 @@ export const FORM_FIELD_VALIDATORS = [
new MaxDateFieldValidator(), new MaxDateFieldValidator(),
new FixedValueFieldValidator(), new FixedValueFieldValidator(),
new MinDateTimeFieldValidator(), new MinDateTimeFieldValidator(),
new MaxDateTimeFieldValidator() new MaxDateTimeFieldValidator(),
new DecimalFieldValidator()
]; ];

View File

@ -58,6 +58,7 @@ export class FormFieldModel extends FormWidgetModel {
maxValue: string; maxValue: string;
maxDateRangeValue: number = 0; maxDateRangeValue: number = 0;
minDateRangeValue: number = 0; minDateRangeValue: number = 0;
precision: number;
dynamicDateRangeSelection: boolean; dynamicDateRangeSelection: boolean;
regexPattern: string; regexPattern: string;
options: FormFieldOption[] = []; options: FormFieldOption[] = [];
@ -100,8 +101,10 @@ export class FormFieldModel extends FormWidgetModel {
} }
set value(v: any) { set value(v: any) {
this._value = v; if (v !== this._value) {
this.updateForm(); this._value = v;
this.updateForm();
}
} }
get readOnly(): boolean { get readOnly(): boolean {
@ -200,6 +203,7 @@ export class FormFieldModel extends FormWidgetModel {
this.groupsRestriction = json.groupsRestriction?.groups; this.groupsRestriction = json.groupsRestriction?.groups;
this.variableConfig = json.variableConfig; this.variableConfig = json.variableConfig;
this.schemaDefinition = json.schemaDefinition; this.schemaDefinition = json.schemaDefinition;
this.precision = json.precision;
if (json.placeholder && json.placeholder !== '' && json.placeholder !== 'null') { if (json.placeholder && json.placeholder !== '' && json.placeholder !== 'null') {
this.placeholder = json.placeholder; this.placeholder = json.placeholder;
@ -459,6 +463,10 @@ export class FormFieldModel extends FormWidgetModel {
this.form.values[this.id] = this.enableFractions ? parseFloat(this.value) : parseInt(this.value, 10); this.form.values[this.id] = this.enableFractions ? parseFloat(this.value) : parseInt(this.value, 10);
break; break;
} }
case FormFieldTypes.DECIMAL: {
this.form.values[this.id] = parseFloat(this.value);
break;
};
case FormFieldTypes.BOOLEAN: { case FormFieldTypes.BOOLEAN: {
this.form.values[this.id] = this.value !== null && this.value !== undefined ? this.value : false; this.form.values[this.id] = this.value !== null && this.value !== undefined ? this.value : false;
break; break;

View File

@ -0,0 +1,44 @@
<div
class="adf-textfield adf-decimal-widget {{ field.className }}"
[class.adf-invalid]="!field.isValid && isTouched()"
[class.adf-readonly]="field.readOnly"
[class.adf-left-label-input-container]="field.leftLabels"
>
<div *ngIf="field.leftLabels">
<label class="adf-label adf-left-label" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span>
</label>
</div>
<div>
<mat-form-field [hideRequiredMarker]="true">
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" *ngIf="isRequired()">*</span>
</label>
<input
matInput
class="adf-input"
type="text"
pattern="-?[0-9]*(\.[0-9]*)?"
[id]="field.id"
[required]="isRequired()"
[value]="displayValue"
[(ngModel)]="field.value"
(ngModelChange)="onFieldChanged(field)"
[disabled]="field.readOnly"
[placeholder]="field.placeholder"
[matTooltip]="field.tooltip"
(blur)="markAsTouched()"
matTooltipPosition="above"
matTooltipShowDelay="1000"
/>
</mat-form-field>
<error-widget [error]="field.validationSummary"></error-widget>
<error-widget
*ngIf="isInvalidFieldRequired() && isTouched()"
required="{{ 'FORM.FIELD.REQUIRED' | translate }}">
</error-widget>
</div>
</div>

View File

@ -0,0 +1,9 @@
.adf {
&-decimal-widget {
width: 100%;
.adf-label {
top: 20px;
}
}
}

View File

@ -0,0 +1,175 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { ComponentFixture, TestBed } from '@angular/core/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HarnessLoader } from '@angular/cdk/testing';
import { DecimalWidgetComponent } from './decimal.component';
import { FormService } from '../../../services/form.service';
import { FormFieldModel, FormFieldTypes, FormModel } from '../core';
import { MatInputHarness } from '@angular/material/input/testing';
import { MatTooltipHarness } from '@angular/material/tooltip/testing';
import { MatInputModule } from '@angular/material/input';
import { TranslateModule } from '@ngx-translate/core';
import { CoreTestingModule } from '../../../../testing';
import { FormsModule } from '@angular/forms';
describe('DecimalComponent', () => {
let loader: HarnessLoader;
let widget: DecimalWidgetComponent;
let fixture: ComponentFixture<DecimalWidgetComponent>;
let element: HTMLElement;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreTestingModule,
MatInputModule,
FormsModule
],
declarations: [DecimalWidgetComponent],
providers: [FormService]
}).compileComponents();
fixture = TestBed.createComponent(DecimalWidgetComponent);
widget = fixture.componentInstance;
element = fixture.nativeElement;
loader = TestbedHarnessEnvironment.loader(fixture);
});
describe('when tooltip is set', () => {
beforeEach(() => {
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
type: FormFieldTypes.DECIMAL,
tooltip: 'my custom tooltip'
});
fixture.detectChanges();
});
it('should show tooltip', async () => {
const input = await loader.getHarness(MatInputHarness);
await (await input.host()).hover();
const tooltip = await loader.getHarness(MatTooltipHarness);
expect(await tooltip.getTooltipText()).toBe('my custom tooltip');
});
it('should hide tooltip', async () => {
const input = await loader.getHarness(MatInputHarness);
await (await input.host()).hover();
await (await input.host()).mouseAway();
const tooltip = await loader.getHarness(MatTooltipHarness);
expect(await tooltip.isOpen()).toBe(false);
});
});
describe('when is required', () => {
beforeEach(() => {
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
type: FormFieldTypes.DECIMAL,
required: true
});
fixture.detectChanges();
});
it('should be marked as invalid after interaction', async () => {
const input = await loader.getHarness(MatInputHarness);
expect(fixture.nativeElement.querySelector('.adf-invalid')).toBeFalsy();
const inputHost = await input.host();
await inputHost.blur();
fixture.detectChanges();
const invalidElement = fixture.nativeElement.querySelector('.adf-invalid');
expect(invalidElement).toBeTruthy();
});
it('should be able to display label with asterisk', async () => {
const asterisk = element.querySelector('.adf-asterisk');
expect(asterisk).toBeTruthy();
expect(asterisk?.textContent).toEqual('*');
});
});
describe('when form model has left labels', () => {
it('should have left labels classes on leftLabels true', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: true }), {
id: 'decimal-id',
name: 'decimal-name',
value: '',
type: FormFieldTypes.DECIMAL,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
const widgetContainer = element.querySelector('.adf-left-label-input-container');
expect(widgetContainer).not.toBeNull();
const adfLeftLabel = element.querySelector('.adf-left-label');
expect(adfLeftLabel).not.toBeNull();
});
it('should not have left labels classes on leftLabels false', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id', leftLabels: false }), {
id: 'decimal-id',
name: 'decimal-name',
value: '',
type: FormFieldTypes.DECIMAL,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
const widgetContainer = element.querySelector('.adf-left-label-input-container');
expect(widgetContainer).toBeNull();
const adfLeftLabel = element.querySelector('.adf-left-label');
expect(adfLeftLabel).toBeNull();
});
it('should not have left labels classes on leftLabels not present', async () => {
widget.field = new FormFieldModel(new FormModel({ taskId: 'fake-task-id' }), {
id: 'decimal-id',
name: 'decimal-name',
value: '',
type: FormFieldTypes.DECIMAL,
readOnly: false,
required: true
});
fixture.detectChanges();
await fixture.whenStable();
const widgetContainer = element.querySelector('.adf-left-label-input-container');
expect(widgetContainer).toBeNull();
const adfLeftLabel = element.querySelector('.adf-left-label');
expect(adfLeftLabel).toBeNull();
});
});
});

View File

@ -0,0 +1,49 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { FormService } from '../../../services/form.service';
import { WidgetComponent } from '../widget.component';
@Component({
selector: 'adf-decimal',
templateUrl: './decimal.component.html',
styleUrls: ['./decimal.component.scss'],
host: {
'(click)': 'event($event)',
'(blur)': 'event($event)',
'(change)': 'event($event)',
'(focus)': 'event($event)',
'(focusin)': 'event($event)',
'(focusout)': 'event($event)',
'(input)': 'event($event)',
'(invalid)': 'event($event)',
'(select)': 'event($event)'
},
encapsulation: ViewEncapsulation.None
})
export class DecimalWidgetComponent extends WidgetComponent implements OnInit {
displayValue: number;
constructor(public formService: FormService) {
super(formService);
}
ngOnInit(): void {
this.displayValue = this.field.value;
}
}

View File

@ -30,6 +30,7 @@ import { TextWidgetComponent } from './text/text.widget';
import { DateTimeWidgetComponent } from './date-time/date-time.widget'; import { DateTimeWidgetComponent } from './date-time/date-time.widget';
import { JsonWidgetComponent } from './json/json.widget'; import { JsonWidgetComponent } from './json/json.widget';
import { BaseViewerWidgetComponent } from './base-viewer/base-viewer.widget'; import { BaseViewerWidgetComponent } from './base-viewer/base-viewer.widget';
import { DecimalWidgetComponent } from './decimal/decimal.component';
// core // core
export * from './widget.component'; export * from './widget.component';
@ -39,6 +40,7 @@ export * from './core';
export * from './unknown/unknown.widget'; export * from './unknown/unknown.widget';
export * from './text/text.widget'; export * from './text/text.widget';
export * from './number/number.widget'; export * from './number/number.widget';
export * from './decimal/decimal.component';
export * from './checkbox/checkbox.widget'; export * from './checkbox/checkbox.widget';
export * from './multiline-text/multiline-text.widget'; export * from './multiline-text/multiline-text.widget';
export * from './hyperlink/hyperlink.widget'; export * from './hyperlink/hyperlink.widget';
@ -55,6 +57,7 @@ export const WIDGET_DIRECTIVES: any[] = [
UnknownWidgetComponent, UnknownWidgetComponent,
TextWidgetComponent, TextWidgetComponent,
NumberWidgetComponent, NumberWidgetComponent,
DecimalWidgetComponent,
CheckboxWidgetComponent, CheckboxWidgetComponent,
MultilineTextWidgetComponentComponent, MultilineTextWidgetComponentComponent,
HyperlinkWidgetComponent, HyperlinkWidgetComponent,

View File

@ -35,6 +35,7 @@ export class FormRenderingService extends DynamicComponentMapper {
[FormFieldTypes.TEXT]: DynamicComponentResolver.fromType(widgets.TextWidgetComponent), [FormFieldTypes.TEXT]: DynamicComponentResolver.fromType(widgets.TextWidgetComponent),
[FormFieldTypes.STRING]: DynamicComponentResolver.fromType(widgets.TextWidgetComponent), [FormFieldTypes.STRING]: DynamicComponentResolver.fromType(widgets.TextWidgetComponent),
[FormFieldTypes.INTEGER]: DynamicComponentResolver.fromType(widgets.NumberWidgetComponent), [FormFieldTypes.INTEGER]: DynamicComponentResolver.fromType(widgets.NumberWidgetComponent),
[FormFieldTypes.DECIMAL]: DynamicComponentResolver.fromType(widgets.DecimalWidgetComponent),
[FormFieldTypes.MULTILINE_TEXT]: DynamicComponentResolver.fromType(widgets.MultilineTextWidgetComponentComponent), [FormFieldTypes.MULTILINE_TEXT]: DynamicComponentResolver.fromType(widgets.MultilineTextWidgetComponentComponent),
[FormFieldTypes.BOOLEAN]: DynamicComponentResolver.fromType(widgets.CheckboxWidgetComponent), [FormFieldTypes.BOOLEAN]: DynamicComponentResolver.fromType(widgets.CheckboxWidgetComponent),
[FormFieldTypes.DATE]: DynamicComponentResolver.fromType(widgets.DateWidgetComponent), [FormFieldTypes.DATE]: DynamicComponentResolver.fromType(widgets.DateWidgetComponent),

View File

@ -52,6 +52,8 @@
"NO_FILE_ATTACHED": "No file attached", "NO_FILE_ATTACHED": "No file attached",
"VALIDATOR": { "VALIDATOR": {
"INVALID_NUMBER": "Use a different number format", "INVALID_NUMBER": "Use a different number format",
"INVALID_DECIMAL_NUMBER": "Use a decimal number format",
"INVALID_DECIMAL_PRECISION": "Incorrect decimal value, there should be a maximum of {{precision}} digits after the decimal point.",
"INVALID_DATE": "Use a different date format", "INVALID_DATE": "Use a different date format",
"INVALID_VALUE": "Enter a different value", "INVALID_VALUE": "Enter a different value",
"NOT_GREATER_THAN": "Can't be greater than {{ maxValue }}", "NOT_GREATER_THAN": "Can't be greater than {{ maxValue }}",
@ -192,7 +194,8 @@
"INT_VALIDATION_ERROR": "Use an integer format", "INT_VALIDATION_ERROR": "Use an integer format",
"LENGTH_VALIDATION_ERROR": "Value should be between {{ minLength }} and {{ maxLength }} in length", "LENGTH_VALIDATION_ERROR": "Value should be between {{ minLength }} and {{ maxLength }} in length",
"MATCH_VALIDATION_ERROR": "Value doesn't match pattern: {{ expression }}", "MATCH_VALIDATION_ERROR": "Value doesn't match pattern: {{ expression }}",
"MINMAX_VALIDATION_ERROR": "Value should be between {{ minValue }} and {{ maxValue }}" "MINMAX_VALIDATION_ERROR": "Value should be between {{ minValue }} and {{ maxValue }}",
"ONLY_POSITIVE_NUMBER": "Only positive value is allowed"
}, },
"MORE": "More" "MORE": "More"
}, },