mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ACA-3742] Metadata - constraints validation (#5908)
* card min max value validator * card match value validator * card value length validator * map validators to constraint type * add minmax * update exported validators * register validators based on constraint type * translate errors with parameters * tests
This commit is contained in:
@@ -129,7 +129,8 @@
|
||||
"uploadfileform",
|
||||
"processwithstarteventform",
|
||||
"processstring",
|
||||
"typeahed"
|
||||
"typeahed",
|
||||
"minmax"
|
||||
],
|
||||
"dictionaries": [
|
||||
"html",
|
||||
|
@@ -55,7 +55,7 @@
|
||||
class="adf-textitem-editable-error"
|
||||
*ngIf="hasErrors">
|
||||
<ul>
|
||||
<li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li>
|
||||
<li *ngFor="let error of errors">{{ error.message | translate: error }}</li>
|
||||
</ul>
|
||||
</mat-error>
|
||||
|
||||
|
@@ -27,6 +27,7 @@ import { MatChipsModule } from '@angular/material/chips';
|
||||
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface';
|
||||
|
||||
describe('CardViewTextItemComponent', () => {
|
||||
|
||||
@@ -441,7 +442,7 @@ describe('CardViewTextItemComponent', () => {
|
||||
});
|
||||
|
||||
it('should set the errorMessages properly if the editedValue is invalid', async () => {
|
||||
const expectedErrorMessages = ['Something went wrong'];
|
||||
const expectedErrorMessages = [{ message: 'Something went wrong' } as CardViewItemValidator];
|
||||
component.property.isValid = () => false;
|
||||
component.property.getValidationErrors = () => expectedErrorMessages;
|
||||
|
||||
@@ -450,7 +451,7 @@ describe('CardViewTextItemComponent', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
updateTextField(component.property.key, 'updated-value');
|
||||
expect(component.errorMessages).toBe(expectedErrorMessages);
|
||||
expect(component.errors).toBe(expectedErrorMessages);
|
||||
});
|
||||
|
||||
it('should update the property value after a successful update attempt', async () => {
|
||||
|
@@ -22,6 +22,7 @@ import { BaseCardView } from '../base-card-view';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
||||
import { TranslationService } from '../../../services/translation.service';
|
||||
import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface';
|
||||
|
||||
export const DEFAULT_SEPARATOR = ', ';
|
||||
const templateTypes = {
|
||||
@@ -55,7 +56,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
||||
multiValueSeparator: string = DEFAULT_SEPARATOR;
|
||||
|
||||
editedValue: string | string[];
|
||||
errorMessages: string[];
|
||||
errors: CardViewItemValidator[];
|
||||
templateType: string;
|
||||
|
||||
constructor(cardViewUpdateService: CardViewUpdateService,
|
||||
@@ -92,7 +93,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
||||
}
|
||||
|
||||
private resetErrorMessages() {
|
||||
this.errorMessages = [];
|
||||
this.errors = [];
|
||||
}
|
||||
|
||||
update(): void {
|
||||
@@ -102,7 +103,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
||||
this.property.value = updatedValue;
|
||||
this.resetErrorMessages();
|
||||
} else {
|
||||
this.errorMessages = this.property.getValidationErrors(this.editedValue);
|
||||
this.errors = this.property.getValidationErrors(this.editedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +177,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
||||
}
|
||||
|
||||
get hasErrors(): boolean {
|
||||
return this.errorMessages && this.errorMessages.length > 0;
|
||||
return (!!this.errors?.length) ?? false;
|
||||
}
|
||||
|
||||
get isChipViewEnabled(): boolean {
|
||||
|
@@ -91,7 +91,7 @@ describe('CardViewBaseItemModel', () => {
|
||||
const isValid = itemModel.isValid('test-against-this');
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
expect(itemModel.getValidationErrors('test-against-this')).toEqual(['validator 1', 'validator 3']);
|
||||
expect(itemModel.getValidationErrors('test-against-this')).toEqual([validator1, validator3 ]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
import { CardViewItemProperties, CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
import validatorsMap from '../validators/validators.map';
|
||||
|
||||
export abstract class CardViewBaseItemModel {
|
||||
label: string;
|
||||
@@ -38,6 +39,14 @@ export abstract class CardViewBaseItemModel {
|
||||
this.icon = cardViewItemProperties.icon || '';
|
||||
this.validators = cardViewItemProperties.validators || [];
|
||||
this.data = cardViewItemProperties.data || null;
|
||||
|
||||
if (cardViewItemProperties?.constraints?.length ?? 0) {
|
||||
for (const constraint of cardViewItemProperties.constraints) {
|
||||
if (constraint.type !== 'LIST') {
|
||||
this.validators.push(validatorsMap[constraint.type.toLowerCase()](constraint.parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
@@ -54,11 +63,11 @@ export abstract class CardViewBaseItemModel {
|
||||
.reduce((isValidUntilNow, isValid) => isValidUntilNow && isValid, true);
|
||||
}
|
||||
|
||||
getValidationErrors(value): string[] {
|
||||
getValidationErrors(value): CardViewItemValidator[] {
|
||||
if (!this.validators.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.validators.filter((validator) => !validator.isValid(value)).map((validator) => validator.message);
|
||||
return this.validators.filter((validator) => !validator.isValid(value)).map((validator) => validator);
|
||||
}
|
||||
}
|
||||
|
@@ -56,4 +56,24 @@ describe('CardViewFloatItemModel', () => {
|
||||
expect(itemModel.isValid('42.3')).toBe(true, 'For "42.3" it should be true');
|
||||
expect(itemModel.isValid('test')).toBe(false, 'For "test" it should be false');
|
||||
});
|
||||
|
||||
it('should validate based on defined constraints', () => {
|
||||
const constrainedProperties = {
|
||||
label: 'Tribe',
|
||||
value: '42.42',
|
||||
key: 'tribe',
|
||||
dataType: 'd:float',
|
||||
constraints: [{
|
||||
id: 'constraint-id',
|
||||
type: 'MINMAX',
|
||||
parameters: { minValue: 10, maxValue: 40 }
|
||||
}]
|
||||
};
|
||||
|
||||
const itemModel = new CardViewFloatItemModel(constrainedProperties);
|
||||
expect(itemModel.isValid(itemModel.value)).toBe(false, '42.42 is bigger than maximum allowed');
|
||||
|
||||
itemModel.value = '9.1';
|
||||
expect(itemModel.isValid(itemModel.value)).toBe(false, '9.1 is less than minimum allowed');
|
||||
});
|
||||
});
|
||||
|
@@ -56,4 +56,24 @@ describe('CardViewIntItemModel', () => {
|
||||
expect(itemModel.isValid('42.3')).toBe(false, 'For "42.3" it should be false');
|
||||
expect(itemModel.isValid('test')).toBe(false, 'For "test" it should be false');
|
||||
});
|
||||
|
||||
it('should validate based on defined constraints', () => {
|
||||
const constrainedProperties = {
|
||||
label: 'Tribe',
|
||||
value: '20',
|
||||
key: 'tribe',
|
||||
dataType: 'd:float',
|
||||
constraints: [{
|
||||
id: 'constraint-id',
|
||||
type: 'MINMAX',
|
||||
parameters: { minValue: 10, maxValue: 15 }
|
||||
}]
|
||||
};
|
||||
|
||||
const itemModel = new CardViewIntItemModel(constrainedProperties);
|
||||
expect(itemModel.isValid(itemModel.value)).toBe(false, '20 is bigger than maximum allowed');
|
||||
|
||||
itemModel.value = '5';
|
||||
expect(itemModel.isValid(itemModel.value)).toBe(false, '5 is less than minimum allowed');
|
||||
});
|
||||
});
|
||||
|
@@ -85,4 +85,24 @@ describe('CardViewTextItemModel', () => {
|
||||
expect(itemModel.displayValue).toBe('testpiped-testpiped-testpiped-Banuk-1-2-3');
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate based on defined constraints', () => {
|
||||
const constrainedProperties = {
|
||||
label: 'Tribe',
|
||||
value: 'test',
|
||||
key: 'tribe',
|
||||
dataType: 'd:text',
|
||||
constraints: [{
|
||||
id: 'constraint-id',
|
||||
type: 'REGEX',
|
||||
parameters: { expression: '^(?=.*test).*' }
|
||||
}]
|
||||
};
|
||||
|
||||
const itemModel = new CardViewTextItemModel(constrainedProperties);
|
||||
expect(itemModel.isValid(itemModel.value)).toBe(true);
|
||||
|
||||
itemModel.value = 'dummy';
|
||||
expect(itemModel.isValid(itemModel.value)).toBe(false, '`dummy` is not a constraint expression pattern');
|
||||
});
|
||||
});
|
||||
|
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export interface LengthValidatorParams {
|
||||
minLength: number;
|
||||
maxLength: number;
|
||||
}
|
||||
|
||||
export class CardViewItemLengthValidator implements CardViewItemValidator {
|
||||
message = 'CORE.CARDVIEW.VALIDATORS.LENGTH_VALIDATION_ERROR';
|
||||
|
||||
constructor(private minLength: number, private maxLength: number) {}
|
||||
|
||||
isValid(value: string = ''): boolean {
|
||||
const stringLength = value.length;
|
||||
return stringLength >= this.minLength && stringLength <= this.maxLength;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export interface MatchValidatorParams {
|
||||
expression: string;
|
||||
flags?: string;
|
||||
}
|
||||
|
||||
export class CardViewItemMatchValidator implements CardViewItemValidator {
|
||||
message = 'CORE.CARDVIEW.VALIDATORS.MATCH_VALIDATION_ERROR';
|
||||
|
||||
constructor(private expression: string, private flags?: string) {}
|
||||
|
||||
isValid(value: string): boolean {
|
||||
const regex = new RegExp(this.expression, this.flags);
|
||||
return value === '' || regex.test(value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
import { CardViewItemIntValidator } from './card-view-item-int.validator';
|
||||
|
||||
export interface MinMaxValidatorParams {
|
||||
minValue: number;
|
||||
maxValue: number;
|
||||
}
|
||||
|
||||
export class CardViewItemMinMaxValidator implements CardViewItemValidator {
|
||||
message = 'CORE.CARDVIEW.VALIDATORS.MINMAX_VALIDATION_ERROR';
|
||||
private intValidator: CardViewItemIntValidator;
|
||||
|
||||
constructor(private minValue: number, private maxValue: number) {
|
||||
this.intValidator = new CardViewItemIntValidator();
|
||||
}
|
||||
|
||||
isValid(value: number): boolean {
|
||||
return this.intValidator.isValid(value) && (value >= this.minValue && value <= this.maxValue);
|
||||
}
|
||||
}
|
@@ -17,3 +17,7 @@
|
||||
|
||||
export * from './card-view-item-int.validator';
|
||||
export * from './card-view-item-float.validator';
|
||||
export * from './card-view-item-match.valiator';
|
||||
export * from './card-view-item-minmax.valiator';
|
||||
export * from './card-view-item-length.valiator';
|
||||
export * from './validators.map';
|
||||
|
27
lib/core/card-view/validators/validators.map.ts
Normal file
27
lib/core/card-view/validators/validators.map.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { CardViewItemMatchValidator, MatchValidatorParams } from './card-view-item-match.valiator';
|
||||
import { CardViewItemMinMaxValidator, MinMaxValidatorParams } from './card-view-item-minmax.valiator';
|
||||
import { CardViewItemLengthValidator, LengthValidatorParams } from './card-view-item-length.valiator';
|
||||
|
||||
const validators = {
|
||||
minmax: (parameters: MinMaxValidatorParams) => new CardViewItemMinMaxValidator(parameters.minValue, parameters.maxValue),
|
||||
regex: (parameters: MatchValidatorParams) => new CardViewItemMatchValidator(parameters.expression),
|
||||
length: (parameters: LengthValidatorParams) => new CardViewItemLengthValidator(parameters.minLength, parameters.maxLength)
|
||||
};
|
||||
export default validators;
|
@@ -175,7 +175,10 @@
|
||||
"NONE": "None",
|
||||
"VALIDATORS": {
|
||||
"FLOAT_VALIDATION_ERROR": "Use a number format",
|
||||
"INT_VALIDATION_ERROR": "Use an integer format"
|
||||
"INT_VALIDATION_ERROR": "Use an integer format",
|
||||
"LENGTH_VALIDATION_ERROR": "Value should be minimum {{ minLength }} and maximum {{ maxLength }} in length",
|
||||
"MATCH_VALIDATION_ERROR": "Value doesn't match pattern: {{ expression }}",
|
||||
"MINMAX_VALIDATION_ERROR": "Value should be between minimum {{ minValue }} and maximum {{ maxValue }}"
|
||||
},
|
||||
"MORE": "More"
|
||||
},
|
||||
|
Reference in New Issue
Block a user