diff --git a/projects/core/src/lib/classes/mask-model/mask-model.ts b/projects/core/src/lib/classes/mask-model/mask-model.ts index eee3003cc..1bf3cc318 100644 --- a/projects/core/src/lib/classes/mask-model/mask-model.ts +++ b/projects/core/src/lib/classes/mask-model/mask-model.ts @@ -10,7 +10,7 @@ export class MaskModel implements ElementState { constructor( readonly initialElementState: ElementState, - private readonly maskOptions: MaskitoOptions, + private readonly maskOptions: Required, ) { const {value, selection} = calibrateValueByMask( initialElementState, @@ -37,23 +37,34 @@ export class MaskModel implements ElementState { newCharacters, this.maskOptions.overwriteMode, ).selection; - const newUnmaskedValue = - unmaskedElementState.value.slice(0, unmaskedFrom) + - newCharacters + - unmaskedElementState.value.slice(unmaskedTo); - - const newCaretIndex = unmaskedFrom + newCharacters.length; + const newUnmaskedLeadingValuePart = + unmaskedElementState.value.slice(0, unmaskedFrom) + newCharacters; + const newCaretIndex = newUnmaskedLeadingValuePart.length; const maskedElementState = calibrateValueByMask( { - value: newUnmaskedValue, + value: + newUnmaskedLeadingValuePart + + unmaskedElementState.value.slice(unmaskedTo), selection: [newCaretIndex, newCaretIndex], }, maskExpression, initialElementState, ); + const isInvalidCharsInsertion = + value.slice(0, unmaskedFrom) === + calibrateValueByMask( + { + value: newUnmaskedLeadingValuePart, + selection: [newCaretIndex, newCaretIndex], + }, + maskExpression, + initialElementState, + ).value; - if (areElementStatesEqual(this, maskedElementState)) { - // If typing new characters does not change value + if ( + isInvalidCharsInsertion || + areElementStatesEqual(this, maskedElementState) // If typing new characters does not change value + ) { throw new Error('Invalid mask value'); } diff --git a/projects/core/src/lib/classes/mask-model/tests/mask-model-fixed-characters.spec.ts b/projects/core/src/lib/classes/mask-model/tests/mask-model-fixed-characters.spec.ts index e2ce5b5c0..936899844 100644 --- a/projects/core/src/lib/classes/mask-model/tests/mask-model-fixed-characters.spec.ts +++ b/projects/core/src/lib/classes/mask-model/tests/mask-model-fixed-characters.spec.ts @@ -1,9 +1,11 @@ +import {MASKITO_DEFAULT_OPTIONS} from '../../../constants'; import {MaskitoOptions} from '../../../types'; import {MaskModel} from '../mask-model'; describe('MaskModel | Fixed characters', () => { describe('New typed character is equal to the previous (already existing) fixed character', () => { - const phoneMaskitoOptions: MaskitoOptions = { + const phoneMaskitoOptions: Required = { + ...MASKITO_DEFAULT_OPTIONS, mask: [ '+', '7', diff --git a/projects/demo-integrations/cypress/tests/kit/number/number-decimal-zero-padding.cy.ts b/projects/demo-integrations/cypress/tests/kit/number/number-decimal-zero-padding.cy.ts index 73925309e..e3ef3309e 100644 --- a/projects/demo-integrations/cypress/tests/kit/number/number-decimal-zero-padding.cy.ts +++ b/projects/demo-integrations/cypress/tests/kit/number/number-decimal-zero-padding.cy.ts @@ -87,4 +87,58 @@ describe('Number | decimalZeroPadding', () => { .should('have.prop', 'selectionStart', ','.length) .should('have.prop', 'selectionEnd', ','.length); }); + + describe('Extra decimal separator insertion', () => { + it('42,|2700 => Type , => 42,|2700', () => { + cy.get('@input') + .type('42,27') + .type('{leftArrow}'.repeat('27'.length)) + .should('have.value', '42,2700') + .should('have.prop', 'selectionStart', '42,'.length) + .should('have.prop', 'selectionEnd', '42,'.length) + .type(',') + .should('have.value', '42,2700') + .should('have.prop', 'selectionStart', '42,'.length) + .should('have.prop', 'selectionEnd', '42,'.length); + }); + + it('42|,2700 => Type , => 42,|2700', () => { + cy.get('@input') + .type('42,27') + .type('{leftArrow}'.repeat(',27'.length)) + .should('have.value', '42,2700') + .should('have.prop', 'selectionStart', '42'.length) + .should('have.prop', 'selectionEnd', '42'.length) + .type(',') + .should('have.value', '42,2700') + .should('have.prop', 'selectionStart', '42,'.length) + .should('have.prop', 'selectionEnd', '42,'.length); + }); + + it('42,2|700 => Type , => 42,2|700', () => { + cy.get('@input') + .type('42,27') + .type('{leftArrow}') + .should('have.value', '42,2700') + .should('have.prop', 'selectionStart', '42,2'.length) + .should('have.prop', 'selectionEnd', '42,2'.length) + .type(',') + .should('have.value', '42,2700') + .should('have.prop', 'selectionStart', '42,2'.length) + .should('have.prop', 'selectionEnd', '42,2'.length); + }); + + it('9|9,1234 => Type , => 9,|9123', () => { + cy.get('@input') + .type('99,1234') + .type('{moveToStart}{rightArrow}') + .should('have.value', '99,1234') + .should('have.prop', 'selectionStart', 1) + .should('have.prop', 'selectionEnd', 1) + .type(',') + .should('have.value', '9,9123') + .should('have.prop', 'selectionStart', 2) + .should('have.prop', 'selectionEnd', 2); + }); + }); });