Skip to content

Commit

Permalink
feat(phone): add non-strict phone mask
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirpotekhin committed Sep 6, 2023
1 parent 9b4b402 commit 86096ad
Show file tree
Hide file tree
Showing 18 changed files with 511 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import {DemoPath} from '@demo/constants';

import {BROWSER_SUPPORTS_REAL_EVENTS} from '../../../support/constants';

describe('Phone', () => {
describe('Non-strict', () => {
beforeEach(() => {
cy.visit(`/${DemoPath.PhonePackage}/API?strict=false`);
cy.get('#demo-content input')
.should('be.visible')
.first()
.focus()
.as('input');
});

describe('basic typing (1 character per keydown)', () => {
const tests = [
// [Typed value, Masked value, caretIndex]
['7920', '+7 920', '+7 920'.length],
['7920341', '+7 920 341', '+7 920 341'.length],
['792034156', '+7 920 341-56', '+7 920 341-56'.length],
['79203415627', '+7 920 341-56-27', '+7 920 341-56-27'.length],
['792034156274234123', '+7 920 341-56-27', '+7 920 341-56-27'.length],
['79 nd4 e', '+7 94', '+7 94'.length],
] as const;

tests.forEach(([typedValue, maskedValue, caretIndex]) => {
it(`Type "${typedValue}" => "${maskedValue}"`, () => {
cy.get('@input')
.type(typedValue)
.should('have.value', maskedValue)
.should('have.prop', 'selectionStart', caretIndex)
.should('have.prop', 'selectionEnd', caretIndex);
});
});
});

describe('basic erasing (value = "+7 920 424-11-32"', () => {
beforeEach(() => {
cy.get('@input').type('+79204241132');
});

const tests = [
// [How many times "Backspace"-key was pressed, caretPosition, Masked value]
[1, '+7 920 424-11-3'.length, '+7 920 424-11-3'],
[2, '+7 920 424-11'.length, '+7 920 424-11'],
[3, '+7 920 424-1'.length, '+7 920 424-1'],
[4, '+7 920 424'.length, '+7 920 424'],
[13, ''.length, ''],
] as const;

tests.forEach(([n, caretIndex, maskedValue]) => {
it(`Backspace x${n} => "${maskedValue}"`, () => {
cy.get('@input')
.type('{backspace}'.repeat(n))
.should('have.value', maskedValue)
.should('have.prop', 'selectionStart', caretIndex)
.should('have.prop', 'selectionEnd', caretIndex);
});
});
});

describe('Editing somewhere in the middle of a value (NOT the last character)', () => {
beforeEach(() => {
cy.get('@input').type('+7 920 424-11-32');
});

it('+7 920 424-1|1-32 => Backspace => +7 920 424-|13-2 => Type "2" => +7 920 424-2|1-32', () => {
cy.get('@input')
.type('{leftArrow}'.repeat('13-2'.length))
.type('{backspace}')
.should('have.value', '+7 920 424-13-2')
.should('have.prop', 'selectionStart', '+7 920 424-'.length)
.should('have.prop', 'selectionEnd', '+7 920 424-'.length)
.type('2')
.should('have.value', '+7 920 424-21-32')
.should('have.prop', 'selectionStart', '+7 920 424-2'.length)
.should('have.prop', 'selectionEnd', '+7 920 424-2'.length);
});

it('+7 9|20 424-11-32 => Backspace => +7 2|04241132', () => {
cy.get('@input')
.type('{leftArrow}'.repeat('20 424-11-32'.length))
.type('{backspace}')
.should('have.value', '+7 204241132')
.should('have.prop', 'selectionStart', '+7 '.length)
.should('have.prop', 'selectionEnd', '+7 '.length);
});
});

describe('Text selection', () => {
beforeEach(() => {
cy.get('@input').type('+7 920 424-11-32');
});

describe(
'Select range and press Backspace',
BROWSER_SUPPORTS_REAL_EVENTS,
() => {
it('+7 920 424-11-32 => Select "+7 920 424-|11|-32" => Backspace => +7 920 424-|32', () => {
cy.get('@input')
.type('{leftArrow}'.repeat('-32'.length))
.realPress([
'Shift',
...Array('11'.length).fill('ArrowLeft'),
]);

cy.get('@input')
.type('{backspace}')
.should('have.value', '+7 920 424-32')
.should('have.prop', 'selectionStart', '+7 920 424-'.length)
.should('have.prop', 'selectionEnd', '+7 920 424-'.length);
});

it('+7 920 424-11-32 => Select "+7 920 424-1|1-3|2" => Backspace => +7 920 424-1|2', () => {
cy.get('@input')
.type('{leftArrow}')
.realPress([
'Shift',
...Array('1-3'.length).fill('ArrowLeft'),
]);

cy.get('@input')
.type('{backspace}')
.should('have.value', '+7 920 424-12')
.should('have.prop', 'selectionStart', '+7 920 424-1'.length)
.should('have.prop', 'selectionEnd', '+7 920 424-1'.length);
});
},
);

describe(
'Select range and type a digit',
BROWSER_SUPPORTS_REAL_EVENTS,
() => {
it('+7 920 424-11-32 => Select "+7 920 424-|11|-32" => Type "5" => +7 920 424-5|3-2', () => {
cy.get('@input')
.type('{leftArrow}'.repeat('-32'.length))
.realPress([
'Shift',
...Array('11'.length).fill('ArrowLeft'),
]);

cy.get('@input')
.type('5')
.should('have.value', '+7 920 424-53-2')
.should('have.prop', 'selectionStart', '+7 920 424-5'.length)
.should('have.prop', 'selectionEnd', '+7 920 424-5'.length);
});

it('+7 920 424-11-32 => Select "+7 920 424-1|1-3|2" => Type "5" => +7 920 424-15-|2', () => {
cy.get('@input')
.type('{leftArrow}')
.realPress([
'Shift',
...Array('1-3'.length).fill('ArrowLeft'),
]);

cy.get('@input')
.type('5')
.should('have.value', '+7 920 424-15-2')
.should(
'have.prop',
'selectionStart',
'+7 920 424-15-'.length,
)
.should('have.prop', 'selectionEnd', '+7 920 424-15-'.length);
});
},
);
});
});

describe('Some countries', () => {
it('US: +1 212 343-3355', () => {
openCounrty('US');

cy.get('@input').type('12123433355');
cy.get('@input').should('have.value', '+1 212 343-3355');
});

it('KZ: +7 771 931-1111', () => {
openCounrty('KZ');

cy.get('@input').type('77719311111');
cy.get('@input').should('have.value', '+7 771 931-1111');
});

it('BY: +375 44 748-82-69', () => {
openCounrty('BY');

cy.get('@input').type('375447488269');
cy.get('@input').should('have.value', '+375 44 748-82-69');
});
});
});

