Skip to content

Commit

Permalink
feat(kit): add TuiExtractCountryCodePipe, TuiIsoToCountryCodePipe (
Browse files Browse the repository at this point in the history
  • Loading branch information
sviat9440 authored and mdlufy committed Aug 24, 2023
1 parent 6d14519 commit f3ab2e3
Show file tree
Hide file tree
Showing 26 changed files with 327 additions and 26 deletions.
4 changes: 0 additions & 4 deletions projects/kit/components/input-phone-international/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
export * from './const/countries';
export * from './country-iso-code';
export * from './input-phone-international.component';
export * from './input-phone-international.module';
export * from './input-phone-international.options';
export * from './interfaces/country';
export * from './tokens/countries-masks';
export * from './utils/extract-value-from-event';
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '@taiga-ui/cdk';
import {
TUI_MASK_SYMBOLS_REGEXP,
TUI_NON_DIGITS_REGEXP,
TuiFlagPipe,
TuiPrimitiveTextfieldComponent,
TuiSizeL,
Expand All @@ -35,18 +36,18 @@ import {
import {TuiCountryIsoCode} from '@taiga-ui/i18n';
import {TUI_ARROW} from '@taiga-ui/kit/components/arrow';
import {TuiInputPhoneComponent} from '@taiga-ui/kit/components/input-phone';
import {TuiToCountryCodePipe} from '@taiga-ui/kit/pipes';
import {FIXED_DROPDOWN_CONTROLLER_PROVIDER} from '@taiga-ui/kit/providers';
import {TUI_COUNTRIES} from '@taiga-ui/kit/tokens';
import {TUI_COUNTRIES, TUI_COUNTRIES_MASKS} from '@taiga-ui/kit/tokens';
import {tuiGetMaxAllowedPhoneLength, tuiIsoToCountryCode} from '@taiga-ui/kit/utils';
import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus';
import {Observable} from 'rxjs';

import {MASK_AFTER_CODE_REGEXP} from './const/countries';
import {
TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS,
TuiInputPhoneInternationalOptions,
} from './input-phone-international.options';
import {TUI_COUNTRIES_MASKS} from './tokens/countries-masks';
import {extractValueFromEvent} from './utils/extract-value-from-event';
import {tuiExtractValueFromEvent} from './utils/extract-value-from-event';

// @dynamic
@Component({
Expand All @@ -62,6 +63,7 @@ import {extractValueFromEvent} from './utils/extract-value-from-event';
FIXED_DROPDOWN_CONTROLLER_PROVIDER,
// TODO: for backward compatibility only. Drop in v4.0
TuiFlagPipe,
TuiToCountryCodePipe,
],
})
export class TuiInputPhoneInternationalComponent
Expand Down Expand Up @@ -109,6 +111,8 @@ export class TuiInputPhoneInternationalComponent
private readonly options: TuiInputPhoneInternationalOptions,
@Inject(TuiFlagPipe)
private readonly flagPipe: TuiFlagPipe,
@Inject(TuiToCountryCodePipe)
private readonly extractCountryCodePipe: TuiToCountryCodePipe,
) {
super(control, changeDetectorRef);
}
Expand All @@ -127,11 +131,11 @@ export class TuiInputPhoneInternationalComponent
}

get inputPhoneCountryCode(): string {
return this.isoToCountryCode(this.countryIsoCode);
return tuiIsoToCountryCode(this.countriesMasks, this.countryIsoCode);
}

