Skip to content

Commit

Permalink
fix(core): show trailing fixed characters + duplicated fixed characte…
Browse files Browse the repository at this point in the history
…r on `Drop` (#185)
  • Loading branch information
nsbarsukov authored Mar 10, 2023
1 parent ff48b73 commit c7f6a1b
Show file tree
Hide file tree
Showing 17 changed files with 412 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {validateValueWithMask} from './validate-value-with-mask';
export function calibrateValueByMask(
elementState: ElementState,
mask: MaskExpression,
initialElementState: ElementState = elementState,
initialElementState: ElementState | null = null,
): ElementState {
if (validateValueWithMask(elementState.value, mask)) {
return elementState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ export function getLeadingFixedCharacters(
mask: Array<RegExp | string>,
validatedValuePart: string,
newCharacter: string,
initialElementState: ElementState,
initialElementState: ElementState | null,
): string {
let leadingFixedCharacters = ``;

for (let i = validatedValuePart.length; i < mask.length; i++) {
const charConstraint = mask[i];
const isInitiallyExisted = initialElementState.value[i] === charConstraint;
const isInitiallyExisted = initialElementState?.value[i] === charConstraint;

if (
!isFixedCharacter(charConstraint) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {ElementState} from '../../../types';
import {getLeadingFixedCharacters} from './get-leading-fixed-characters';
import {isFixedCharacter} from './is-fixed-character';
import {validateValueWithMask} from './validate-value-with-mask';

export function guessValidValueByPattern(
elementState: ElementState,
mask: Array<RegExp | string>,
initialElementState: ElementState,
initialElementState: ElementState | null,
): ElementState {
let maskedFrom: number | null = null;
let maskedTo: number | null = null;
Expand Down Expand Up @@ -42,8 +43,17 @@ export function guessValidValueByPattern(
'',
);

const trailingFixedCharacters = getLeadingFixedCharacters(
mask,
maskedValue,
'',
initialElementState,
);

return {
value: maskedValue,
value: validateValueWithMask(maskedValue + trailingFixedCharacters, mask)
? maskedValue + trailingFixedCharacters
: maskedValue,
selection: [maskedFrom ?? maskedValue.length, maskedTo ?? maskedValue.length],
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function validateValueWithMask(
): boolean {
if (Array.isArray(maskExpression)) {
return (
value.length <= maskExpression.length &&
value.length === maskExpression.length &&
Array.from(value).every((char, i) => {
const charConstraint = maskExpression[i];

Expand Down
115 changes: 72 additions & 43 deletions projects/core/src/lib/utils/test/transform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {MaskitoOptions} from '../../types';
import {maskitoTransform} from '../transform';

describe('maskitoTransform', () => {
const maskitoOptions: MaskitoOptions = {
const numberOptions: MaskitoOptions = {
mask: /^\d+(,\d*)?$/,
preprocessor: ({elementState, data}) => {
const {value, selection} = elementState;
Expand All @@ -23,54 +23,83 @@ describe('maskitoTransform', () => {
return {value: newValue, selection: [from - deletedChars, to - deletedChars]};
},
};
const usPhoneOptions: MaskitoOptions = {
mask: [
'+',
'1',
' ',
'(',
/\d/,
/\d/,
/\d/,
')',
' ',
/\d/,
/\d/,
/\d/,
'-',
/\d/,
/\d/,
/\d/,
/\d/,
],
};

it('returns string if the first argument is a string', () => {
expect(typeof maskitoTransform('100', maskitoOptions)).toBe('string');
});
describe('Basic API', () => {
it('returns string if the first argument is a string', () => {
expect(typeof maskitoTransform('100', numberOptions)).toBe('string');
});

it('returns ElementState if the first argument is a ElementState', () => {
const res = maskitoTransform({value: '', selection: [0, 0]}, maskitoOptions);
it('returns ElementState if the first argument is a ElementState', () => {
const res = maskitoTransform({value: '', selection: [0, 0]}, numberOptions);

expect(res).toBeTruthy();
expect(typeof res).toBe('object');
});
expect(res).toBeTruthy();
expect(typeof res).toBe('object');
});

it('formats value using preprocessor', () => {
expect(maskitoTransform('100.42', maskitoOptions)).toBe('100,42');
expect(
maskitoTransform(
{
value: '100.42',
selection: [2, 2],
},
maskitoOptions,
),
).toEqual({value: '100,42', selection: [2, 2]});
});
it('formats value using preprocessor', () => {
expect(maskitoTransform('100.42', numberOptions)).toBe('100,42');
expect(
maskitoTransform(
{
value: '100.42',
selection: [2, 2],
},
numberOptions,
),
).toEqual({value: '100,42', selection: [2, 2]});
});

it('formats value using postprocessor', () => {
expect(maskitoTransform('0000,1234', maskitoOptions)).toBe('0,1234');
expect(
maskitoTransform(
{
value: '0000,1234',
selection: [6, 6],
},
maskitoOptions,
),
).toEqual({value: '0,1234', selection: [3, 3]});
it('formats value using postprocessor', () => {
expect(maskitoTransform('0000,1234', numberOptions)).toBe('0,1234');
expect(
maskitoTransform(
{
value: '0000,1234',
selection: [6, 6],
},
numberOptions,
),
).toEqual({value: '0,1234', selection: [3, 3]});
});

it('drops invalid characters (mask expression works)', () => {
expect(maskitoTransform('42Taiga UI42', numberOptions)).toBe('4242');
expect(
maskitoTransform(
{
value: '42Taiga UI42',
selection: [11, 11],
},
numberOptions,
),
).toEqual({value: '4242', selection: [3, 3]});
});
});

it('drops invalid characters (mask expression works)', () => {
expect(maskitoTransform('42Taiga UI42', maskitoOptions)).toBe('4242');
expect(
maskitoTransform(
{
value: '42Taiga UI42',
selection: [11, 11],
},
maskitoOptions,
),
).toEqual({value: '4242', selection: [3, 3]});
describe('Drop / Browser autofill cases', () => {
it('US Phone mask | Drops "+1(21"', () => {
expect(maskitoTransform('+1(21', usPhoneOptions)).toBe('+1 (21');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {DemoPath} from '@demo/path';

describe('Postfix | Percentage', () => {
beforeEach(() => {
cy.visit(`/${DemoPath.Postfix}`);
cy.get('#percentage input')
.should('be.visible')
.first()
.should('have.value', '')
.focus()
.as('input');
});

it('Empty input => Type 1 => 1|%', () => {
cy.get('@input')
.type('1')
.should('have.value', '1%')
.should('have.prop', 'selectionStart', 1)
.should('have.prop', 'selectionEnd', 1);
});

it('Empty input => Type 10 => 10|%', () => {
cy.get('@input')
.type('10')
.should('have.value', '10%')
.should('have.prop', 'selectionStart', 2)
.should('have.prop', 'selectionEnd', 2);
});

it('10|% => Backspace => 1|%', () => {
cy.get('@input')
.type('10')
.type('{backspace}')
.should('have.value', '1%')
.should('have.prop', 'selectionStart', 1)
.should('have.prop', 'selectionEnd', 1);
});

it('1|% => Backspace => Empty input', () => {
cy.get('@input')
.type('1')
.type('{backspace}')
.should('have.value', '')
.should('have.prop', 'selectionStart', 0)
.should('have.prop', 'selectionEnd', 0);
});

it('|53% => Delete => |3%', () => {
cy.get('@input')
.type('53')
.type('{moveToStart}{del}')
.should('have.value', '3%')
.should('have.prop', 'selectionStart', 0)
.should('have.prop', 'selectionEnd', 0);
});

it('|3% => Delete => Empty input', () => {
cy.get('@input')
.type('3')
.type('{moveToStart}{del}')
.should('have.value', '')
.should('have.prop', 'selectionStart', 0)
.should('have.prop', 'selectionEnd', 0);
});

it('cannot erase the % (by backspace)', () => {
cy.get('@input')
.type('42')
.type('{moveToEnd}')
.should('have.value', '42%')
.should('have.prop', 'selectionStart', '42%'.length)
.should('have.prop', 'selectionEnd', '42%'.length)
.type('{backspace}')
.should('have.value', '42%')
.should('have.prop', 'selectionStart', '42'.length)
.should('have.prop', 'selectionEnd', '42'.length);
});

it('cannot erase the % (by delete)', () => {
cy.get('@input')
.type('42')
.type('{del}')
.should('have.value', '42%')
.should('have.prop', 'selectionStart', '42%'.length)
.should('have.prop', 'selectionEnd', '42%'.length);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {DemoPath} from '@demo/path';

describe('Postfix | Time', () => {
beforeEach(() => {
cy.visit(`/${DemoPath.Postfix}`);
cy.get('#time input')
.should('be.visible')
.first()
.should('have.value', '')
.focus()
.as('input');
});

it('Empty input => 1159 => 11:59| pm', () => {
cy.get('@input')
.type('1159')
.should('have.value', '11:59 pm')
.should('have.prop', 'selectionStart', '11:59'.length)
.should('have.prop', 'selectionEnd', '11:59'.length);
});

it('11:59| pm => Backspace => 11:5', () => {
cy.get('@input')
.type('1159')
.type('{backspace}')
.should('have.value', '11:5')
.should('have.prop', 'selectionStart', '11:5'.length)
.should('have.prop', 'selectionEnd', '11:5'.length);
});

it('cannot erase the " pm" (by backspace)', () => {
cy.get('@input')
.type('1159')
.type('{moveToEnd}')
.should('have.value', '11:59 pm')
.should('have.prop', 'selectionStart', '11:59 pm'.length)
.should('have.prop', 'selectionEnd', '11:59 pm'.length)
.type('{backspace}'.repeat(3))
.should('have.value', '11:59 pm')
.should('have.prop', 'selectionStart', '11:59'.length)
.should('have.prop', 'selectionEnd', '11:59'.length);
});

it('cannot erase the " pm" (by delete)', () => {
cy.get('@input')
.type('1159')
.type('{del}'.repeat(2))
.should('have.value', '11:59 pm')
.should('have.prop', 'selectionStart', '11:59 p'.length)
.should('have.prop', 'selectionEnd', '11:59 p'.length);
});
});
10 changes: 10 additions & 0 deletions projects/demo/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ export const appRoutes: Routes = [
title: `Textarea`,
},
},
{
path: DemoPath.Postfix,
loadChildren: async () =>
import(`../pages/recipes/postfix/postfix-doc.module`).then(
m => m.PostfixDocModule,
),
data: {
title: `With postfix`,
},
},
// Other
{
path: DemoPath.Angular,
Expand Down
1 change: 1 addition & 0 deletions projects/demo/src/app/demo-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const enum DemoPath {
DateTime = 'kit/date-time',
Card = 'recipes/card',
Phone = 'recipes/phone',
Postfix = 'recipes/postfix',
Textarea = 'recipes/textarea',
Angular = 'angular',
BrowserSupport = 'browser-support',
Expand Down
6 changes: 6 additions & 0 deletions projects/demo/src/pages/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export const DEMO_PAGES: TuiDocPages = [
route: DemoPath.Textarea,
keywords: `textarea, latin, mask, recipe`,
},
{
section: 'Recipes',
title: 'With postfix',
route: DemoPath.Postfix,
keywords: `postfix, after, percent, am, pm, recipe`,
},
{
section: 'Other',
title: 'Angular',
Expand Down
Loading

0 comments on commit c7f6a1b

Please sign in to comment.