Skip to content

Commit

Permalink
fix(kit): Number has problems when prefix/postfix includes `decimal…
Browse files Browse the repository at this point in the history
…Separator` symbol (#816)
  • Loading branch information
aktanoff authored Jan 18, 2024
1 parent e223569 commit 4f1f104
Show file tree
Hide file tree
Showing 16 changed files with 391 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ describe('Number | postfix with point', () => {
.should('have.prop', 'selectionEnd', 2);
});

// TODO https://github.com/taiga-family/maskito/issues/703
it.skip('Empty => Type 0.42 => 0.42 lbs.', () => {
it('Empty => Type 0.42 => 0.42 lbs.', () => {
cy.mount(TestInput, {componentProperties: {maskitoOptions}});
cy.get('input')
.type('0')
Expand All @@ -52,8 +51,7 @@ describe('Number | postfix with point', () => {
});
});

// TODO https://github.com/taiga-family/maskito/issues/703
describe.skip('Complex: maskitoCaretGuard + maskitoAddOnFocusPlugin + maskitoRemoveOnBlurPlugin', () => {
describe('Complex: maskitoCaretGuard + maskitoAddOnFocusPlugin + maskitoRemoveOnBlurPlugin', () => {
const postfix = ' lbs.';
const numberOptions = maskitoNumberOptionsGenerator({
postfix,
Expand Down
48 changes: 39 additions & 9 deletions projects/kit/src/lib/masks/number/number-mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
createThousandSeparatorPostprocessor,
createZeroPrecisionPreprocessor,
} from './processors';
import {createAffixesFilterPreprocessor} from './processors/affixes-filter-preprocessor';
import {generateMaskExpression, validateDecimalPseudoSeparators} from './utils';

export function maskitoNumberOptionsGenerator({
Expand Down Expand Up @@ -79,21 +80,40 @@ export function maskitoNumberOptionsGenerator({
decimalSeparator,
decimalPseudoSeparators: validatedDecimalPseudoSeparators,
pseudoMinuses,
prefix,
postfix,
}),
createAffixesFilterPreprocessor({prefix, postfix}),
createFullWidthToHalfWidthPreprocessor(),
createPseudoCharactersPreprocessor(CHAR_MINUS, pseudoMinuses),
createPseudoCharactersPreprocessor(
decimalSeparator,
validatedDecimalPseudoSeparators,
),
createPseudoCharactersPreprocessor({
validCharacter: CHAR_MINUS,
pseudoCharacters: pseudoMinuses,
prefix,
postfix,
}),
createPseudoCharactersPreprocessor({
validCharacter: decimalSeparator,
pseudoCharacters: validatedDecimalPseudoSeparators,
prefix,
postfix,
}),
createNotEmptyIntegerPartPreprocessor({decimalSeparator, precision}),
createNonRemovableCharsDeletionPreprocessor({
decimalSeparator,
decimalZeroPadding,
thousandSeparator,
}),
createZeroPrecisionPreprocessor(precision, decimalSeparator),
createRepeatedDecimalSeparatorPreprocessor(decimalSeparator),
createZeroPrecisionPreprocessor({
precision,
decimalSeparator,
prefix,
postfix,
}),
createRepeatedDecimalSeparatorPreprocessor({
decimalSeparator,
prefix,
postfix,
}),
],
postprocessors: [
createMinMaxPostprocessor({decimalSeparator, min, max}),
Expand All @@ -109,12 +129,22 @@ export function maskitoNumberOptionsGenerator({
decimalSeparator,
decimalZeroPadding,
precision,
prefix,
postfix,
}),
],
plugins: [
createLeadingZeroesValidationPlugin(decimalSeparator, thousandSeparator),
createNotEmptyIntegerPlugin(decimalSeparator),
createLeadingZeroesValidationPlugin({
decimalSeparator,
thousandSeparator,
prefix,
postfix,
}),
createNotEmptyIntegerPlugin({
decimalSeparator,
prefix,
postfix,
}),
createMinMaxPlugin({min, max, decimalSeparator}),
],
overwriteMode: decimalZeroPadding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,23 @@ const DUMMY_SELECTION = [0, 0] as const;
* @example 000000 => blur => 0
* @example 00005 => blur => 5
*/
export function createLeadingZeroesValidationPlugin(
decimalSeparator: string,
thousandSeparator: string,
): MaskitoPlugin {
const dropRepeatedLeadingZeroes = createLeadingZeroesValidationPostprocessor(
export function createLeadingZeroesValidationPlugin({
decimalSeparator,
thousandSeparator,
prefix,
postfix,
}: {
decimalSeparator: string;
thousandSeparator: string;
prefix: string;
postfix: string;
}): MaskitoPlugin {
const dropRepeatedLeadingZeroes = createLeadingZeroesValidationPostprocessor({
decimalSeparator,
thousandSeparator,
);
prefix,
postfix,
});

return maskitoEventHandler(
'blur',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import {MaskitoPlugin, maskitoUpdateElement} from '@maskito/core';

import {maskitoEventHandler} from '../../../plugins';
import {escapeRegExp} from '../../../utils';
import {escapeRegExp, extractAffixes} from '../../../utils';

/**
* It pads EMPTY integer part with zero if decimal parts exists.
* It works on blur event only!
* @example 1|,23 => Backspace => Blur => 0,23
*/
export function createNotEmptyIntegerPlugin(decimalSeparator: string): MaskitoPlugin {
export function createNotEmptyIntegerPlugin({
decimalSeparator,
prefix,
postfix,
}: {
decimalSeparator: string;
prefix: string;
postfix: string;
}): MaskitoPlugin {
return maskitoEventHandler(
'blur',
element => {
const newValue = element.value.replace(
new RegExp(`^(\\D+)?${escapeRegExp(decimalSeparator)}`),
`$10${decimalSeparator}`,
const {cleanValue, extractedPostfix, extractedPrefix} = extractAffixes(
element.value,
{prefix, postfix},
);
const newValue =
extractedPrefix +
cleanValue.replace(
new RegExp(`^(\\D+)?${escapeRegExp(decimalSeparator)}`),
`$10${decimalSeparator}`,
) +
extractedPostfix;

if (newValue !== element.value) {
maskitoUpdateElement(element, newValue);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {MaskitoPreprocessor} from '@maskito/core';

import {extractAffixes} from '../../../utils';

/**
* It drops prefix and postfix from data
* Needed for case, when prefix or postfix contain decimalSeparator, to ignore it in resulting number
* @example User pastes '{prefix}123.45{postfix}' => 123.45
*/
export function createAffixesFilterPreprocessor({
prefix,
postfix,
}: {
prefix: string;
postfix: string;
}): MaskitoPreprocessor {
return ({elementState, data}) => {
const {cleanValue: cleanData} = extractAffixes(data, {
prefix,
postfix,
});

return {
elementState,
data: cleanData,
};
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {MaskitoPostprocessor} from '@maskito/core';

import {escapeRegExp, identity} from '../../../utils';
import {extractAffixes, identity} from '../../../utils';
import {maskitoParseNumber} from '../utils';

/**
Expand All @@ -12,34 +12,38 @@ export function createDecimalZeroPaddingPostprocessor({
decimalSeparator,
precision,
decimalZeroPadding,
prefix,
postfix,
}: {
decimalSeparator: string;
decimalZeroPadding: boolean;
precision: number;
prefix: string;
postfix: string;
}): MaskitoPostprocessor {
if (precision <= 0 || !decimalZeroPadding) {
return identity;
}

const trailingPostfixRegExp = new RegExp(`${escapeRegExp(postfix)}$`);

return ({value, selection}) => {
if (Number.isNaN(maskitoParseNumber(value, decimalSeparator))) {
const {cleanValue, extractedPrefix, extractedPostfix} = extractAffixes(value, {
prefix,
postfix,
});

if (Number.isNaN(maskitoParseNumber(cleanValue, decimalSeparator))) {
return {value, selection};
}

const [integerPart, decimalPart = ''] = value
.replace(trailingPostfixRegExp, '')
.split(decimalSeparator);
const [integerPart, decimalPart = ''] = cleanValue.split(decimalSeparator);

return {
value:
extractedPrefix +
integerPart +
decimalSeparator +
decimalPart.padEnd(precision, '0') +
postfix,
extractedPostfix,
selection,
};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {MaskitoPreprocessor, maskitoTransform} from '@maskito/core';

import {extractAffixes} from '../../../utils';
import {generateMaskExpression} from '../utils';

/**
Expand All @@ -16,10 +17,14 @@ export function createInitializationOnlyPreprocessor({
decimalSeparator,
decimalPseudoSeparators,
pseudoMinuses,
prefix,
postfix,
}: {
decimalSeparator: string;
decimalPseudoSeparators: readonly string[];
pseudoMinuses: readonly string[];
prefix: string;
postfix: string;
}): MaskitoPreprocessor {
let isInitializationPhase = true;
const cleanNumberMask = generateMaskExpression({
Expand All @@ -40,10 +45,18 @@ export function createInitializationOnlyPreprocessor({

isInitializationPhase = false;

const {cleanValue} = extractAffixes(elementState.value, {prefix, postfix});

return {
elementState: maskitoTransform(elementState, {
mask: cleanNumberMask,
}),
elementState: maskitoTransform(
{
...elementState,
value: cleanValue,
},
{
mask: cleanNumberMask,
},
),
data,
};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {MaskitoPostprocessor} from '@maskito/core';

import {escapeRegExp} from '../../../utils';
import {escapeRegExp, extractAffixes} from '../../../utils';

/**
* It removes repeated leading zeroes for integer part.
Expand All @@ -9,10 +9,17 @@ import {escapeRegExp} from '../../../utils';
* @example User types "000000" => 0|
* @example 0| => User types "5" => 5|
*/
export function createLeadingZeroesValidationPostprocessor(
decimalSeparator: string,
thousandSeparator: string,
): MaskitoPostprocessor {
export function createLeadingZeroesValidationPostprocessor({
decimalSeparator,
thousandSeparator,
prefix,
postfix,
}: {
decimalSeparator: string;
thousandSeparator: string;
prefix: string;
postfix: string;
}): MaskitoPostprocessor {
const trimLeadingZeroes = (value: string): string => {
const escapedThousandSeparator = escapeRegExp(thousandSeparator);

Expand Down Expand Up @@ -42,8 +49,13 @@ export function createLeadingZeroesValidationPostprocessor(

return ({value, selection}) => {
const [from, to] = selection;
const hasDecimalSeparator = value.includes(decimalSeparator);
const [integerPart, decimalPart = ''] = value.split(decimalSeparator);
const {cleanValue, extractedPrefix, extractedPostfix} = extractAffixes(value, {
prefix,
postfix,
});

const hasDecimalSeparator = cleanValue.includes(decimalSeparator);
const [integerPart, decimalPart = ''] = cleanValue.split(decimalSeparator);
const zeroTrimmedIntegerPart = trimLeadingZeroes(integerPart);

if (integerPart === zeroTrimmedIntegerPart) {
Expand All @@ -55,9 +67,11 @@ export function createLeadingZeroesValidationPostprocessor(

return {
value:
extractedPrefix +
zeroTrimmedIntegerPart +
(hasDecimalSeparator ? decimalSeparator : '') +
decimalPart,
decimalPart +
extractedPostfix,
selection: [Math.max(newFrom, 0), Math.max(newTo, 0)],
};
};
Expand Down
Loading

0 comments on commit 4f1f104

Please sign in to comment.