get phoneMaskAfterCountryCode(): string {
const countryCode = this.isoToCountryCode(this.countryIsoCode);
const countryCode = this.inputPhoneCountryCode;

return this.calculateMaskAfterCountryCode(
this.countriesMasks[this.countryIsoCode],
Expand All @@ -150,17 +154,20 @@ export class TuiInputPhoneInternationalComponent
@HostListener('paste.capture.prevent.stop', ['$event'])
@HostListener('drop.capture.prevent.stop', ['$event'])
onPaste(event: ClipboardEvent | DragEvent): void {
let value = extractValueFromEvent(event);
const countryIsoCode = this.countries.find(countryIsoCode =>
value.startsWith(this.isoToCountryCode(countryIsoCode)),
let value = tuiExtractValueFromEvent(event).replace(TUI_NON_DIGITS_REGEXP, '');
const countryIsoCode = this.extractCountryCodePipe.transform(
value,
this.countries,
);

if (!countryIsoCode) {
this.updateValue(
`${this.inputPhoneCountryCode}${value}`
.replace(TUI_MASK_SYMBOLS_REGEXP, '')
.slice(0, this.getMaxAllowedLength(this.countryIsoCode)),
);
// @ts-ignore
this.value = `${this.inputPhoneCountryCode}${value}`
.replace(TUI_MASK_SYMBOLS_REGEXP, '')
.slice(
0,
tuiGetMaxAllowedPhoneLength(this.countriesMasks, this.countryIsoCode),
);

return;
}
Expand All @@ -174,7 +181,7 @@ export class TuiInputPhoneInternationalComponent
}

readonly isoToCountryCodeMapper: TuiMapper<TuiCountryIsoCode, string> = item =>
this.isoToCountryCode(item);
tuiIsoToCountryCode(this.countriesMasks, item);

/**
* @deprecated use `<img [src]="countryIsoCode | tuiFlagPipe" />`
Expand All @@ -190,7 +197,7 @@ export class TuiInputPhoneInternationalComponent
// recalculates mask inside inputPhone to prevent isoCode conflict
this.changeDetectorRef.detectChanges();

const maxLength = this.getMaxAllowedLength(isoCode);
const maxLength = tuiGetMaxAllowedPhoneLength(this.countriesMasks, isoCode);

if (this.value.length > maxLength) {
this.updateValue(this.value.slice(0, maxLength));
Expand All @@ -206,8 +213,12 @@ export class TuiInputPhoneInternationalComponent
this.close();
}

/**
* @deprecated use `{{ countryIsoCode | tuiIsoToCountryCode }}`
* TODO drop in v4.0
*/
isoToCountryCode(isoCode: TuiCountryIsoCode): string {
return this.countriesMasks[isoCode].replace(MASK_AFTER_CODE_REGEXP, '');
return tuiIsoToCountryCode(this.countriesMasks, isoCode);
}

onModelChange(value: string): void {
Expand All @@ -231,10 +242,6 @@ export class TuiInputPhoneInternationalComponent
this.open = false;
}

private getMaxAllowedLength(isoCode: TuiCountryIsoCode): number {
return this.countriesMasks[isoCode].replace(/[()\- ]/g, '').length;
}

private updateCountryIsoCode(code: TuiCountryIsoCode): void {
this.countryIsoCode = code;
this.countryIsoCodeChange.emit(code);
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions projects/kit/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './date-time-separator';
export * from './empty-mask';
export * from './group-class-names';
export * from './icon-blank';
export * from './mask-after-code-regexp';
export * from './masks';
export * from './math';
export * from './max-day-range-length-mapper';
Expand Down
1 change: 1 addition & 0 deletions projects/kit/constants/mask-after-code-regexp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MASK_AFTER_CODE_REGEXP = /\([#]+\)|[#\- ]/g;
File renamed without changes.
2 changes: 2 additions & 0 deletions projects/kit/pipes/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from '@taiga-ui/kit/pipes/field-error';
export * from '@taiga-ui/kit/pipes/filter-by-input';
export * from '@taiga-ui/kit/pipes/iso-to-country-code';
export * from '@taiga-ui/kit/pipes/stringify';
export * from '@taiga-ui/kit/pipes/stringify-content';
export * from '@taiga-ui/kit/pipes/to-country-code';
2 changes: 2 additions & 0 deletions projects/kit/pipes/iso-to-country-code/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './iso-to-country-code.module';
export * from './iso-to-country-code.pipe';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {NgModule} from '@angular/core';

import {TuiIsoToCountryCodePipe} from './iso-to-country-code.pipe';

@NgModule({
declarations: [TuiIsoToCountryCodePipe],
exports: [TuiIsoToCountryCodePipe],
})
export class TuiIsoToCountryCodeModule {}
18 changes: 18 additions & 0 deletions projects/kit/pipes/iso-to-country-code/iso-to-country-code.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {Inject, Pipe, PipeTransform} from '@angular/core';
import {TuiCountryIsoCode} from '@taiga-ui/i18n';
import {TUI_COUNTRIES_MASKS} from '@taiga-ui/kit/tokens';
import {tuiIsoToCountryCode} from '@taiga-ui/kit/utils';

@Pipe({
name: `tuiIsoToCountryCode`,
})
export class TuiIsoToCountryCodePipe implements PipeTransform {
constructor(
@Inject(TUI_COUNTRIES_MASKS)
private readonly countriesMasks: Record<TuiCountryIsoCode, string>,
) {}

transform(isoCode: TuiCountryIsoCode): string {
return tuiIsoToCountryCode(this.countriesMasks, isoCode);
}
}
5 changes: 5 additions & 0 deletions projects/kit/pipes/iso-to-country-code/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "index.ts"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {TestBed} from '@angular/core/testing';
import {TuiCountryIsoCode} from '@taiga-ui/i18n';

import {TuiIsoToCountryCodePipe} from '../iso-to-country-code.pipe';

describe(`TuiIsoToCountryCodePipe`, () => {
let pipe: TuiIsoToCountryCodePipe;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [TuiIsoToCountryCodePipe],
});

pipe = TestBed.inject(TuiIsoToCountryCodePipe);
});

it(`should transform US iso code to country code`, () => {
const transformed = pipe.transform(TuiCountryIsoCode.US);

expect(transformed).toEqual(`+1`);
});

it(`should transform GB iso code to country code`, () => {
const transformed = pipe.transform(TuiCountryIsoCode.GB);

expect(transformed).toEqual(`+44`);
});

it(`should transform AU iso code to country code`, () => {
const transformed = pipe.transform(TuiCountryIsoCode.AU);

expect(transformed).toEqual(`+61`);
});
});
2 changes: 2 additions & 0 deletions projects/kit/pipes/to-country-code/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './to-country-code.module';
export * from './to-country-code.pipe';
5 changes: 5 additions & 0 deletions projects/kit/pipes/to-country-code/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "index.ts"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {TestBed} from '@angular/core/testing';
import {TuiCountryIsoCode} from '@taiga-ui/i18n';
import {TuiIsoToCountryCodePipe, TuiToCountryCodePipe} from '@taiga-ui/kit';

describe(`TuiToCountryCodePipe`, () => {
let pipe: TuiToCountryCodePipe;

const testCases: Array<{
input: string;
countries: TuiCountryIsoCode[];
expected: TuiCountryIsoCode | undefined;
}> = [
{
input: `79123456789`,
countries: [TuiCountryIsoCode.US, TuiCountryIsoCode.RU, TuiCountryIsoCode.UA],
expected: TuiCountryIsoCode.RU,
},
{
input: `12024561111`,
countries: [TuiCountryIsoCode.US, TuiCountryIsoCode.RU, TuiCountryIsoCode.UA],
expected: TuiCountryIsoCode.US,
},
{
input: `380442228888`,
countries: [TuiCountryIsoCode.US, TuiCountryIsoCode.RU, TuiCountryIsoCode.UA],
expected: TuiCountryIsoCode.UA,
},
{
input: `0123456789`,
countries: [TuiCountryIsoCode.DE],
expected: undefined,
},

// RU or KZ phones cases

{
input: `79123456789`,
countries: [TuiCountryIsoCode.KZ],
expected: undefined,
},
{
input: `76861234568`,
countries: [TuiCountryIsoCode.RU],
expected: undefined,
},

{
input: `79123456789`,
countries: [TuiCountryIsoCode.RU, TuiCountryIsoCode.KZ],
expected: TuiCountryIsoCode.RU,
},
{
input: `79123456789`,
countries: [TuiCountryIsoCode.KZ, TuiCountryIsoCode.RU],
expected: TuiCountryIsoCode.RU,
},
{
input: `76861234568`,
countries: [TuiCountryIsoCode.KZ, TuiCountryIsoCode.RU],
expected: TuiCountryIsoCode.KZ,
},
{
input: `76861234568`,
countries: [TuiCountryIsoCode.RU, TuiCountryIsoCode.KZ],
expected: TuiCountryIsoCode.KZ,
},
];

beforeEach(() => {
TestBed.configureTestingModule({
providers: [TuiIsoToCountryCodePipe, TuiToCountryCodePipe],
});

pipe = TestBed.inject(TuiToCountryCodePipe);
});

testCases.forEach(({input, countries, expected}) => {
it(`should return expected result for input: "${input}" and countries: ${countries}`, () => {
expect(pipe.transform(input, countries)).toEqual(expected);
});
});
});
9 changes: 9 additions & 0 deletions projects/kit/pipes/to-country-code/to-country-code.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {NgModule} from '@angular/core';

import {TuiToCountryCodePipe} from './to-country-code.pipe';

@NgModule({
declarations: [TuiToCountryCodePipe],
exports: [TuiToCountryCodePipe],
})
export class TuiExtractCountryCodeModule {}
60 changes: 60 additions & 0 deletions projects/kit/pipes/to-country-code/to-country-code.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {Inject, Pipe, PipeTransform} from '@angular/core';
import {CHAR_PLUS} from '@taiga-ui/cdk';
import {TuiCountryIsoCode} from '@taiga-ui/i18n';
import {TUI_COUNTRIES_MASKS} from '@taiga-ui/kit/tokens';
import {
tuiGetMaxAllowedPhoneLength,
tuiIsoToCountryCode,
tuiNotKzRegion,
} from '@taiga-ui/kit/utils';

@Pipe({
name: `tuiToCountryCode`,
})
export class TuiToCountryCodePipe implements PipeTransform {
constructor(
@Inject(TUI_COUNTRIES_MASKS)
private readonly countriesMasks: Record<TuiCountryIsoCode, string>,
) {}

transform(
value: string,
countries: readonly TuiCountryIsoCode[],
): TuiCountryIsoCode | undefined {
return countries.find(countryIsoCode => {
const ruCodeTest =
countryIsoCode === TuiCountryIsoCode.RU &&
/^[78]/.test(value) &&
/^(?!880[1-9 ])/.test(value) &&
value.length + 1 ===
tuiGetMaxAllowedPhoneLength(
this.countriesMasks,
TuiCountryIsoCode.RU,
);

const matched =
ruCodeTest ||
(value.startsWith(
tuiIsoToCountryCode(this.countriesMasks, countryIsoCode).replace(
CHAR_PLUS,
``,
),
) &&
value.length + 1 ===
tuiGetMaxAllowedPhoneLength(this.countriesMasks, countryIsoCode));

if (matched) {
switch (countryIsoCode) {
case TuiCountryIsoCode.RU:
return tuiNotKzRegion(value);
case TuiCountryIsoCode.KZ:
return !tuiNotKzRegion(value);
default:
return true;
}
}

return false;
});
}
}
Loading

0 comments on commit f3ab2e3

Please sign in to comment.