From 08f924de9c3f756a72ce8cee0ac0f3072557b338 Mon Sep 17 00:00:00 2001 From: Alex Inkin Date: Thu, 19 Dec 2024 18:51:20 +0400 Subject: [PATCH] feat(experimental): `InputPhoneInternational` refactor to new textfield approach (#9976) Co-authored-by: taiga-family-bot Co-authored-by: Nikita Barsukov --- .../input-card/input-card.component.ts | 4 +- .../input-cvc/input-cvc.directive.ts | 3 +- .../input-expire/input-expire.directive.ts | 3 +- .../dropdown-mobile.directive.ts | 3 +- .../textfield/textfield.directive.ts | 17 +- .../dropdown/dropdown-open.directive.ts | 18 +- .../core/styles/components/textfield.less | 1 + .../styles/theme/appearance/textfield.less | 23 +- .../input-phone-international.pw.spec.ts | 18 +- .../utils/page-objects/index.ts | 1 + .../input-phone-international.po.ts | 7 + projects/demo/src/modules/app/pages.ts | 1 + .../input-card/examples/1/index.html | 3 - .../input-card/examples/2/index.html | 1 - .../input-card/examples/import/template.md | 3 - .../modules/components/input-card/index.html | 1 - .../examples/1/index.html | 13 +- .../examples/1/index.ts | 9 +- .../examples/2/index.html | 18 +- .../examples/2/index.ts | 11 +- .../examples/3/index.html | 10 + .../examples/3/index.ts | 52 ++-- .../examples/4/index.html | 16 +- .../examples/4/index.less | 3 - .../examples/4/index.ts | 12 +- .../examples/5/index.html | 16 +- .../examples/5/index.ts | 9 +- .../examples/6/index.ts | 31 +++ .../examples/7/index.html | 5 + .../examples/7/index.ts | 53 ++++ .../examples/import/import.md | 3 +- .../examples/import/template.md | 11 +- .../input-phone-international/index.html | 84 ++++-- .../input-phone-international/index.ts | 7 +- .../components/input/examples/4/index.html | 1 - .../sheet-dialog/examples/4/index.ts | 2 +- .../thumbnail-card/examples/5/index.html | 1 - projects/demo/src/utils/demo.ts | 2 + projects/demo/src/utils/disabled.directive.ts | 19 ++ projects/demo/src/utils/index.ts | 1 + projects/experimental/components/index.ts | 1 + .../input-phone-international/index.ts | 1 + .../input-phone-international.component.ts | 242 +++++++++++++++++ .../input-phone-international.style.less | 89 +++++++ .../input-phone-international.template.html | 67 +++++ .../input-phone-international/ng-package.json | 5 + ...nput-phone-international.component.spec.ts | 250 ++++++++++++++++++ .../experimental/components/ng-package.json | 5 + projects/experimental/index.ts | 2 +- .../input-password.component.ts | 4 +- 50 files changed, 1019 insertions(+), 143 deletions(-) create mode 100644 projects/demo-playwright/utils/page-objects/input-phone-international.po.ts create mode 100644 projects/demo/src/modules/components/input-phone-international/examples/3/index.html delete mode 100644 projects/demo/src/modules/components/input-phone-international/examples/4/index.less create mode 100644 projects/demo/src/modules/components/input-phone-international/examples/6/index.ts create mode 100644 projects/demo/src/modules/components/input-phone-international/examples/7/index.html create mode 100644 projects/demo/src/modules/components/input-phone-international/examples/7/index.ts create mode 100644 projects/demo/src/utils/disabled.directive.ts create mode 100644 projects/experimental/components/index.ts create mode 100644 projects/experimental/components/input-phone-international/index.ts create mode 100644 projects/experimental/components/input-phone-international/input-phone-international.component.ts create mode 100644 projects/experimental/components/input-phone-international/input-phone-international.style.less create mode 100644 projects/experimental/components/input-phone-international/input-phone-international.template.html create mode 100644 projects/experimental/components/input-phone-international/ng-package.json create mode 100644 projects/experimental/components/input-phone-international/test/input-phone-international.component.spec.ts create mode 100644 projects/experimental/components/ng-package.json diff --git a/projects/addon-commerce/components/input-card/input-card.component.ts b/projects/addon-commerce/components/input-card/input-card.component.ts index baa506ef9dc3..7f8907a44247 100644 --- a/projects/addon-commerce/components/input-card/input-card.component.ts +++ b/projects/addon-commerce/components/input-card/input-card.component.ts @@ -15,7 +15,7 @@ import {MaskitoDirective} from '@maskito/angular'; import {TUI_MASK_CARD} from '@taiga-ui/addon-commerce/constants'; import {TUI_PAYMENT_SYSTEM_ICONS} from '@taiga-ui/addon-commerce/tokens'; import {tuiControlValue} from '@taiga-ui/cdk/observables'; -import {TuiTextfieldContent} from '@taiga-ui/core/components/textfield'; +import {TuiTextfieldContent, TuiWithTextfield} from '@taiga-ui/core/components/textfield'; import {tuiInjectIconResolver} from '@taiga-ui/core/tokens'; import {tuiMaskito} from '@taiga-ui/kit/utils'; import {distinctUntilChanged, map, skip, startWith, switchMap, timer} from 'rxjs'; @@ -38,7 +38,7 @@ import {TUI_INPUT_CARD_OPTIONS} from './input-card.options'; styleUrls: ['./input-card.style.less'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, - hostDirectives: [MaskitoDirective], + hostDirectives: [MaskitoDirective, TuiWithTextfield], host: { inputmode: 'numeric', placeholder: '0000 0000 0000 0000', diff --git a/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts b/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts index d59a6df10dd7..e4a930489924 100644 --- a/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts +++ b/projects/addon-commerce/components/input-cvc/input-cvc.directive.ts @@ -3,12 +3,13 @@ import {Directive, inject, Input} from '@angular/core'; import {MaskitoDirective} from '@maskito/angular'; import {TUI_INPUT_CARD_OPTIONS} from '@taiga-ui/addon-commerce/components/input-card'; import {TUI_MASK_CVC} from '@taiga-ui/addon-commerce/constants'; +import {TuiWithTextfield} from '@taiga-ui/core/components/textfield'; import {tuiMaskito} from '@taiga-ui/kit/utils'; @Directive({ standalone: true, selector: 'input[tuiInputCVC]', - hostDirectives: [MaskitoDirective], + hostDirectives: [MaskitoDirective, TuiWithTextfield], host: { inputmode: 'numeric', '[autocomplete]': 'autocomplete ? "cc-csc" : "off"', diff --git a/projects/addon-commerce/components/input-expire/input-expire.directive.ts b/projects/addon-commerce/components/input-expire/input-expire.directive.ts index 570a77fe4070..326347457a64 100644 --- a/projects/addon-commerce/components/input-expire/input-expire.directive.ts +++ b/projects/addon-commerce/components/input-expire/input-expire.directive.ts @@ -2,12 +2,13 @@ import {Directive, inject, Input} from '@angular/core'; import {MaskitoDirective} from '@maskito/angular'; import {TUI_INPUT_CARD_OPTIONS} from '@taiga-ui/addon-commerce/components/input-card'; import {TUI_MASK_EXPIRE} from '@taiga-ui/addon-commerce/constants'; +import {TuiWithTextfield} from '@taiga-ui/core/components/textfield'; import {tuiMaskito} from '@taiga-ui/kit/utils'; @Directive({ standalone: true, selector: 'input[tuiInputExpire]', - hostDirectives: [MaskitoDirective], + hostDirectives: [MaskitoDirective, TuiWithTextfield], host: { inputmode: 'numeric', placeholder: '00/00', diff --git a/projects/addon-mobile/directives/dropdown-mobile/dropdown-mobile.directive.ts b/projects/addon-mobile/directives/dropdown-mobile/dropdown-mobile.directive.ts index b04fb1d5428b..b98498a8e13f 100644 --- a/projects/addon-mobile/directives/dropdown-mobile/dropdown-mobile.directive.ts +++ b/projects/addon-mobile/directives/dropdown-mobile/dropdown-mobile.directive.ts @@ -32,7 +32,8 @@ export class TuiDropdownMobile { if ( !this.isMobile || !tuiIsHTMLElement(event.target) || - !event.target.matches('input,textarea') + !event.target.matches('input,textarea') || + this.tuiDropdownMobile ) { return; } diff --git a/projects/core/components/textfield/textfield.directive.ts b/projects/core/components/textfield/textfield.directive.ts index 4a08dea3757a..c4c0241a43fd 100644 --- a/projects/core/components/textfield/textfield.directive.ts +++ b/projects/core/components/textfield/textfield.directive.ts @@ -28,7 +28,7 @@ export class TuiTextfieldBase implements OnChanges { protected readonly s = tuiAppearanceState(null); protected readonly m = tuiAppearanceMode(this.mode); protected readonly f = tuiAppearanceFocus( - computed(() => this.focused() || this.textfield.focused()), + computed(() => this.focused() ?? this.textfield.focused()), ); protected readonly el = tuiInjectElement(); @@ -99,7 +99,9 @@ export class TuiTextfieldBase implements OnChanges { @Directive({ standalone: true, - selector: 'input[tuiTextfield]', + // TODO: Remove :not in v.5 + selector: + 'input[tuiTextfield]:not([tuiInputCard]):not([tuiInputExpire]):not([tuiInputCVC])', hostDirectives: [TuiNativeValidator, TuiAppearance], host: { '[id]': 'textfield.id', @@ -111,3 +113,14 @@ export class TuiTextfieldBase implements OnChanges { }, }) export class TuiTextfieldDirective extends TuiTextfieldBase {} + +@Directive({ + standalone: true, + hostDirectives: [ + { + directive: TuiTextfieldDirective, + inputs: ['invalid', 'focused', 'readOnly', 'state'], + }, + ], +}) +export class TuiWithTextfield {} diff --git a/projects/core/directives/dropdown/dropdown-open.directive.ts b/projects/core/directives/dropdown/dropdown-open.directive.ts index 87b4db9f4f02..7ce0b3ee1ce1 100644 --- a/projects/core/directives/dropdown/dropdown-open.directive.ts +++ b/projects/core/directives/dropdown/dropdown-open.directive.ts @@ -137,19 +137,15 @@ export class TuiDropdownOpen implements OnChanges { protected onKeydown({key, target, defaultPrevented}: KeyboardEvent): void { if ( - defaultPrevented || - !tuiIsEditingKey(key) || - !this.editable || - !this.focused || - !this.directive.content || - !tuiIsHTMLElement(target) || - (tuiIsElementEditable(target) && target !== this.host) + !defaultPrevented && + tuiIsEditingKey(key) && + this.editable && + this.focused && + tuiIsHTMLElement(target) && + !tuiIsElementEditable(target) ) { - return; + this.host.focus({preventScroll: true}); } - - this.update(true); - this.host.focus({preventScroll: true}); } private get host(): HTMLElement { diff --git a/projects/core/styles/components/textfield.less b/projects/core/styles/components/textfield.less index 1346b7d32b13..e12904d30d3b 100644 --- a/projects/core/styles/components/textfield.less +++ b/projects/core/styles/components/textfield.less @@ -279,6 +279,7 @@ tui-textfield { gap: 0.25rem; margin-inline-start: auto; isolation: isolate; + border-radius: inherit; > tui-icon { pointer-events: auto; diff --git a/projects/core/styles/theme/appearance/textfield.less b/projects/core/styles/theme/appearance/textfield.less index 232447effc6f..4b37a83c2cbf 100644 --- a/projects/core/styles/theme/appearance/textfield.less +++ b/projects/core/styles/theme/appearance/textfield.less @@ -12,8 +12,28 @@ outline-offset: -1px; border: none; + // TODO: Remove tuiWrapper hack in v5 + &:not([tuiWrapper])::before, + &:not([tuiWrapper])::after { + .transition(~'color, transform'); + + color: var(--tui-text-tertiary); + } + .appearance-hover({ --t-shadow: 0 0.125rem 0.3125rem rgba(0, 0, 0, 0.16); + + &:not([tuiWrapper]):before, + &:not([tuiWrapper]):after { + color: var(--tui-text-secondary); + } + }); + + .appearance-active({ + &:not([tuiWrapper]):before, + &:not([tuiWrapper]):after { + color: var(--tui-text-primary); + } }); .appearance-focus({ @@ -33,7 +53,8 @@ &[data-mode~='readonly'], input&:read-only:not([data-mode]) { - box-shadow: none; + background: transparent !important; + box-shadow: none !important; outline-color: var(--tui-border-normal) !important; } diff --git a/projects/demo-playwright/tests/legacy/input-phone-international/input-phone-international.pw.spec.ts b/projects/demo-playwright/tests/legacy/input-phone-international/input-phone-international.pw.spec.ts index 20aefaef2602..e1dcce935938 100644 --- a/projects/demo-playwright/tests/legacy/input-phone-international/input-phone-international.pw.spec.ts +++ b/projects/demo-playwright/tests/legacy/input-phone-international/input-phone-international.pw.spec.ts @@ -1,18 +1,24 @@ import {DemoRoute} from '@demo/routes'; -import {TuiDocumentationPagePO, tuiGoto} from '@demo-playwright/utils'; +import { + TuiDocumentationPagePO, + tuiGoto, + TuiInputPhoneInternationalPO, +} from '@demo-playwright/utils'; import type {Locator} from '@playwright/test'; import {expect, test} from '@playwright/test'; test.describe('InputPhoneInternational', () => { test.describe('API page', () => { let example: Locator; - let select!: Locator; let dropdown!: Locator; + let inputPhoneInternational!: TuiInputPhoneInternationalPO; test.beforeEach(({page}) => { example = new TuiDocumentationPagePO(page).apiPageExample; - select = example.locator('tui-input-phone-international select'); dropdown = page.locator('tui-dropdown'); + inputPhoneInternational = new TuiInputPhoneInternationalPO( + example.locator('tui-textfield'), + ); }); test('Open dropdown if readonly=false', async ({page}) => { @@ -23,7 +29,7 @@ test.describe('InputPhoneInternational', () => { await expect(dropdown).not.toBeAttached(); - await select.click(); + await inputPhoneInternational.select.click(); await expect(dropdown).toBeAttached(); }); @@ -33,7 +39,9 @@ test.describe('InputPhoneInternational', () => { await expect(dropdown).not.toBeAttached(); - await select.click(); + await expect(async () => { + await inputPhoneInternational.select.click(); + }).rejects.toThrow(); await expect(dropdown).not.toBeAttached(); }); diff --git a/projects/demo-playwright/utils/page-objects/index.ts b/projects/demo-playwright/utils/page-objects/index.ts index 89273f67557c..478c42399d06 100644 --- a/projects/demo-playwright/utils/page-objects/index.ts +++ b/projects/demo-playwright/utils/page-objects/index.ts @@ -12,6 +12,7 @@ export * from './input-date-time.po'; export * from './input-month.po'; export * from './input-month-range.po'; export * from './input-phone.po'; +export * from './input-phone-international.po'; export * from './input-range.po'; export * from './input-slider.po'; export * from './input-tag.po'; diff --git a/projects/demo-playwright/utils/page-objects/input-phone-international.po.ts b/projects/demo-playwright/utils/page-objects/input-phone-international.po.ts new file mode 100644 index 000000000000..e955eb8f3c4d --- /dev/null +++ b/projects/demo-playwright/utils/page-objects/input-phone-international.po.ts @@ -0,0 +1,7 @@ +import type {Locator} from '@playwright/test'; + +import {TuiTextfieldWithDataListPO} from './textfield-with-data-list.po'; + +export class TuiInputPhoneInternationalPO extends TuiTextfieldWithDataListPO { + public select: Locator = this.host.locator('.t-ipi-select'); +} diff --git a/projects/demo/src/modules/app/pages.ts b/projects/demo/src/modules/app/pages.ts index d978607349e5..d96e7120f696 100644 --- a/projects/demo/src/modules/app/pages.ts +++ b/projects/demo/src/modules/app/pages.ts @@ -426,6 +426,7 @@ export const pages: DocRoutePages = [ title: 'Form', keywords: 'форма, поле, кнопка, группировка, группа', route: DemoRoute.FormLayout, + meta: {scheme: 'beaver', name: 'form'}, }, { section: 'Components', diff --git a/projects/demo/src/modules/components/input-card/examples/1/index.html b/projects/demo/src/modules/components/input-card/examples/1/index.html index 5668af0f3c57..4519ffc88b45 100644 --- a/projects/demo/src/modules/components/input-card/examples/1/index.html +++ b/projects/demo/src/modules/components/input-card/examples/1/index.html @@ -4,7 +4,6 @@ @@ -15,7 +14,6 @@ @@ -23,7 +21,6 @@ diff --git a/projects/demo/src/modules/components/input-card/examples/2/index.html b/projects/demo/src/modules/components/input-card/examples/2/index.html index e1a08f93bacd..b6be11eda34b 100644 --- a/projects/demo/src/modules/components/input-card/examples/2/index.html +++ b/projects/demo/src/modules/components/input-card/examples/2/index.html @@ -3,7 +3,6 @@ @@ -13,7 +12,6 @@ @@ -21,7 +19,6 @@ diff --git a/projects/demo/src/modules/components/input-card/index.html b/projects/demo/src/modules/components/input-card/index.html index 9500f7a33f83..3f8326f7bb26 100644 --- a/projects/demo/src/modules/components/input-card/index.html +++ b/projects/demo/src/modules/components/input-card/index.html @@ -54,7 +54,6 @@ + + + diff --git a/projects/demo/src/modules/components/input-phone-international/examples/1/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/1/index.ts index 8409bc979af1..c2bd003a65aa 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/1/index.ts +++ b/projects/demo/src/modules/components/input-phone-international/examples/1/index.ts @@ -2,16 +2,15 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; -import { - TuiInputPhoneInternational, - tuiInputPhoneInternationalOptionsProvider, -} from '@taiga-ui/kit'; +import {tuiInputPhoneInternationalOptionsProvider} from '@taiga-ui/kit'; import {defer} from 'rxjs'; @Component({ standalone: true, - imports: [FormsModule, TuiInputPhoneInternational], + imports: [FormsModule, TuiInputPhoneInternational, TuiTextfield], templateUrl: './index.html', encapsulation, changeDetection, diff --git a/projects/demo/src/modules/components/input-phone-international/examples/2/index.html b/projects/demo/src/modules/components/input-phone-international/examples/2/index.html index af29a7108750..6f896c1f37ea 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/2/index.html +++ b/projects/demo/src/modules/components/input-phone-international/examples/2/index.html @@ -1,8 +1,10 @@ - - Type your number - + + + + diff --git a/projects/demo/src/modules/components/input-phone-international/examples/2/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/2/index.ts index c6790ff2b933..c789b14a18ad 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/2/index.ts +++ b/projects/demo/src/modules/components/input-phone-international/examples/2/index.ts @@ -3,9 +3,10 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; import { - TuiInputPhoneInternational, tuiInputPhoneInternationalOptionsProvider, TuiSortCountriesPipe, } from '@taiga-ui/kit'; @@ -14,7 +15,13 @@ import {defer} from 'rxjs'; @Component({ standalone: true, - imports: [AsyncPipe, FormsModule, TuiInputPhoneInternational, TuiSortCountriesPipe], + imports: [ + AsyncPipe, + FormsModule, + TuiInputPhoneInternational, + TuiSortCountriesPipe, + TuiTextfield, + ], templateUrl: './index.html', encapsulation, changeDetection, diff --git a/projects/demo/src/modules/components/input-phone-international/examples/3/index.html b/projects/demo/src/modules/components/input-phone-international/examples/3/index.html new file mode 100644 index 000000000000..220d0efbe030 --- /dev/null +++ b/projects/demo/src/modules/components/input-phone-international/examples/3/index.html @@ -0,0 +1,10 @@ + + + + diff --git a/projects/demo/src/modules/components/input-phone-international/examples/3/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/3/index.ts index 14b5b9369ccd..aa8a03d348a5 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/3/index.ts +++ b/projects/demo/src/modules/components/input-phone-international/examples/3/index.ts @@ -1,31 +1,43 @@ +import {AsyncPipe} from '@angular/common'; import {Component} from '@angular/core'; +import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; -import {MaskitoPipe} from '@maskito/angular'; -import {maskitoTransform} from '@maskito/core'; -import {maskitoPhoneOptionsGenerator} from '@maskito/phone'; -import metadata from 'libphonenumber-js/max/metadata'; +import {TuiDropdownMobile} from '@taiga-ui/addon-mobile'; +import {TuiButton, TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; +import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; +import { + tuiInputPhoneInternationalOptionsProvider, + TuiSortCountriesPipe, +} from '@taiga-ui/kit'; +import {getCountries} from 'libphonenumber-js'; +import {defer} from 'rxjs'; @Component({ standalone: true, - imports: [MaskitoPipe], - template: ` - Phone: {{ rawValue | maskito: mask }} - `, + imports: [ + AsyncPipe, + FormsModule, + TuiButton, + TuiDropdownMobile, + TuiInputPhoneInternational, + TuiSortCountriesPipe, + TuiTextfield, + ], + templateUrl: './index.html', encapsulation, changeDetection, - host: { - '(click)': 'showUtilityPower()', - }, + providers: [ + tuiInputPhoneInternationalOptionsProvider({ + metadata: defer(async () => + import('libphonenumber-js/max/metadata').then((m) => m.default), + ), + }), + ], }) export default class Example { - protected rawValue = '12125552368'; - protected readonly mask = maskitoPhoneOptionsGenerator({ - metadata, - countryIsoCode: 'US', - }); - - protected showUtilityPower(): void { - console.info(maskitoTransform(this.rawValue, this.mask)); - } + protected readonly countries = getCountries(); + protected countryIsoCode: TuiCountryIsoCode = 'CN'; + protected value = ''; } diff --git a/projects/demo/src/modules/components/input-phone-international/examples/4/index.html b/projects/demo/src/modules/components/input-phone-international/examples/4/index.html index 7bf4eb99f129..02ea60a90db7 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/4/index.html +++ b/projects/demo/src/modules/components/input-phone-international/examples/4/index.html @@ -1,9 +1,11 @@ - - Phone number + + + - + diff --git a/projects/demo/src/modules/components/input-phone-international/examples/4/index.less b/projects/demo/src/modules/components/input-phone-international/examples/4/index.less deleted file mode 100644 index 0e046833c439..000000000000 --- a/projects/demo/src/modules/components/input-phone-international/examples/4/index.less +++ /dev/null @@ -1,3 +0,0 @@ -tui-input-phone-international { - min-inline-size: min(20rem, 80vw); -} diff --git a/projects/demo/src/modules/components/input-phone-international/examples/4/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/4/index.ts index df934c0db04c..33e759f29b9a 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/4/index.ts +++ b/projects/demo/src/modules/components/input-phone-international/examples/4/index.ts @@ -2,20 +2,16 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; -import {TuiIcon} from '@taiga-ui/core'; +import {TuiIcon, TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; -import { - TuiInputPhoneInternational, - tuiInputPhoneInternationalOptionsProvider, - TuiTooltip, -} from '@taiga-ui/kit'; +import {tuiInputPhoneInternationalOptionsProvider, TuiTooltip} from '@taiga-ui/kit'; import {defer} from 'rxjs'; @Component({ standalone: true, - imports: [FormsModule, TuiIcon, TuiInputPhoneInternational, TuiTooltip], + imports: [FormsModule, TuiIcon, TuiInputPhoneInternational, TuiTextfield, TuiTooltip], templateUrl: './index.html', - styleUrls: ['./index.less'], encapsulation, changeDetection, providers: [ diff --git a/projects/demo/src/modules/components/input-phone-international/examples/5/index.html b/projects/demo/src/modules/components/input-phone-international/examples/5/index.html index 1da67d646d9b..5ff5c0b4b49f 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/5/index.html +++ b/projects/demo/src/modules/components/input-phone-international/examples/5/index.html @@ -1,7 +1,9 @@ - - Type your number - + + + + diff --git a/projects/demo/src/modules/components/input-phone-international/examples/5/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/5/index.ts index 73ad55396f88..b8bebbafdf46 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/5/index.ts +++ b/projects/demo/src/modules/components/input-phone-international/examples/5/index.ts @@ -2,17 +2,16 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {changeDetection} from '@demo/emulate/change-detection'; import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; -import { - TuiInputPhoneInternational, - tuiInputPhoneInternationalOptionsProvider, -} from '@taiga-ui/kit'; +import {tuiInputPhoneInternationalOptionsProvider} from '@taiga-ui/kit'; import {getCountries} from 'libphonenumber-js'; import {defer} from 'rxjs'; @Component({ standalone: true, - imports: [FormsModule, TuiInputPhoneInternational], + imports: [FormsModule, TuiInputPhoneInternational, TuiTextfield], templateUrl: './index.html', encapsulation, changeDetection, diff --git a/projects/demo/src/modules/components/input-phone-international/examples/6/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/6/index.ts new file mode 100644 index 000000000000..14b5b9369ccd --- /dev/null +++ b/projects/demo/src/modules/components/input-phone-international/examples/6/index.ts @@ -0,0 +1,31 @@ +import {Component} from '@angular/core'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {MaskitoPipe} from '@maskito/angular'; +import {maskitoTransform} from '@maskito/core'; +import {maskitoPhoneOptionsGenerator} from '@maskito/phone'; +import metadata from 'libphonenumber-js/max/metadata'; + +@Component({ + standalone: true, + imports: [MaskitoPipe], + template: ` + Phone: {{ rawValue | maskito: mask }} + `, + encapsulation, + changeDetection, + host: { + '(click)': 'showUtilityPower()', + }, +}) +export default class Example { + protected rawValue = '12125552368'; + protected readonly mask = maskitoPhoneOptionsGenerator({ + metadata, + countryIsoCode: 'US', + }); + + protected showUtilityPower(): void { + console.info(maskitoTransform(this.rawValue, this.mask)); + } +} diff --git a/projects/demo/src/modules/components/input-phone-international/examples/7/index.html b/projects/demo/src/modules/components/input-phone-international/examples/7/index.html new file mode 100644 index 000000000000..6f716414072d --- /dev/null +++ b/projects/demo/src/modules/components/input-phone-international/examples/7/index.html @@ -0,0 +1,5 @@ + diff --git a/projects/demo/src/modules/components/input-phone-international/examples/7/index.ts b/projects/demo/src/modules/components/input-phone-international/examples/7/index.ts new file mode 100644 index 000000000000..2b9aa7681108 --- /dev/null +++ b/projects/demo/src/modules/components/input-phone-international/examples/7/index.ts @@ -0,0 +1,53 @@ +import {Component} from '@angular/core'; +import {FormsModule} from '@angular/forms'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; +import {TuiTextfield} from '@taiga-ui/core'; +import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; +import { + TuiInputPhoneInternational, + tuiInputPhoneInternationalOptionsProvider, +} from '@taiga-ui/kit'; +import {defer} from 'rxjs'; + +@Component({ + standalone: true, + imports: [FormsModule, TuiInputPhoneInternational, TuiTextfield], + templateUrl: './index.html', + encapsulation, + changeDetection, + providers: [ + /** + * You can choose: lazily load metadata or include it in your bundle. + * Lazy loading: + */ + tuiInputPhoneInternationalOptionsProvider({ + metadata: defer(async () => + import('libphonenumber-js/max/metadata').then((m) => m.default), + ), + }), + /** + * Eager loading: + * ```ts + * import metadata from 'libphonenumber-js/mobile/metadata'; + * import {of} from 'rxjs'; + * // [...] + * tuiInputPhoneInternationalOptionsProvider({ + * metadata: of(metadata), + * }), + * ``` + */ + ], +}) +export default class Example { + protected readonly countries: readonly TuiCountryIsoCode[] = [ + 'IN', + 'CN', + 'US', + 'ID', + 'PK', + ]; + + protected countryIsoCode: TuiCountryIsoCode = 'US'; + protected value = '+12125552368'; +} diff --git a/projects/demo/src/modules/components/input-phone-international/examples/import/import.md b/projects/demo/src/modules/components/input-phone-international/examples/import/import.md index d509adf5d824..8bf32bd872d0 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/import/import.md +++ b/projects/demo/src/modules/components/input-phone-international/examples/import/import.md @@ -1,7 +1,8 @@ ```ts import {ReactiveFormsModule} from '@angular/forms'; import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; -import {TuiInputPhoneInternational} from '@taiga-ui/kit'; +import {TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; @Component({ standalone: true, diff --git a/projects/demo/src/modules/components/input-phone-international/examples/import/template.md b/projects/demo/src/modules/components/input-phone-international/examples/import/template.md index 9f4b575ecd37..3484e66129d7 100644 --- a/projects/demo/src/modules/components/input-phone-international/examples/import/template.md +++ b/projects/demo/src/modules/components/input-phone-international/examples/import/template.md @@ -1,8 +1,11 @@ ```html
- + + +
``` diff --git a/projects/demo/src/modules/components/input-phone-international/index.html b/projects/demo/src/modules/components/input-phone-international/index.html index 844cfae9ba3c..45c429eafab7 100644 --- a/projects/demo/src/modules/components/input-phone-international/index.html +++ b/projects/demo/src/modules/components/input-phone-international/index.html @@ -1,6 +1,6 @@ @@ -58,7 +58,7 @@ id="metadata" heading="Choose metadata" [component]="1 | tuiComponent" - [content]="1 | tuiExample: 'html,ts'" + [content]="1 | tuiExample" [description]="metadataDescription" > @@ -124,7 +124,7 @@ id="countries" heading="Choose any countries" [component]="2 | tuiComponent" - [content]="2 | tuiExample: 'html,ts'" + [content]="2 | tuiExample" [description]="selectCountriesDescription" > @@ -149,19 +149,16 @@ - - InputPhoneInternational - internally uses - Maskito - to format phone number. -
- Don't hesitate to use it too to manually format any phone number. + + You can enable mobile specific dropdown design on mobile devices by adding + TuiDropdownMobile + directive.
@@ -169,7 +166,7 @@ id="customize-with-icons" heading="Customize with icons" [component]="4 | tuiComponent" - [content]="4 | tuiExample: 'html,ts,less'" + [content]="4 | tuiExample" [description]="iconsDescription" > @@ -195,7 +192,7 @@ id="customize-separator" heading="Customize separator" [component]="5 | tuiComponent" - [content]="5 | tuiExample: 'html,ts'" + [content]="5 | tuiExample" [description]="separatorDescription" > @@ -206,17 +203,45 @@ . + + + + InputPhoneInternational + internally uses + Maskito + to format phone number. +
+ Don't hesitate to use it too to manually format any phone number. +
+
+ + + + This component has been refactored to maintain uniformity across new textfield components. Newest + version is in the + @taiga-ui/experimental + package and will replace previous syntax in the next major release. Old syntax is displayed in the + example below: + +
- - Type a phone number - + + + diff --git a/projects/demo/src/modules/components/input-phone-international/index.ts b/projects/demo/src/modules/components/input-phone-international/index.ts index bf92b17ac733..1ee78cd0edb2 100644 --- a/projects/demo/src/modules/components/input-phone-international/index.ts +++ b/projects/demo/src/modules/components/input-phone-international/index.ts @@ -7,12 +7,9 @@ import {changeDetection} from '@demo/emulate/change-detection'; import {DemoRoute} from '@demo/routes'; import {TuiDemo} from '@demo/utils'; import {TuiDropdown, TuiIcon, TuiTextfield} from '@taiga-ui/core'; +import {TuiInputPhoneInternational} from '@taiga-ui/experimental'; import type {TuiCountryIsoCode} from '@taiga-ui/i18n'; -import { - TuiInputPhoneInternational, - tuiInputPhoneInternationalOptionsProvider, - TuiTooltip, -} from '@taiga-ui/kit'; +import {tuiInputPhoneInternationalOptionsProvider, TuiTooltip} from '@taiga-ui/kit'; import {getCountries} from 'libphonenumber-js'; @Component({ diff --git a/projects/demo/src/modules/components/input/examples/4/index.html b/projects/demo/src/modules/components/input/examples/4/index.html index e8f66e1757b9..134f97220f31 100644 --- a/projects/demo/src/modules/components/input/examples/4/index.html +++ b/projects/demo/src/modules/components/input/examples/4/index.html @@ -66,7 +66,6 @@ diff --git a/projects/demo/src/modules/components/sheet-dialog/examples/4/index.ts b/projects/demo/src/modules/components/sheet-dialog/examples/4/index.ts index dae151d98d02..5fdabf8459e7 100644 --- a/projects/demo/src/modules/components/sheet-dialog/examples/4/index.ts +++ b/projects/demo/src/modules/components/sheet-dialog/examples/4/index.ts @@ -76,7 +76,7 @@ export default class Example { ); protected readonly height$ = this.size$.pipe( - map(({height}) => `calc(${height - this.offset}px - 14.5rem`), + map(({height}) => `calc(${height - this.offset}px - 14rem`), ); protected toggle(open: boolean): void { diff --git a/projects/demo/src/modules/components/thumbnail-card/examples/5/index.html b/projects/demo/src/modules/components/thumbnail-card/examples/5/index.html index 62fc0311697e..632ce8f856da 100644 --- a/projects/demo/src/modules/components/thumbnail-card/examples/5/index.html +++ b/projects/demo/src/modules/components/thumbnail-card/examples/5/index.html @@ -13,7 +13,6 @@ { + @ViewChildren(TuiOption, {read: ElementRef}) + protected readonly list: QueryList> = EMPTY_QUERY; + + protected readonly el = tuiInjectElement(); + protected readonly ios = inject(TUI_IS_IOS); + protected readonly icons = inject(TUI_COMMON_ICONS); + protected readonly options = inject(TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS); + protected readonly countries = signal(this.options.countries); + protected readonly code = signal(this.options.countryIsoCode); + protected readonly label = toSignal(inject(TUI_INTERNATIONAL_SEARCH)); + protected readonly metadata = toSignal(from(this.options.metadata)); + protected readonly names = toSignal(inject(TUI_COUNTRIES)); + protected readonly open = tuiDropdownOpen(); + protected readonly dropdown = tuiDropdown(null); + protected readonly search = signal(''); + protected readonly size = inject(TUI_TEXTFIELD_OPTIONS).size; + + protected readonly mask = tuiMaskito( + computed(() => this.computeMask(this.code(), this.metadata())), + ); + + protected readonly masked = computed( + () => + maskitoTransform(this.value(), this.mask() || MASKITO_DEFAULT_OPTIONS) || + this.el.value, + ); + + protected readonly filtered = computed(() => + this.countries() + .map((iso) => ({ + iso, + name: this.names()?.[iso] || '', + code: tuiGetCallingCode(iso, this.metadata()), + })) + .filter(({name, code}) => TUI_DEFAULT_MATCHER(name + code, this.search())), + ); + + protected readonly enabled = tuiDirectiveBinding( + TuiDropdownOpen, + 'tuiDropdownEnabled', + this.interactive, + {}, + ); + + protected readonly $ = inject(TuiActiveZone) + .tuiActiveZoneChange.pipe( + filter(() => !this.readOnly()), + takeUntilDestroyed(), + ) + .subscribe((active) => { + const prefix = `${tuiGetCallingCode(this.code(), this.metadata())} `; + const fallback = active ? this.el.value || prefix : this.el.value; + + this.search.set(''); + this.el.value = this.el.value === prefix ? '' : fallback; + }); + + @Input() + public countrySearch = false; + + @Output() + public readonly countryIsoCodeChange = toObservable(this.code).pipe(skip(1)); + + @Input('countries') + public set countriesValue(value: readonly TuiCountryIsoCode[]) { + this.countries.set(value); + } + + @Input('countryIsoCode') + public set isoCode(code: TuiCountryIsoCode) { + this.code.set(code); + } + + @ViewChild(TuiTextfieldDropdownDirective, {read: TemplateRef}) + protected set template(template: PolymorpheusContent) { + this.dropdown.set(template); + } + + protected onInput(): void { + const value = this.el.value.replaceAll(NOT_FORM_CONTROL_SYMBOLS, ''); + + this.onChange( + value === tuiGetCallingCode(this.code(), this.metadata()) ? '' : value, + ); + } + + protected onPaste(event: Event): void { + const metadata = this.metadata(); + const data = tuiIsInputEvent(event) && event.data; + + if ( + !data || + !metadata || + (!event.inputType.includes('Drop') && !event.inputType.includes('Paste')) + ) { + return; + } + + const value = data.startsWith(CHAR_PLUS) ? data : CHAR_PLUS + data; + const code = maskitoGetCountryFromNumber(value, metadata); + + if (code && validatePhoneNumberLength(value) !== 'TOO_SHORT') { + this.code.set(code); + } + } + + protected onItemClick(isoCode: TuiCountryIsoCode): void { + this.el.focus(); + this.el.value = this.el.value || tuiGetCallingCode(this.code(), this.metadata()); + this.open.set(false); + this.code.set(isoCode); + this.search.set(''); + } + + private computeMask( + countryIsoCode: TuiCountryIsoCode, + metadata?: MetadataJson, + ): MaskitoOptions | null { + if (!metadata) { + return null; + } + + const {plugins, ...options} = maskitoPhoneOptionsGenerator({ + countryIsoCode, + metadata, + separator: this.options.separator, + }); + + return { + ...options, + plugins: [...plugins, maskitoInitialCalibrationPlugin()], + }; + } +} diff --git a/projects/experimental/components/input-phone-international/input-phone-international.style.less b/projects/experimental/components/input-phone-international/input-phone-international.style.less new file mode 100644 index 000000000000..9f87a6182c05 --- /dev/null +++ b/projects/experimental/components/input-phone-international/input-phone-international.style.less @@ -0,0 +1,89 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +[tuiInputPhoneInternational][tuiInputPhoneInternational] { + left: var(--t-offset); + border-start-start-radius: 0; + border-end-start-radius: 0; + inline-size: calc(100% - var(--t-offset)); + + tui-root:has(tui-dropdown-mobile) & { + caret-color: transparent; + } + + & + label { + padding-left: var(--t-offset); + } +} + +tui-textfield[data-size='s'] { + --t-offset: 4.125rem; + + .t-ipi-flag { + margin: 0 0.1875rem; + } +} + +tui-textfield[data-size='m'] { + --t-offset: 4.875rem; + + .t-ipi-flag { + margin: 0 -0.1875rem; + } +} + +tui-textfield[data-size='l'] { + --t-offset: 5.25rem; + + .t-ipi-flag { + margin: 0 -0.1875rem; + } +} + +.t-ipi-select { + position: absolute; + left: 0; + border-radius: inherit; + border-start-end-radius: 0; + border-end-end-radius: 0; + + &_readonly { + pointer-events: none; + } +} + +.t-ipi-flag { + inline-size: 1.75rem; + block-size: 1.75rem; + border-radius: 100%; + + &_small { + inline-size: 1.25rem; + block-size: 1.25rem; + } +} + +.t-ipi-code { + color: var(--tui-text-secondary); +} + +.t-ipi-search { + position: sticky; + top: 0; + z-index: 1; + background: var(--tui-background-elevation-3); + padding: 0.375rem 0.375rem 0; + + input { + .tui-prevent-ios-scroll(); + } +} + +tui-dropdown-mobile { + .t-ipi-search { + background: var(--tui-background-elevation-1); + } + + .t-ipi-options:not(:first-child) { + min-block-size: calc(100 * var(--tui-viewport-vh) - 8.75rem); + } +} diff --git a/projects/experimental/components/input-phone-international/input-phone-international.template.html b/projects/experimental/components/input-phone-international/input-phone-international.template.html new file mode 100644 index 000000000000..3ff7d65e932f --- /dev/null +++ b/projects/experimental/components/input-phone-international/input-phone-international.template.html @@ -0,0 +1,67 @@ + + + + + + + + + + diff --git a/projects/experimental/components/input-phone-international/ng-package.json b/projects/experimental/components/input-phone-international/ng-package.json new file mode 100644 index 000000000000..bebf62dcb5e5 --- /dev/null +++ b/projects/experimental/components/input-phone-international/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/projects/experimental/components/input-phone-international/test/input-phone-international.component.spec.ts b/projects/experimental/components/input-phone-international/test/input-phone-international.component.spec.ts new file mode 100644 index 000000000000..988b9d823da7 --- /dev/null +++ b/projects/experimental/components/input-phone-international/test/input-phone-international.component.spec.ts @@ -0,0 +1,250 @@ +import type {DebugElement} from '@angular/core'; +import {ChangeDetectionStrategy, Component, ViewChild} from '@angular/core'; +import type {ComponentFixture} from '@angular/core/testing'; +import {TestBed} from '@angular/core/testing'; +import {FormControl, ReactiveFormsModule} from '@angular/forms'; +import {By} from '@angular/platform-browser'; +import {TuiRoot} from '@taiga-ui/core'; +import {NG_EVENT_PLUGINS} from '@taiga-ui/event-plugins'; +import type {TuiCountryIsoCode, TuiLanguage} from '@taiga-ui/i18n'; +import { + TUI_ENGLISH_LANGUAGE, + TUI_FRENCH_LANGUAGE, + TUI_LANGUAGE, + TUI_RUSSIAN_LANGUAGE, +} from '@taiga-ui/i18n'; +import { + TuiInputPhoneInternational, + tuiInputPhoneInternationalOptionsProvider, +} from '@taiga-ui/kit'; +import {TuiNativeInputPO} from '@taiga-ui/testing'; +import metadata from 'libphonenumber-js/max/metadata'; +import {of} from 'rxjs'; + +describe('InputPhoneInternational', () => { + @Component({ + standalone: true, + imports: [ReactiveFormsModule, TuiInputPhoneInternational, TuiRoot], + template: ` + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Test { + @ViewChild(TuiInputPhoneInternational, {static: true}) + public component!: TuiInputPhoneInternational; + + public control = new FormControl('+79110330102'); + + public countries: TuiCountryIsoCode[] = ['RU', 'KZ', 'UA', 'BY', 'TW', 'BD']; + + public countryIsoCode: TuiCountryIsoCode = 'RU'; + + public readOnly = false; + } + + let fixture: ComponentFixture; + let testComponent: Test; + let component: TuiInputPhoneInternational; + let inputPO: TuiNativeInputPO; + + const initializeTestModule = ( + language: TuiLanguage = TUI_ENGLISH_LANGUAGE, + separator?: string, + ): void => { + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [Test], + providers: [ + NG_EVENT_PLUGINS, + { + provide: TUI_LANGUAGE, + useValue: of(language), + }, + tuiInputPhoneInternationalOptionsProvider({ + metadata: of(metadata), + separator, + }), + ], + }); + await TestBed.compileComponents(); + fixture = TestBed.createComponent(Test); + testComponent = fixture.componentInstance; + component = testComponent.component; + fixture.detectChanges(); + inputPO = new TuiNativeInputPO( + fixture, + fixture.debugElement.query(By.css('input')), + ); + }); + }; + + describe('select new country from dropdown', () => { + initializeTestModule(); + + it('should switch country calling code and keeps all rest digits', async () => { + component.onItemClick('UA'); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(testComponent.control.value).toBe('+3809110330102'); + }); + }); + + describe('paste', () => { + initializeTestModule(); + + const paste = async (data: string): Promise => { + const event = new InputEvent('beforeinput', { + inputType: 'insertFromPaste', + data, + }); + + component.onPaste(event); + fixture.detectChanges(); + + inputPO.sendText(data); + await fixture.whenStable(); + }; + + it('should set correct country code on paste event', async () => { + await paste('+380123456789'); + + expect(testComponent.countryIsoCode).toBe('UA'); + }); + + it('should set country code on paste event', async () => { + await paste('+77777777777'); + + expect(testComponent.countryIsoCode).toBe('KZ'); + }); + + describe('should set KZ country code on paste event', () => { + ['+7 777 777-7777', '+7 7272 588300'].forEach((phone) => { + it(`${phone}`, async () => { + await paste(phone); + + expect(testComponent.countryIsoCode).toBe('KZ'); + }); + }); + }); + + it('should replace code 8 on paste event', async () => { + await paste('87777777777'); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(inputPO.value).toBe('+7 777 777-7777'); + }); + + it('should update value on paste', async () => { + await paste('+380 (12) 345-67-89'); + + expect(testComponent.control.value).toBe('+380123456789'); + }); + + it('should update value without "+" on paste', async () => { + await paste('380 (98) 765-4321'); + + expect(inputPO.value).toBe('+380 98 765-4321'); + }); + + it('should set country code on paste event +886', async () => { + await paste('+886355535353'); + + expect(testComponent.countryIsoCode).toBe('TW'); + }); + }); + + describe('programmatically patch', () => { + initializeTestModule(); + + it('should correct update control', () => { + let result: unknown; + const phoneNumber = '+380123456789'; + + testComponent.control.valueChanges.subscribe((value) => { + result = value; + }); + + testComponent.countryIsoCode = 'UA'; + testComponent.control.patchValue(phoneNumber); + + expect(result).toEqual(phoneNumber); + }); + }); + + describe('i18n', () => { + describe('RUSSIAN', () => { + initializeTestModule(TUI_RUSSIAN_LANGUAGE); + + it('displays country names in Russian inside dropdown', () => { + getCountrySelector().nativeElement.click(); + fixture.detectChanges(); + + expect(getDropdownCountryNames()).toEqual([ + 'Россия', + 'Казахстан', + 'Украина', + 'Беларусь (Белоруссия)', + 'Тайвань', + 'Бангладеш', + ]); + }); + }); + + describe('ENGLISH', () => { + initializeTestModule(TUI_ENGLISH_LANGUAGE); + + it('displays country names in English inside dropdown', () => { + getCountrySelector().nativeElement.click(); + fixture.detectChanges(); + + expect(getDropdownCountryNames()).toEqual([ + 'Russia', + 'Kazakhstan', + 'Ukraine', + 'Belarus', + 'Taiwan', + 'Bangladesh', + ]); + }); + }); + }); + + describe('separator', () => { + initializeTestModule(TUI_FRENCH_LANGUAGE, ' '); + + it('should have correct input value with provided custom separator', () => { + const phoneNumber = '+33724783794'; + + testComponent.countryIsoCode = 'FR'; + + inputPO.sendText(phoneNumber); + + expect(inputPO.value).toBe('+33 7 24 78 37 94'); + }); + }); + + function getDropdownCountryNames(): string[] { + const countryNameContainers = + fixture.debugElement.queryAll(By.css('.t-name')) || []; + + return countryNameContainers.map((container) => + container.nativeElement.textContent?.trim(), + ); + } + + function getCountrySelector(): DebugElement { + return fixture.debugElement.query(By.css('.t-select select')); + } +}); diff --git a/projects/experimental/components/ng-package.json b/projects/experimental/components/ng-package.json new file mode 100644 index 000000000000..bebf62dcb5e5 --- /dev/null +++ b/projects/experimental/components/ng-package.json @@ -0,0 +1,5 @@ +{ + "lib": { + "entryFile": "index.ts" + } +} diff --git a/projects/experimental/index.ts b/projects/experimental/index.ts index afe7115f0ba2..3a85d83c4206 100644 --- a/projects/experimental/index.ts +++ b/projects/experimental/index.ts @@ -1 +1 @@ -export const NOTHING = 'to see here, move along'; +export * from '@taiga-ui/experimental/components'; diff --git a/projects/kit/components/input-password/input-password.component.ts b/projects/kit/components/input-password/input-password.component.ts index 39c81a9b7876..33c839b16f41 100644 --- a/projects/kit/components/input-password/input-password.component.ts +++ b/projects/kit/components/input-password/input-password.component.ts @@ -13,7 +13,7 @@ import {TuiIcon} from '@taiga-ui/core/components/icon'; import { TUI_TEXTFIELD_OPTIONS, TuiTextfieldContent, - TuiTextfieldDirective, + TuiWithTextfield, } from '@taiga-ui/core/components/textfield'; import {TuiTooltip} from '@taiga-ui/kit/directives'; import {TUI_PASSWORD_TEXTS} from '@taiga-ui/kit/tokens'; @@ -39,7 +39,7 @@ import {TUI_INPUT_PASSWORD_OPTIONS} from './input-password.options'; `, changeDetection: ChangeDetectionStrategy.OnPush, providers: [tuiFallbackValueProvider('')], - hostDirectives: [TuiTextfieldDirective], + hostDirectives: [TuiWithTextfield], host: { '[type]': 'hidden() ? "password" : "text"', },