diff --git a/CHANGELOG.md b/CHANGELOG.md index fbecc764..9def9397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 17.0.5(2024-03-25) + +### Fix + +https://github.com/JsDaddy/ngx-mask/issues/1295 +https://github.com/JsDaddy/ngx-mask/issues/1292 +https://github.com/JsDaddy/ngx-mask/issues/1314 +https://github.com/JsDaddy/ngx-mask/issues/1310 +https://github.com/JsDaddy/ngx-mask/issues/1304 +https://github.com/JsDaddy/ngx-mask/issues/1308 +https://github.com/JsDaddy/ngx-mask/issues/1299 + # 17.0.4(2023-12-01) ### Feat diff --git a/USAGE.md b/USAGE.md index b1db0e93..5065f52f 100644 --- a/USAGE.md +++ b/USAGE.md @@ -17,7 +17,7 @@ Also, you can use mask pipe. You could path any valid config options, for example thousandSeparator and suffix ```html -{{value | mask: 'separator': {thousandSeparator: ',', suffix: ' sm'}} +{{value | mask: 'separator': { thousandSeparator: ',', suffix: ' sm' } }} ``` ### Examples diff --git a/projects/ngx-mask-lib/package.json b/projects/ngx-mask-lib/package.json index 52f4709d..3e6c603a 100644 --- a/projects/ngx-mask-lib/package.json +++ b/projects/ngx-mask-lib/package.json @@ -1,6 +1,6 @@ { "name": "ngx-mask", - "version": "17.0.4", + "version": "17.0.5", "description": "awesome ngx mask", "keywords": [ "ng2-mask", diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask-applier.service.ts b/projects/ngx-mask-lib/src/lib/ngx-mask-applier.service.ts index fdb571b7..3cd2b535 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask-applier.service.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask-applier.service.ts @@ -173,11 +173,12 @@ export class NgxMaskApplierService { inputValue.length )}`; } - let value = ''; this.allowNegativeNumbers && inputValue.slice(cursor, cursor + 1) === MaskExpression.MINUS - ? (value = inputValue.slice(cursor + 1, cursor + inputValue.length)) + ? (value = + MaskExpression.MINUS + + inputValue.slice(cursor + 1, cursor + inputValue.length)) : (value = inputValue); if (this.percentage(value)) { result = this._splitPercentZero(inputValue); @@ -718,6 +719,7 @@ export class NgxMaskApplierService { if (backspaced) { onlySpecial = inputArray.every((char) => this.specialCharacters.includes(char)); } + let res = `${this.prefix}${onlySpecial ? MaskExpression.EMPTY_STRING : result}${ this.showMaskTyped ? '' : this.suffix }`; @@ -804,7 +806,11 @@ export class NgxMaskApplierService { private percentage = (str: string): boolean => { const sanitizedStr = str.replace(',', '.'); - const value = Number(sanitizedStr); + const value = Number( + this.allowNegativeNumbers && str.includes(MaskExpression.MINUS) + ? sanitizedStr.slice(1, str.length) + : sanitizedStr + ); return !isNaN(value) && value >= 0 && value <= 100; }; @@ -848,7 +854,6 @@ export class NgxMaskApplierService { const precisionRegEx = new RegExp( this._charToRegExpExpression(decimalMarker) + `\\d{${precision}}.*$` ); - const precisionMatch: RegExpMatchArray | null = inputValue.match(precisionRegEx); const precisionMatchLength: number = (precisionMatch && precisionMatch[0]?.length) ?? 0; if (precisionMatchLength - 1 > precision) { @@ -929,6 +934,9 @@ export class NgxMaskApplierService { } private _splitPercentZero(value: string): string { + if (value === MaskExpression.MINUS && this.allowNegativeNumbers) { + return value; + } const decimalIndex = typeof this.decimalMarker === 'string' ? value.indexOf(this.decimalMarker) diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts b/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts index e6fbd527..7a11d442 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask.directive.ts @@ -3,11 +3,11 @@ import { Directive, EventEmitter, HostListener, + Input, OnChanges, Output, SimpleChanges, inject, - input, } from '@angular/core'; import { ControlValueAccessor, @@ -42,30 +42,54 @@ import { MaskExpression } from './ngx-mask-expression.enum'; exportAs: 'mask,ngxMask', }) export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Validator { - public maskExpression = input('', { alias: 'mask' }); - public specialCharacters = input([]); - public patterns = input({}); - public prefix = input(''); - public suffix = input(''); - public thousandSeparator = input(' '); - public decimalMarker = input('.'); - public dropSpecialCharacters = input(null); - public hiddenInput = input(null); - public showMaskTyped = input(null); - public placeHolderCharacter = input(null); - public shownMaskExpression = input(null); - public showTemplate = input(null); - public clearIfNotMatch = input(null); - public validation = input(null); - public separatorLimit = input(null); - public allowNegativeNumbers = input(null); - public leadZeroDateTime = input(null); - public leadZero = input(null); - public triggerOnMaskChange = input(null); - public apm = input(null); - public keepCharacterPositions = input(null); - public inputTransformFn = input(null); - public outputTransformFn = input(null); + // eslint-disable-next-line @angular-eslint/no-input-rename + @Input('mask') public maskExpression: string | undefined | null = ''; + + @Input() public specialCharacters: IConfig['specialCharacters'] = []; + + @Input() public patterns: IConfig['patterns'] = {}; + + @Input() public prefix: IConfig['prefix'] = ''; + + @Input() public suffix: IConfig['suffix'] = ''; + + @Input() public thousandSeparator: IConfig['thousandSeparator'] = ' '; + + @Input() public decimalMarker: IConfig['decimalMarker'] = '.'; + + @Input() public dropSpecialCharacters: IConfig['dropSpecialCharacters'] | null = null; + + @Input() public hiddenInput: IConfig['hiddenInput'] | null = null; + + @Input() public showMaskTyped: IConfig['showMaskTyped'] | null = null; + + @Input() public placeHolderCharacter: IConfig['placeHolderCharacter'] | null = null; + + @Input() public shownMaskExpression: IConfig['shownMaskExpression'] | null = null; + + @Input() public showTemplate: IConfig['showTemplate'] | null = null; + + @Input() public clearIfNotMatch: IConfig['clearIfNotMatch'] | null = null; + + @Input() public validation: IConfig['validation'] | null = null; + + @Input() public separatorLimit: IConfig['separatorLimit'] | null = null; + + @Input() public allowNegativeNumbers: IConfig['allowNegativeNumbers'] | null = null; + + @Input() public leadZeroDateTime: IConfig['leadZeroDateTime'] | null = null; + + @Input() public leadZero: IConfig['leadZero'] | null = null; + + @Input() public triggerOnMaskChange: IConfig['triggerOnMaskChange'] | null = null; + + @Input() public apm: IConfig['apm'] | null = null; + + @Input() public inputTransformFn: IConfig['inputTransformFn'] | null = null; + + @Input() public outputTransformFn: IConfig['outputTransformFn'] | null = null; + + @Input() public keepCharacterPositions: IConfig['keepCharacterPositions'] | null = null; @Output() public maskFilled: IConfig['maskFilled'] = new EventEmitter(); @@ -98,7 +122,6 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida // eslint-disable-next-line @typescript-eslint/no-empty-function public onTouch = () => {}; - //[TODO] andriiKamaldinov1 find better solution public ngOnChanges(changes: SimpleChanges): void { const { maskExpression, @@ -325,9 +348,10 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida ? this._maskValue.length - this._maskService.checkDropSpecialCharAmount(this._maskValue) - counterOfOpt - : this.prefix() - ? this._maskValue.length + this.prefix().length - counterOfOpt + : this.prefix + ? this._maskValue.length + this.prefix.length - counterOfOpt : this._maskValue.length - counterOfOpt; + if (array.length === 1) { if (value.toString().length < length) { return this._createValidationError(value); @@ -339,7 +363,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida lastIndexArray && this._maskService.specialCharacters.includes(lastIndexArray[0] as string) && String(value).includes(lastIndexArray[0] ?? '') && - !this.dropSpecialCharacters() + !this.dropSpecialCharacters ) { const special = value.split(lastIndexArray[0]); return special[special.length - 1].length === lastIndexArray.length - 1 @@ -420,12 +444,12 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida : (el.selectionStart as number); if ( - this.showMaskTyped() && - this.keepCharacterPositions() && + this.showMaskTyped && + this.keepCharacterPositions && this._maskService.placeHolderCharacter.length === 1 ) { const inputSymbol = el.value.slice(position - 1, position); - const prefixLength = this.prefix().length; + const prefixLength = this.prefix.length; const checkSymbols: boolean = this._maskService._checkSymbolMask( inputSymbol, this._maskService.maskExpression[position - 1 - prefixLength] ?? @@ -446,9 +470,9 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida if (!selectRangeBackspace) { if (this._maskService.selStart === prefixLength) { this._maskService.actualValue = - this.prefix() + + this.prefix + this._maskService.maskIsShown.slice(0, selEnd) + - this._inputValue.split(this.prefix()).join(''); + this._inputValue.split(this.prefix).join(''); } else if ( this._maskService.selStart === this._maskService.maskIsShown.length + prefixLength @@ -458,9 +482,9 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida this._maskService.maskIsShown.slice(selStart, selEnd); } else { this._maskService.actualValue = - this.prefix() + + this.prefix + this._inputValue - .split(this.prefix()) + .split(this.prefix) .join('') .slice(0, selStart) + this._maskService.maskIsShown.slice(selStart, selEnd) + @@ -468,27 +492,27 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida selEnd + prefixLength, this._maskService.maskIsShown.length + prefixLength ) + - this.suffix(); + this.suffix; } } else if ( !this._maskService.specialCharacters.includes( this._maskService.maskExpression.slice( - position - this.prefix().length, - position + 1 - this.prefix().length + position - this.prefix.length, + position + 1 - this.prefix.length ) ) && selectRangeBackspace ) { - if (selStart === 1 && this.prefix()) { + if (selStart === 1 && this.prefix) { this._maskService.actualValue = - this.prefix() + + this.prefix + this._maskService.placeHolderCharacter + el.value - .split(this.prefix()) + .split(this.prefix) .join('') - .split(this.suffix()) + .split(this.suffix) .join('') + - this.suffix(); + this.suffix; position = position - 1; } else { const part1 = el.value.substring(0, position); @@ -519,25 +543,25 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida } else if (checkSymbols) { if (el.value.length === 1 && position === 1) { this._maskService.actualValue = - this.prefix() + + this.prefix + inputSymbol + this._maskService.maskIsShown.slice( 1, this._maskService.maskIsShown.length ) + - this.suffix(); + this.suffix; } else { this._maskService.actualValue = el.value.slice(0, position - 1) + inputSymbol + el.value .slice(position + 1) - .split(this.suffix()) + .split(this.suffix) .join('') + - this.suffix(); + this.suffix; } } else if ( - this.prefix() && + this.prefix && el.value.length === 1 && position - prefixLength === 1 && this._maskService._checkSymbolMask( @@ -547,13 +571,13 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida ) ) { this._maskService.actualValue = - this.prefix() + + this.prefix + el.value + this._maskService.maskIsShown.slice( 1, this._maskService.maskIsShown.length ) + - this.suffix(); + this.suffix; } } } @@ -577,7 +601,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida } if ( this._maskService.maskExpression === MaskExpression.DAYS_MONTHS_YEARS && - this.leadZeroDateTime() + this.leadZeroDateTime ) { if ( (position < 3 && Number(el.value) > 31 && Number(el.value) < 40) || @@ -586,10 +610,9 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida position = position + 2; } } - if ( this._maskService.maskExpression === MaskExpression.HOURS_MINUTES_SECONDS && - this.apm() + this.apm ) { if (this._justPasted && el.value.slice(0, 2) === MaskExpression.DOUBLE_ZERO) { el.value = el.value.slice(1, 2) + el.value.slice(2, el.value.length); @@ -622,7 +645,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida // update position after applyValueChanges to prevent cursor on wrong position when it has an array of maskExpression if (this._maskExpressionArray.length) { if (this._code === MaskExpression.BACKSPACE) { - position = this.specialCharacters().includes( + position = this.specialCharacters.includes( this._inputValue.slice(position - 1, position) ) ? position - 1 @@ -688,8 +711,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida public onBlur(e: CustomKeyboardEvent): void { if (this._maskValue) { const el: HTMLInputElement = e.target as HTMLInputElement; - const decimalMarker = this.decimalMarker(); - if (this.leadZero() && el.value.length > 0 && typeof decimalMarker === 'string') { + if (this.leadZero && el.value.length > 0 && typeof this.decimalMarker === 'string') { const maskExpression = this._maskService.maskExpression; const precision = Number( this._maskService.maskExpression.slice( @@ -698,16 +720,16 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida ) ); if (precision > 1) { - el.value = this.suffix() ? el.value.split(this.suffix()).join('') : el.value; - const decimalPart = el.value.split(decimalMarker)[1] as string; - el.value = el.value.includes(decimalMarker) + el.value = this.suffix ? el.value.split(this.suffix).join('') : el.value; + const decimalPart = el.value.split(this.decimalMarker)[1] as string; + el.value = el.value.includes(this.decimalMarker) ? el.value + MaskExpression.NUMBER_ZERO.repeat(precision - decimalPart.length) + - this.suffix() + this.suffix : el.value + - decimalMarker + + this.decimalMarker + MaskExpression.NUMBER_ZERO.repeat(precision) + - this.suffix(); + this.suffix; this._maskService.actualValue = el.value; } } @@ -815,29 +837,29 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida } if (e.key === MaskExpression.BACKSPACE && (el.selectionStart as number) !== 0) { // If specialChars is false, (shouldn't ever happen) then set to the defaults - const specialCharacters = this.specialCharacters()?.length - ? this.specialCharacters() + this.specialCharacters = this.specialCharacters?.length + ? this.specialCharacters : this._config.specialCharacters; if ( - this.prefix().length > 1 && - (el.selectionStart as number) <= this.prefix().length + this.prefix.length > 1 && + (el.selectionStart as number) <= this.prefix.length ) { - el.setSelectionRange(this.prefix().length, el.selectionEnd); + el.setSelectionRange(this.prefix.length, el.selectionEnd); } else { if ( this._inputValue.length !== (el.selectionStart as number) && (el.selectionStart as number) !== 1 ) { while ( - specialCharacters.includes( + this.specialCharacters.includes( ( this._inputValue[(el.selectionStart as number) - 1] ?? MaskExpression.EMPTY_STRING ).toString() ) && - ((this.prefix().length >= 1 && - (el.selectionStart as number) > this.prefix().length) || - this.prefix().length === 0) + ((this.prefix.length >= 1 && + (el.selectionStart as number) > this.prefix.length) || + this.prefix.length === 0) ) { el.setSelectionRange( (el.selectionStart as number) - 1, @@ -872,12 +894,12 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida } } if ( - !!this.suffix() && - this.suffix().length > 1 && - this._inputValue.length - this.suffix().length < (el.selectionStart as number) + !!this.suffix && + this.suffix.length > 1 && + this._inputValue.length - this.suffix.length < (el.selectionStart as number) ) { el.setSelectionRange( - this._inputValue.length - this.suffix().length, + this._inputValue.length - this.suffix.length, this._inputValue.length ); } else if ( @@ -902,9 +924,10 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida controlValue = controlValue.value; } if (controlValue !== null) { - const inputTransformFn = this.inputTransformFn(); // eslint-disable-next-line no-param-reassign - controlValue = inputTransformFn ? inputTransformFn(controlValue) : controlValue; + controlValue = this.inputTransformFn + ? this.inputTransformFn(controlValue) + : controlValue; } if ( @@ -936,11 +959,17 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida ) : inputValue; } + if ( + Array.isArray(this._maskService.decimalMarker) && + this.decimalMarker === MaskExpression.DOT + ) { + this._maskService.decimalMarker = MaskExpression.COMMA; + } if ( this._maskService.leadZero && inputValue && - this.maskExpression() && - this.dropSpecialCharacters() !== false + this.maskExpression && + this.dropSpecialCharacters !== false ) { // eslint-disable-next-line no-param-reassign inputValue = this._maskService._checkPrecision( @@ -954,10 +983,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida .toString() .replace(MaskExpression.DOT, MaskExpression.COMMA); } - if ( - this.maskExpression()?.startsWith(MaskExpression.SEPARATOR) && - this.leadZero() - ) { + if (this.maskExpression?.startsWith(MaskExpression.SEPARATOR) && this.leadZero) { requestAnimationFrame(() => { this._maskService.applyMask( inputValue?.toString() ?? '', @@ -982,7 +1008,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida (this._maskService.prefix || this._maskService.showMaskTyped)) ) { // Let the service we know we are writing value so that triggering onChange function won't happen during applyMask - typeof this.inputTransformFn() !== 'function' + typeof this.inputTransformFn !== 'function' ? (this._maskService.writingValue = true) : ''; this._maskService.formElementProperty = [ @@ -990,7 +1016,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida this._maskService.applyMask(inputValue, this._maskService.maskExpression), ]; // Let the service know we've finished writing value - typeof this.inputTransformFn() !== 'function' + typeof this.inputTransformFn !== 'function' ? (this._maskService.writingValue = false) : ''; } else { @@ -1024,12 +1050,12 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida public checkSelectionOnDeletion(el: HTMLInputElement): void { el.selectionStart = Math.min( - Math.max(this.prefix().length, el.selectionStart as number), - this._inputValue.length - this.suffix().length + Math.max(this.prefix.length, el.selectionStart as number), + this._inputValue.length - this.suffix.length ); el.selectionEnd = Math.min( - Math.max(this.prefix().length, el.selectionEnd as number), - this._inputValue.length - this.suffix().length + Math.max(this.prefix.length, el.selectionEnd as number), + this._inputValue.length - this.suffix.length ); } @@ -1096,21 +1122,23 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida this._maskService.removeMask(this._inputValue)?.length <= this._maskService.removeMask(mask)?.length; if (test) { - this._maskValue = this._maskService.maskExpression = mask.includes( - MaskExpression.CURLY_BRACKETS_LEFT - ) - ? this._maskService._repeatPatternSymbols(mask) - : mask; + this._maskValue = + this.maskExpression = + this._maskService.maskExpression = + mask.includes(MaskExpression.CURLY_BRACKETS_LEFT) + ? this._maskService._repeatPatternSymbols(mask) + : mask; return test; } else { const expression = this._maskExpressionArray[this._maskExpressionArray.length - 1] ?? MaskExpression.EMPTY_STRING; - this._maskValue = this._maskService.maskExpression = expression.includes( - MaskExpression.CURLY_BRACKETS_LEFT - ) - ? this._maskService._repeatPatternSymbols(expression) - : expression; + this._maskValue = + this.maskExpression = + this._maskService.maskExpression = + expression.includes(MaskExpression.CURLY_BRACKETS_LEFT) + ? this._maskService._repeatPatternSymbols(expression) + : expression; } } else { const check: boolean = this._maskService @@ -1121,7 +1149,7 @@ export class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valida return this._maskService._checkSymbolMask(character, indexMask); }); if (check) { - this._maskValue = this._maskService.maskExpression = mask; + this._maskValue = this.maskExpression = this._maskService.maskExpression = mask; return check; } } diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask.pipe.ts b/projects/ngx-mask-lib/src/lib/ngx-mask.pipe.ts index 67cf6169..9288e956 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask.pipe.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask.pipe.ts @@ -1,6 +1,6 @@ import { inject, Pipe, PipeTransform } from '@angular/core'; -import { IConfig } from './ngx-mask.config'; +import { IConfig, NGX_MASK_CONFIG } from './ngx-mask.config'; import { NgxMaskService } from './ngx-mask.service'; import { MaskExpression } from './ngx-mask-expression.enum'; @@ -10,7 +10,7 @@ import { MaskExpression } from './ngx-mask-expression.enum'; standalone: true, }) export class NgxMaskPipe implements PipeTransform { - private readonly defaultOptions: Partial = {}; + private readonly defaultOptions = inject(NGX_MASK_CONFIG); private readonly _maskService = inject(NgxMaskService); diff --git a/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts b/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts index 7f4cd908..ad6bee1c 100644 --- a/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts +++ b/projects/ngx-mask-lib/src/lib/ngx-mask.service.ts @@ -124,7 +124,8 @@ export class NgxMaskService extends NgxMaskApplierService { this.specialCharacters.indexOf( this.maskExpression[position] ?? MaskExpression.EMPTY_STRING ) !== -1 && - this.showMaskTyped + this.showMaskTyped && + !this.prefix ) { newInputValue = this._currentValue; } diff --git a/projects/ngx-mask-lib/src/test/add-prefix.spec.ts b/projects/ngx-mask-lib/src/test/add-prefix.spec.ts index 36634901..a8dff092 100644 --- a/projects/ngx-mask-lib/src/test/add-prefix.spec.ts +++ b/projects/ngx-mask-lib/src/test/add-prefix.spec.ts @@ -121,4 +121,13 @@ describe('Directive: Mask (Add prefix)', () => { equal('-12345', '-€1234.5', fixture); equal('-123456', '-€1234.56', fixture); }); + + it('should remove prefix when setValue', () => { + component.mask = '000 000'; + component.prefix = 'KZ'; + component.dropSpecialCharacters = true; + component.form.setValue('KZ123123'); + equal('KZ123123', 'KZ123 123', fixture); + expect(component.form.value).toBe('123123'); + }); }); diff --git a/projects/ngx-mask-lib/src/test/delete.cy-spec.ts b/projects/ngx-mask-lib/src/test/delete.cy-spec.ts index 7b3ff2c2..4ce8e69f 100644 --- a/projects/ngx-mask-lib/src/test/delete.cy-spec.ts +++ b/projects/ngx-mask-lib/src/test/delete.cy-spec.ts @@ -201,4 +201,91 @@ describe('Directive: Mask (Delete)', () => { .type('{backspace}') .should('have.value', '12D : 34H : 56M : 78S'); }); + + it('should backspace with showMaskTyped and prefix', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: '(000) 000-0000', + prefix: '+7 ', + showMaskTyped: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('1234567890') + .should('have.value', '+7 (123) 456-7890') + .type('{backspace}') + .should('have.prop', 'selectionStart', 16) + .should('have.value', '+7 (123) 456-789_') + .type('{backspace}') + .should('have.prop', 'selectionStart', 15) + .should('have.value', '+7 (123) 456-78__') + .type('{backspace}') + .should('have.prop', 'selectionStart', 14) + .should('have.value', '+7 (123) 456-7___') + .type('{backspace}') + .should('have.prop', 'selectionStart', 12) + .should('have.value', '+7 (123) 456-____') + .type('{backspace}') + .should('have.prop', 'selectionStart', 11) + .should('have.value', '+7 (123) 45_-____') + .type('{backspace}') + .should('have.prop', 'selectionStart', 10) + .should('have.value', '+7 (123) 4__-____') + .type('{backspace}') + .should('have.prop', 'selectionStart', 7) + .should('have.value', '+7 (123) ___-____') + .type('{backspace}') + .should('have.prop', 'selectionStart', 6) + .should('have.value', '+7 (12_) ___-____') + .type('{backspace}') + .should('have.prop', 'selectionStart', 5) + .should('have.value', '+7 (1__) ___-____') + .type('{backspace}') + .should('have.prop', 'selectionStart', 3) + .should('have.value', '+7 (___) ___-____'); + }); + + it('should backspace with showMaskTyped and prefix', () => { + cy.mount(CypressTestMaskComponent, { + componentProperties: { + mask: '00 000 00 00', + prefix: '+32 ', + showMaskTyped: true, + }, + imports: [CypressTestMaskModule], + }); + + cy.get('#masked') + .type('1234567890') + .should('have.value', '+32 12 345 67 89') + .type('{backspace}') + .should('have.prop', 'selectionStart', 15) + .should('have.value', '+32 12 345 67 8_') + .type('{backspace}') + .should('have.prop', 'selectionStart', 13) + .should('have.value', '+32 12 345 67 __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 12) + .should('have.value', '+32 12 345 6_ __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 10) + .should('have.value', '+32 12 345 __ __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 9) + .should('have.value', '+32 12 34_ __ __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 8) + .should('have.value', '+32 12 3__ __ __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 6) + .should('have.value', '+32 12 ___ __ __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 5) + .should('have.value', '+32 1_ ___ __ __') + .type('{backspace}') + .should('have.prop', 'selectionStart', 4) + .should('have.value', '+32 __ ___ __ __'); + }); }); diff --git a/projects/ngx-mask-lib/src/test/drop-special-charaters.spec.ts b/projects/ngx-mask-lib/src/test/drop-special-charaters.spec.ts index 6310cd0e..a3a6fcf3 100644 --- a/projects/ngx-mask-lib/src/test/drop-special-charaters.spec.ts +++ b/projects/ngx-mask-lib/src/test/drop-special-charaters.spec.ts @@ -65,4 +65,65 @@ describe('Directive: Mask (Drop special characters)', () => { equal('1234567.89', '1 234 567.89', fixture); expect(component.form.value).toBe(1234567.89); }); + + it('dropSpecialCharacter test for valid', () => { + component.mask = '(000) 000-0000'; + component.dropSpecialCharacters = true; + component.validation = true; + equal('1', '(1', fixture); + expect(component.form.valid).toBe(false); + equal('12', '(12', fixture); + expect(component.form.valid).toBe(false); + equal('123', '(123', fixture); + expect(component.form.valid).toBe(false); + equal('1234', '(123) 4', fixture); + expect(component.form.valid).toBe(false); + equal('12345', '(123) 45', fixture); + expect(component.form.valid).toBe(false); + equal('123456', '(123) 456', fixture); + expect(component.form.valid).toBe(false); + equal('1234567', '(123) 456-7', fixture); + expect(component.form.valid).toBe(false); + equal('12345678', '(123) 456-78', fixture); + expect(component.form.valid).toBe(false); + equal('123456789', '(123) 456-789', fixture); + expect(component.form.valid).toBe(false); + equal('1234567890', '(123) 456-7890', fixture); + expect(component.form.valid).toBe(true); + }); + + it('dropSpecialCharacter = false test for valid', () => { + component.mask = '(000) 000-0000'; + component.dropSpecialCharacters = true; + component.validation = true; + equal('1', '(1', fixture); + expect(component.form.valid).toBe(false); + equal('12', '(12', fixture); + expect(component.form.valid).toBe(false); + equal('123', '(123', fixture); + expect(component.form.valid).toBe(false); + equal('1234', '(123) 4', fixture); + expect(component.form.valid).toBe(false); + equal('12345', '(123) 45', fixture); + expect(component.form.valid).toBe(false); + equal('123456', '(123) 456', fixture); + expect(component.form.valid).toBe(false); + equal('1234567', '(123) 456-7', fixture); + expect(component.form.valid).toBe(false); + equal('12345678', '(123) 456-78', fixture); + expect(component.form.valid).toBe(false); + equal('123456789', '(123) 456-789', fixture); + expect(component.form.valid).toBe(false); + equal('1234567890', '(123) 456-7890', fixture); + expect(component.form.valid).toBe(true); + }); + + it('dropSpecialCharacter = true test for valid with setValue', () => { + component.mask = '(000) 000-0000'; + component.dropSpecialCharacters = true; + component.validation = true; + component.form.setValue('1234567890'); + equal('1234567890', '(123) 456-7890', fixture); + expect(component.form.valid).toBe(true); + }); }); diff --git a/projects/ngx-mask-lib/src/test/mask.pipe.spec.ts b/projects/ngx-mask-lib/src/test/mask.pipe.spec.ts index c7ae6ff2..a2e6cdf3 100644 --- a/projects/ngx-mask-lib/src/test/mask.pipe.spec.ts +++ b/projects/ngx-mask-lib/src/test/mask.pipe.spec.ts @@ -351,5 +351,15 @@ describe('Pipe: Mask', () => { }); expect(value).toEqual('3.000'); }); - //TODO(inepipepnko): need cover all config options + + it('should show second pipe without suffix', () => { + const valueWithSuffix: string | number = maskPipe.transform('55555', '00 (000)', { + suffix: ' DDD', + }); + const valueWithPrefix: string | number = maskPipe.transform('55555', '00 (000)', { + prefix: 'DDD ', + }); + expect(valueWithSuffix).toEqual('55 (555) DDD'); + expect(valueWithPrefix).toEqual('DDD 55 (555)'); + }); }); diff --git a/projects/ngx-mask-lib/src/test/percent.spec.ts b/projects/ngx-mask-lib/src/test/percent.spec.ts index fd639b3f..3e62d808 100644 --- a/projects/ngx-mask-lib/src/test/percent.spec.ts +++ b/projects/ngx-mask-lib/src/test/percent.spec.ts @@ -227,4 +227,77 @@ describe('Directive: Mask (Percent)', () => { equal('12,221', '12,221%', fixture); expect(component.form.value).toBe('12.221'); }); + + it('percent with allowNegative = true', () => { + component.mask = 'percent'; + component.allowNegativeNumbers = true; + + equal('-', '-', fixture); + equal('-1', '-1', fixture); + equal('-12', '-12', fixture); + expect(component.form.value).toBe('-12'); + }); + + it('percent 2 with allowNegative = true', () => { + component.mask = 'percent.2'; + component.allowNegativeNumbers = true; + + equal('-', '-', fixture); + equal('-1', '-1', fixture); + equal('-12', '-12', fixture); + equal('-12.3', '-12.3', fixture); + equal('-12.34', '-12.34', fixture); + expect(component.form.value).toBe('-12.34'); + }); + + it('percent 3 with allowNegative = true', () => { + component.mask = 'percent.3'; + component.allowNegativeNumbers = true; + + equal('-', '-', fixture); + equal('-1', '-1', fixture); + equal('-12', '-12', fixture); + equal('-12.3', '-12.3', fixture); + equal('-12.34', '-12.34', fixture); + equal('-12.345', '-12.345', fixture); + expect(component.form.value).toBe('-12.345'); + }); + + it('percent with allowNegative = true, decimalMarker = ,', () => { + component.mask = 'percent'; + component.decimalMarker = ','; + component.allowNegativeNumbers = true; + + equal('-', '-', fixture); + equal('-1', '-1', fixture); + equal('-12', '-12', fixture); + expect(component.form.value).toBe('-12'); + }); + + it('percent 2 with allowNegative = true, decimalMarker = ,', () => { + component.mask = 'percent.2'; + component.decimalMarker = ','; + component.allowNegativeNumbers = true; + + equal('-', '-', fixture); + equal('-1', '-1', fixture); + equal('-12', '-12', fixture); + equal('-12,3', '-12,3', fixture); + equal('-12,34', '-12,34', fixture); + expect(component.form.value).toBe('-12.34'); + }); + + it('percent 3 with allowNegative = true, decimalMarker = ,', () => { + component.mask = 'percent.3'; + component.allowNegativeNumbers = true; + component.decimalMarker = ','; + + equal('-', '-', fixture); + equal('-1', '-1', fixture); + equal('-12', '-12', fixture); + equal('-12,3', '-12,3', fixture); + equal('-12,34', '-12,34', fixture); + equal('-12,345', '-12,345', fixture); + expect(component.form.value).toBe('-12.345'); + }); }); diff --git a/projects/ngx-mask-lib/src/test/separator.spec.ts b/projects/ngx-mask-lib/src/test/separator.spec.ts index 9f7e041d..807ee99b 100644 --- a/projects/ngx-mask-lib/src/test/separator.spec.ts +++ b/projects/ngx-mask-lib/src/test/separator.spec.ts @@ -1447,4 +1447,142 @@ describe('Separator: Mask', () => { equal('-.34', '-0.34', fixture); equal('-.345', '-0.345', fixture); })); + + it('separator.2 thousandSeparator = . should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.mask = 'separator.2'; + component.thousandSeparator = '.'; + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + fixture.detectChanges(); + requestAnimationFrame(() => { + expect(inputTarget.value).toBe('1.255,78'); + }); + })); + + it('separator.3 thousandSeparator = . should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.mask = 'separator.3'; + component.thousandSeparator = '.'; + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + requestAnimationFrame(() => { + expect(inputTarget.value).toBe('1.255,78'); + }); + })); + + it('separator.1 thousandSeparator = . should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.thousandSeparator = '.'; + component.mask = 'separator.1'; + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + requestAnimationFrame(() => { + expect(inputTarget.value).toBe('1.255,78'); + }); + })); + + it('separator.2 thousandSeparator = , should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + component.mask = 'separator.2'; + component.thousandSeparator = ','; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + + expect(inputTarget.value).toBe('1,255.78'); + })); + + it('separator.3 thousandSeparator = , should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.mask = 'separator.3'; + component.thousandSeparator = ','; + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + + expect(inputTarget.value).toBe('1,255.78'); + })); + + it('separator.1 thousandSeparator = , should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.mask = 'separator.1'; + component.thousandSeparator = ','; + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + + expect(inputTarget.value).toBe('1,255.7'); + })); + + it('separator.2 thousandSeparator = . leadZero should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.mask = 'separator.2'; + component.thousandSeparator = '.'; + component.leadZero = true; + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + requestAnimationFrame(() => { + expect(inputTarget.value).toBe('1.255,78'); + }); + })); + + it('separator.3 thousandSeparator = . leadZero should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.mask = 'separator.3'; + component.thousandSeparator = '.'; + component.leadZero = true; + + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + requestAnimationFrame(() => { + expect(inputTarget.value).toBe('1.255,780'); + }); + })); + + it('separator.1 thousandSeparator = . leadZero should display correct value if decimalMarker is array 12345.67', fakeAsync(() => { + component.thousandSeparator = '.'; + component.mask = 'separator.1'; + component.leadZero = true; + + const debugElement: DebugElement = fixture.debugElement.query(By.css('input')); + const inputTarget: HTMLInputElement = debugElement.nativeElement as HTMLInputElement; + spyOnProperty(document, 'activeElement').and.returnValue(inputTarget); + fixture.detectChanges(); + + component.form.setValue(1255.78); + tick(); + requestAnimationFrame(() => { + expect(inputTarget.value).toBe('1.255,78'); + }); + })); }); diff --git a/src/app/options/options.component.html b/src/app/options/options.component.html index 8c74c362..47bb32a3 100644 --- a/src/app/options/options.component.html +++ b/src/app/options/options.component.html @@ -100,6 +100,7 @@ mask="{{ ex._mask || '' }}" [thousandSeparator]="ex._thousandSeparator || ' '" [formControl]="ex.control.form" + [(ngModel)]="ex.control.model" [decimalMarker]="ex._decimalMarker || '.'" [leadZero]="ex._leadZero || null" class="customDarkInput bg-black white span" /> diff --git a/src/libraries b/src/libraries index 62334a2c..f402f517 160000 --- a/src/libraries +++ b/src/libraries @@ -1 +1 @@ -Subproject commit 62334a2c1cb5d45b3440c01f978915f8017fba8e +Subproject commit f402f51789609e8de8500d39ff80cdcb68f89bb9