mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-26 17:24:56 +00:00
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:
parent
300d46dc7e
commit
16005ef298
@ -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);
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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';
|
||||||
|
@ -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';
|
||||||
|
@ -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();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -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()
|
||||||
];
|
];
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
@ -0,0 +1,9 @@
|
|||||||
|
.adf {
|
||||||
|
&-decimal-widget {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.adf-label {
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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),
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user