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 e3ef3309e..2422a3de8 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 @@ -141,4 +141,90 @@ describe('Number | decimalZeroPadding', () => { .should('have.prop', 'selectionEnd', 2); }); }); + + describe('Move caret when user tries to delete non-removable zeroes in decimal part', () => { + beforeEach(() => { + cy.get('@input').type(',').should('have.value', '0,0000'); + }); + + describe('Via `Backspace` button', () => { + it('0,0000| => Backspace => 0,000|0', () => { + cy.get('@input') + .type('{moveToEnd}{backspace}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,000'.length) + .should('have.prop', 'selectionEnd', '0,000'.length); + }); + + it('0,000|0 => Backspace => 0,00|00', () => { + cy.get('@input') + .type('{moveToEnd}{leftArrow}{backspace}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,00'.length) + .should('have.prop', 'selectionEnd', '0,00'.length); + }); + + it('0,00|00 => Backspace => 0,0|000', () => { + cy.get('@input') + .type('{moveToEnd}') + .type('{leftArrow}'.repeat(2)) + .type('{backspace}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,0'.length) + .should('have.prop', 'selectionEnd', '0,0'.length); + }); + + it('0,0|000 => Backspace => 0,|0000', () => { + cy.get('@input') + .type('{moveToEnd}') + .type('{leftArrow}'.repeat(3)) + .type('{backspace}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,'.length) + .should('have.prop', 'selectionEnd', '0,'.length); + }); + }); + + describe('Via `Delete` button', () => { + it('0,|0000 => Delete => 0,0|000', () => { + cy.get('@input') + .type('{moveToStart}') + .type('{rightArrow}'.repeat('0,'.length)) + .type('{del}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,0'.length) + .should('have.prop', 'selectionEnd', '0,0'.length); + }); + + it('0,0|000 => Delete => 0,00|00', () => { + cy.get('@input') + .type('{moveToStart}') + .type('{rightArrow}'.repeat('0,0'.length)) + .type('{del}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,00'.length) + .should('have.prop', 'selectionEnd', '0,00'.length); + }); + + it('0,00|00 => Delete => 0,000|0', () => { + cy.get('@input') + .type('{moveToStart}') + .type('{rightArrow}'.repeat('0,00'.length)) + .type('{del}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,000'.length) + .should('have.prop', 'selectionEnd', '0,000'.length); + }); + + it('0,000|0 => Delete => 0,0000|', () => { + cy.get('@input') + .type('{moveToStart}') + .type('{rightArrow}'.repeat('0,000'.length)) + .type('{del}') + .should('have.value', '0,0000') + .should('have.prop', 'selectionStart', '0,0000'.length) + .should('have.prop', 'selectionEnd', '0,0000'.length); + }); + }); + }); }); diff --git a/projects/kit/src/lib/masks/number/number-mask.ts b/projects/kit/src/lib/masks/number/number-mask.ts index 67fc4c0b3..553a1ed7b 100644 --- a/projects/kit/src/lib/masks/number/number-mask.ts +++ b/projects/kit/src/lib/masks/number/number-mask.ts @@ -11,9 +11,9 @@ import { createDecimalZeroPaddingPostprocessor, createLeadingZeroesValidationPostprocessor, createMaxValidationPostprocessor, + createNonRemovableCharsDeletionPreprocessor, createNotEmptyIntegerPartPreprocessor, createPseudoCharactersPreprocessor, - createSeparatorDeletionPreprocessor, createThousandSeparatorPostprocessor, createZeroPrecisionPreprocessor, } from './processors'; @@ -54,7 +54,7 @@ export function maskitoNumberOptionsGenerator({ createPseudoCharactersPreprocessor(CHAR_MINUS, pseudoMinuses), createPseudoCharactersPreprocessor(decimalSeparator, decimalPseudoSeparators), createNotEmptyIntegerPartPreprocessor({decimalSeparator, precision}), - createSeparatorDeletionPreprocessor({ + createNonRemovableCharsDeletionPreprocessor({ decimalSeparator, decimalZeroPadding, thousandSeparator, diff --git a/projects/kit/src/lib/masks/number/processors/index.ts b/projects/kit/src/lib/masks/number/processors/index.ts index 60a57e8a5..fc0b69302 100644 --- a/projects/kit/src/lib/masks/number/processors/index.ts +++ b/projects/kit/src/lib/masks/number/processors/index.ts @@ -1,8 +1,8 @@ export * from './decimal-zero-padding-postprocessor'; export * from './leading-zeroes-validation-postprocessor'; export * from './max-validation-postprocessor'; +export * from './non-removable-chars-deletion-preprocessor'; export * from './not-empty-integer-part-preprocessor'; export * from './preudo-character-preprocessor'; -export * from './separator-deletion-preprocessor'; export * from './thousand-separator-postprocessor'; export * from './zero-precision-preprocessor'; diff --git a/projects/kit/src/lib/masks/number/processors/separator-deletion-preprocessor.ts b/projects/kit/src/lib/masks/number/processors/non-removable-chars-deletion-preprocessor.ts similarity index 70% rename from projects/kit/src/lib/masks/number/processors/separator-deletion-preprocessor.ts rename to projects/kit/src/lib/masks/number/processors/non-removable-chars-deletion-preprocessor.ts index 96bcc1321..1b5c63c41 100644 --- a/projects/kit/src/lib/masks/number/processors/separator-deletion-preprocessor.ts +++ b/projects/kit/src/lib/masks/number/processors/non-removable-chars-deletion-preprocessor.ts @@ -1,12 +1,13 @@ import {MaskitoOptions} from '@maskito/core'; /** - * Manage caret-navigation when user "deletes" non-removable separators + * Manage caret-navigation when user "deletes" non-removable digits or separators * @example 1,|42 => Backspace => 1|,42 (only if `decimalZeroPadding` is `true`) * @example 1|,42 => Delete => 1,|42 (only if `decimalZeroPadding` is `true`) + * @example 0,|00 => Delete => 0,0|0 (only if `decimalZeroPadding` is `true`) * @example 1 |000 => Backspace => 1| 000 (always) */ -export function createSeparatorDeletionPreprocessor({ +export function createNonRemovableCharsDeletionPreprocessor({ decimalSeparator, thousandSeparator, decimalZeroPadding, @@ -22,10 +23,15 @@ export function createSeparatorDeletionPreprocessor({ const nonRemovableSeparators = decimalZeroPadding ? [decimalSeparator, thousandSeparator] : [thousandSeparator]; + const areNonRemovableZeroesSelected = + decimalZeroPadding && + from > value.indexOf(decimalSeparator) && + Boolean(selectedCharacters.match(/^0+$/gi)); if ( (actionType !== 'deleteBackward' && actionType !== 'deleteForward') || - !nonRemovableSeparators.includes(selectedCharacters) + (!nonRemovableSeparators.includes(selectedCharacters) && + !areNonRemovableZeroesSelected) ) { return { elementState,