Skip to content

Commit cc17d36

Browse files
authored
feat(editors): add min/max length options to text editors (#542)
- add a text length counter (currentTextLength/maxLength) to the LongText Editor - the new `minLenght`, `maxLength` options can be used by the following Editors (autoComplete, longText, text) - also flip the position of the Cancel/Save buttons, Cancel should be on the left and Save on the right
1 parent 3578d9e commit cc17d36

13 files changed

+494
-48
lines changed

src/app/examples/grid-angular.component.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ export class GridAngularComponent implements OnInit {
9292
sortable: true,
9393
type: FieldType.string,
9494
editor: {
95-
model: Editors.longText
95+
model: Editors.longText,
96+
minLength: 5,
97+
maxLength: 255,
9698
},
9799
onCellChange: (e: Event, args: OnEventArgs) => {
98100
console.log(args);

src/app/modules/angular-slickgrid/constants.ts

+5
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,9 @@ export class Constants {
5959
static readonly VALIDATION_EDITOR_NUMBER_MIN = 'Please enter a valid number that is greater than {{minValue}}';
6060
static readonly VALIDATION_EDITOR_NUMBER_MIN_INCLUSIVE = 'Please enter a valid number that is greater than or equal to {{minValue}}';
6161
static readonly VALIDATION_EDITOR_DECIMAL_BETWEEN = 'Please enter a valid number with a maximum of {{maxDecimal}} decimals';
62+
static readonly VALIDATION_EDITOR_TEXT_LENGTH_BETWEEN = 'Please make sure your text length is between {{minLength}} and {{maxLength}} characters';
63+
static readonly VALIDATION_EDITOR_TEXT_MAX_LENGTH = 'Please make sure your text is less than {{maxLength}} characters';
64+
static readonly VALIDATION_EDITOR_TEXT_MAX_LENGTH_INCLUSIVE = 'Please make sure your text is less than or equal to {{maxLength}} characters';
65+
static readonly VALIDATION_EDITOR_TEXT_MIN_LENGTH = 'Please make sure your text is more than {{minLength}} character(s)';
66+
static readonly VALIDATION_EDITOR_TEXT_MIN_LENGTH_INCLUSIVE = 'Please make sure your text is at least {{minLength}} character(s)';
6267
}

src/app/modules/angular-slickgrid/editorValidators/integerValidator.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,20 @@ export function integerValidator(inputValue: any, options: IntegerValidatorOptio
3838
isValid = false;
3939
outputMsg = errorMsg || Constants.VALIDATION_EDITOR_VALID_INTEGER;
4040
} else if (minValue !== undefined && maxValue !== undefined && intNumber !== null && ((operatorConditionalType === 'exclusive' && (intNumber <= minValue || intNumber >= maxValue)) || (operatorConditionalType === 'inclusive' && (intNumber < minValue || intNumber > maxValue)))) {
41-
// MIN & MAX Values provided
41+
// MIN & MAX Values provided (between)
4242
// when decimal value is bigger than 0, we only accept the decimal values as that value set
4343
// for example if we set decimalPlaces to 2, we will only accept numbers between 0 and 2 decimals
4444
isValid = false;
4545
outputMsg = errorMsg || Constants.VALIDATION_EDITOR_INTEGER_BETWEEN.replace(/{{minValue}}|{{maxValue}}/gi, (matched) => mapValidation[matched]);
4646
} else if (minValue !== undefined && intNumber !== null && ((operatorConditionalType === 'exclusive' && intNumber <= minValue) || (operatorConditionalType === 'inclusive' && intNumber !== null && intNumber < minValue))) {
4747
// MIN VALUE ONLY
48-
// when decimal value is bigger than 0, we only accept the decimal values as that value set
49-
// for example if we set decimalPlaces to 2, we will only accept numbers between 0 and 2 decimals
48+
// when decimal value has to be higher then provided minValue
5049
isValid = false;
5150
const defaultErrorMsg = operatorConditionalType === 'inclusive' ? Constants.VALIDATION_EDITOR_INTEGER_MIN_INCLUSIVE : Constants.VALIDATION_EDITOR_INTEGER_MIN;
5251
outputMsg = errorMsg || defaultErrorMsg.replace(/{{minValue}}/gi, (matched) => mapValidation[matched]);
5352
} else if (maxValue !== undefined && intNumber !== null && ((operatorConditionalType === 'exclusive' && intNumber >= maxValue) || (operatorConditionalType === 'inclusive' && intNumber !== null && intNumber > maxValue))) {
5453
// MAX VALUE ONLY
55-
// when decimal value is bigger than 0, we only accept the decimal values as that value set
56-
// for example if we set decimalPlaces to 2, we will only accept numbers between 0 and 2 decimals
54+
// when decimal value has to be lower then provided maxValue
5755
isValid = false;
5856
const defaultErrorMsg = operatorConditionalType === 'inclusive' ? Constants.VALIDATION_EDITOR_INTEGER_MAX_INCLUSIVE : Constants.VALIDATION_EDITOR_INTEGER_MAX;
5957
outputMsg = errorMsg || defaultErrorMsg.replace(/{{maxValue}}/gi, (matched) => mapValidation[matched]);

src/app/modules/angular-slickgrid/editorValidators/textValidator.ts

+34-6
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,53 @@ import { EditorValidator } from '../models/editorValidator.interface';
55
interface TextValidatorOptions {
66
editorArgs: any;
77
errorMessage?: string;
8+
minLength?: number;
9+
maxLength?: number;
10+
operatorConditionalType?: 'inclusive' | 'exclusive';
811
required?: boolean;
912
validator?: EditorValidator;
1013
}
1114

1215
export function textValidator(inputValue: any, options: TextValidatorOptions): EditorValidatorOutput {
13-
const isRequired = options.required;
1416
const errorMsg = options.errorMessage;
17+
const isRequired = options.required;
18+
const minLength = options.minLength;
19+
const maxLength = options.maxLength;
20+
const operatorConditionalType = options.operatorConditionalType || 'inclusive';
21+
const mapValidation = {
22+
'{{minLength}}': minLength,
23+
'{{maxLength}}': maxLength
24+
};
25+
let isValid = true;
26+
let outputMsg = '';
27+
const inputValueLength = inputValue.length;
1528

1629
if (options.validator) {
1730
return options.validator(inputValue, options.editorArgs);
1831
}
1932

2033
// by default the editor is almost always valid (except when it's required but not provided)
2134
if (isRequired && inputValue === '') {
22-
return {
23-
valid: false,
24-
msg: errorMsg || Constants.VALIDATION_REQUIRED_FIELD
25-
};
35+
isValid = false;
36+
outputMsg = errorMsg || Constants.VALIDATION_REQUIRED_FIELD;
37+
} else if (minLength !== undefined && maxLength !== undefined && ((operatorConditionalType === 'exclusive' && (inputValueLength <= minLength || inputValueLength >= maxLength)) || (operatorConditionalType === 'inclusive' && (inputValueLength < minLength || inputValueLength > maxLength)))) {
38+
// MIN & MAX Length provided
39+
// make sure text length is between minLength and maxLength
40+
isValid = false;
41+
outputMsg = errorMsg || Constants.VALIDATION_EDITOR_TEXT_LENGTH_BETWEEN.replace(/{{minLength}}|{{maxLength}}/gi, (matched) => mapValidation[matched]);
42+
} else if (minLength !== undefined && inputValueLength !== null && ((operatorConditionalType === 'exclusive' && inputValueLength <= minLength) || (operatorConditionalType === 'inclusive' && inputValueLength !== null && inputValueLength < minLength))) {
43+
// MIN Length ONLY
44+
// when text length is shorter than minLength
45+
isValid = false;
46+
const defaultErrorMsg = operatorConditionalType === 'inclusive' ? Constants.VALIDATION_EDITOR_TEXT_MIN_LENGTH_INCLUSIVE : Constants.VALIDATION_EDITOR_TEXT_MIN_LENGTH;
47+
outputMsg = errorMsg || defaultErrorMsg.replace(/{{minLength}}/gi, (matched) => mapValidation[matched]);
48+
} else if (maxLength !== undefined && inputValueLength !== null && ((operatorConditionalType === 'exclusive' && inputValueLength >= maxLength) || (operatorConditionalType === 'inclusive' && inputValueLength !== null && inputValueLength > maxLength))) {
49+
// MAX Length ONLY
50+
// when text length is longer than minLength
51+
isValid = false;
52+
const defaultErrorMsg = operatorConditionalType === 'inclusive' ? Constants.VALIDATION_EDITOR_TEXT_MAX_LENGTH_INCLUSIVE : Constants.VALIDATION_EDITOR_TEXT_MAX_LENGTH;
53+
outputMsg = errorMsg || defaultErrorMsg.replace(/{{maxLength}}/gi, (matched) => mapValidation[matched]);
2654
}
2755

28-
return { valid: true, msg: null };
56+
return { valid: isValid, msg: outputMsg };
2957
}

src/app/modules/angular-slickgrid/editors/__tests__/autoCompleteEditor.spec.ts

+120-4
Original file line numberDiff line numberDiff line change
@@ -382,20 +382,136 @@ describe('AutoCompleteEditor', () => {
382382
});
383383

384384
describe('validate method', () => {
385-
it('should validate and return False when field is required and field is an empty string', () => {
385+
it('should return False when field is required and field is empty', () => {
386386
mockColumn.internalColumnEditor.required = true;
387387
editor = new AutoCompleteEditor(editorArguments);
388388
const validation = editor.validate('');
389389

390390
expect(validation).toEqual({ valid: false, msg: 'Field is required' });
391391
});
392392

393-
it('should validate and return True when field is required and field is a valid input value', () => {
393+
it('should return True when field is required and input is a valid input value', () => {
394394
mockColumn.internalColumnEditor.required = true;
395395
editor = new AutoCompleteEditor(editorArguments);
396-
const validation = editor.validate('gender');
396+
const validation = editor.validate('text');
397397

398-
expect(validation).toEqual({ valid: true, msg: null });
398+
expect(validation).toEqual({ valid: true, msg: '' });
399+
});
400+
401+
it('should return False when field is lower than a minLength defined', () => {
402+
mockColumn.internalColumnEditor.minLength = 5;
403+
editor = new AutoCompleteEditor(editorArguments);
404+
const validation = editor.validate('text');
405+
406+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is at least 5 character(s)' });
407+
});
408+
409+
it('should return False when field is lower than a minLength defined using exclusive operator', () => {
410+
mockColumn.internalColumnEditor.minLength = 5;
411+
mockColumn.internalColumnEditor.operatorConditionalType = 'exclusive';
412+
editor = new AutoCompleteEditor(editorArguments);
413+
const validation = editor.validate('text');
414+
415+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is more than 5 character(s)' });
416+
});
417+
418+
it('should return True when field is equal to the minLength defined', () => {
419+
mockColumn.internalColumnEditor.minLength = 4;
420+
editor = new AutoCompleteEditor(editorArguments);
421+
const validation = editor.validate('text');
422+
423+
expect(validation).toEqual({ valid: true, msg: '' });
424+
});
425+
426+
it('should return False when field is greater than a maxLength defined', () => {
427+
mockColumn.internalColumnEditor.maxLength = 10;
428+
editor = new AutoCompleteEditor(editorArguments);
429+
const validation = editor.validate('text is 16 chars');
430+
431+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than or equal to 10 characters' });
432+
});
433+
434+
it('should return False when field is greater than a maxLength defined using exclusive operator', () => {
435+
mockColumn.internalColumnEditor.maxLength = 10;
436+
mockColumn.internalColumnEditor.operatorConditionalType = 'exclusive';
437+
editor = new AutoCompleteEditor(editorArguments);
438+
const validation = editor.validate('text is 16 chars');
439+
440+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than 10 characters' });
441+
});
442+
443+
it('should return True when field is equal to the maxLength defined', () => {
444+
mockColumn.internalColumnEditor.maxLength = 16;
445+
editor = new AutoCompleteEditor(editorArguments);
446+
const validation = editor.validate('text is 16 chars');
447+
448+
expect(validation).toEqual({ valid: true, msg: '' });
449+
});
450+
451+
it('should return True when field is equal to the maxLength defined and "operatorType" is set to "inclusive"', () => {
452+
mockColumn.internalColumnEditor.maxLength = 16;
453+
mockColumn.internalColumnEditor.operatorConditionalType = 'inclusive';
454+
editor = new AutoCompleteEditor(editorArguments);
455+
const validation = editor.validate('text is 16 chars');
456+
457+
expect(validation).toEqual({ valid: true, msg: '' });
458+
});
459+
460+
it('should return False when field is equal to the maxLength defined but "operatorType" is set to "exclusive"', () => {
461+
mockColumn.internalColumnEditor.maxLength = 16;
462+
mockColumn.internalColumnEditor.operatorConditionalType = 'exclusive';
463+
editor = new AutoCompleteEditor(editorArguments);
464+
const validation = editor.validate('text is 16 chars');
465+
466+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than 16 characters' });
467+
});
468+
469+
it('should return False when field is not between minLength & maxLength defined', () => {
470+
mockColumn.internalColumnEditor.minLength = 0;
471+
mockColumn.internalColumnEditor.maxLength = 10;
472+
editor = new AutoCompleteEditor(editorArguments);
473+
const validation = editor.validate('text is 16 chars');
474+
475+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text length is between 0 and 10 characters' });
476+
});
477+
478+
it('should return True when field is is equal to maxLength defined when both min/max values are defined', () => {
479+
mockColumn.internalColumnEditor.minLength = 0;
480+
mockColumn.internalColumnEditor.maxLength = 16;
481+
editor = new AutoCompleteEditor(editorArguments);
482+
const validation = editor.validate('text is 16 chars');
483+
484+
expect(validation).toEqual({ valid: true, msg: '' });
485+
});
486+
487+
it('should return True when field is is equal to minLength defined when "operatorType" is set to "inclusive" and both min/max values are defined', () => {
488+
mockColumn.internalColumnEditor.minLength = 4;
489+
mockColumn.internalColumnEditor.maxLength = 15;
490+
mockColumn.internalColumnEditor.operatorConditionalType = 'inclusive';
491+
editor = new AutoCompleteEditor(editorArguments);
492+
const validation = editor.validate('text');
493+
494+
expect(validation).toEqual({ valid: true, msg: '' });
495+
});
496+
497+
it('should return False when field is equal to maxLength but "operatorType" is set to "exclusive" when both min/max lengths are defined', () => {
498+
mockColumn.internalColumnEditor.minLength = 4;
499+
mockColumn.internalColumnEditor.maxLength = 16;
500+
mockColumn.internalColumnEditor.operatorConditionalType = 'exclusive';
501+
editor = new AutoCompleteEditor(editorArguments);
502+
const validation1 = editor.validate('text is 16 chars');
503+
const validation2 = editor.validate('text');
504+
505+
expect(validation1).toEqual({ valid: false, msg: 'Please make sure your text length is between 4 and 16 characters' });
506+
expect(validation2).toEqual({ valid: false, msg: 'Please make sure your text length is between 4 and 16 characters' });
507+
});
508+
509+
it('should return False when field is greater than a maxValue defined', () => {
510+
mockColumn.internalColumnEditor.maxLength = 10;
511+
editor = new AutoCompleteEditor(editorArguments);
512+
const validation = editor.validate('Task is longer than 10 chars');
513+
514+
expect(validation).toEqual({ valid: false, msg: 'Please make sure your text is less than or equal to 10 characters' });
399515
});
400516
});
401517

0 commit comments

Comments
 (0)