function openCounrty(code: string): void {
cy.visit(`/${DemoPath.PhonePackage}/API?strict=false&countryCode=${code}`);
cy.get('#demo-content input').should('be.visible').first().focus().as('input');
}
35 changes: 35 additions & 0 deletions projects/demo/src/pages/phone/examples/3-non-strict/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {maskitoGetCountryFromNumber} from '@maskito/phone';
import metadata from 'libphonenumber-js/min/metadata';

import mask from './mask';

@Component({
selector: 'phone-doc-example-3',
template: `
<tui-input
tuiTextfieldCustomContent="tuiIconPhoneLarge"
[style.max-width.rem]="30"
[(ngModel)]="value"
>
Basic
<input
tuiTextfield
autocomplete="tel"
inputmode="tel"
[maskito]="mask"
/>
</tui-input>
<div>Country code: {{ countryIsoCode }}</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PhoneMaskDocExample3 {
value = '';
readonly mask = mask;

get countryIsoCode(): string {
return maskitoGetCountryFromNumber(this.value, metadata) || '';
}
}
8 changes: 8 additions & 0 deletions projects/demo/src/pages/phone/examples/3-non-strict/mask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {maskitoPhoneOptionsGenerator} from '@maskito/phone';
import metadata from 'libphonenumber-js/min/metadata';

export default maskitoPhoneOptionsGenerator({
countryIsoCode: 'RU',
metadata,
strict: false,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
import {MASKITO_DEFAULT_OPTIONS} from '@maskito/core';
import {maskitoPhoneOptionsGenerator} from '@maskito/phone';

@Component({
selector: 'phone-doc-example-4',
template: `
<tui-input
tuiTextfieldCustomContent="tuiIconPhoneLarge"
[style.max-width.rem]="30"
[(ngModel)]="value"
>
Lazy metadata
<input
tuiTextfield
autocomplete="tel"
inputmode="tel"
[maskito]="mask"
/>
</tui-input>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PhoneMaskDocExample4 implements OnInit {
value = '+7 920 123-4567';
mask? = MASKITO_DEFAULT_OPTIONS;

async ngOnInit(): Promise<void> {
const metadata = await import('libphonenumber-js/min/metadata').then(
m => m.default,
);

this.mask = maskitoPhoneOptionsGenerator({
countryIsoCode: 'RU',
metadata,
});
}
}
17 changes: 17 additions & 0 deletions projects/demo/src/pages/phone/phone-doc.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,25 @@ export class PhoneDocComponent implements GeneratorOptions {
),
};

readonly nonStrict: TuiDocExample = {
[DocExamplePrimaryTab.MaskitoOptions]: import(
'./examples/3-non-strict/mask.ts?raw'
),
};

readonly lazyMetadata: TuiDocExample = {
[DocExamplePrimaryTab.MaskitoOptions]: import(
'./examples/2-validation/mask.ts?raw'
),
[DocExamplePrimaryTab.Angular]: import(
'./examples/2-validation/component.ts?raw'
),
};

metadata = metadata;

strict = true;

countryCodeVariants = getCountries(this.metadata);

countryIsoCode: CountryCode = 'RU';
Expand Down
12 changes: 11 additions & 1 deletion projects/demo/src/pages/phone/phone-doc.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {MaskitoModule} from '@maskito/angular';
import {TuiAddonDocModule, tuiGenerateRoutes} from '@taiga-ui/addon-doc';
import {
TuiErrorModule,
TuiFlagPipeModule,
TuiLinkModule,
TuiNotificationModule,
TuiTextfieldControllerModule,
Expand All @@ -14,6 +15,8 @@ import {TuiFieldErrorPipeModule, TuiInputModule} from '@taiga-ui/kit';

import {PhoneMaskDocExample1} from './examples/1-basic/component';
import {PhoneMaskDocExample2} from './examples/2-validation/component';
import {PhoneMaskDocExample3} from './examples/3-non-strict/component';
import {PhoneMaskDocExample4} from './examples/4-lazy-metadata/component';
import {PhoneDocComponent} from './phone-doc.component';

@NgModule({
Expand All @@ -24,14 +27,21 @@ import {PhoneDocComponent} from './phone-doc.component';
MaskitoModule,
TuiAddonDocModule,
TuiInputModule,
TuiFlagPipeModule,
TuiLinkModule,
TuiErrorModule,
TuiNotificationModule,
TuiFieldErrorPipeModule,
TuiTextfieldControllerModule,
RouterModule.forChild(tuiGenerateRoutes(PhoneDocComponent)),
],
declarations: [PhoneDocComponent, PhoneMaskDocExample1, PhoneMaskDocExample2],
declarations: [
PhoneDocComponent,
PhoneMaskDocExample1,
PhoneMaskDocExample2,
PhoneMaskDocExample3,
PhoneMaskDocExample4,
],
exports: [PhoneDocComponent],
})
export class PhoneDocModule {}
Loading

0 comments on commit 86096ad

Please sign in to comment.