diff --git a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-input-box.component.fixture.ts b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-input-box.component.fixture.ts index 61618d47be..5bca602c75 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-input-box.component.fixture.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-input-box.component.fixture.ts @@ -1,11 +1,14 @@ import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { SkyInputBoxModule } from '@skyux/forms'; +import { SkyPhoneFieldModule } from '../phone-field.module'; import { SkyPhoneFieldCountry } from '../types/country'; @Component({ + imports: [FormsModule, SkyInputBoxModule, SkyPhoneFieldModule], selector: 'sky-test-cmp', templateUrl: './phone-field-input-box.component.fixture.html', - standalone: false, }) export class PhoneFieldInputBoxTestComponent { public hintText: string | undefined; diff --git a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-reactive.component.fixture.ts b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-reactive.component.fixture.ts index d8f59589f5..9e3b88187f 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-reactive.component.fixture.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field-reactive.component.fixture.ts @@ -1,15 +1,20 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { + ReactiveFormsModule, + UntypedFormControl, + UntypedFormGroup, +} from '@angular/forms'; import { SkyPhoneFieldInputDirective } from '../phone-field-input.directive'; import { SkyPhoneFieldComponent } from '../phone-field.component'; +import { SkyPhoneFieldModule } from '../phone-field.module'; import { SkyPhoneFieldCountry } from '../types/country'; import { SkyPhoneFieldNumberReturnFormat } from '../types/number-return-format'; @Component({ + imports: [ReactiveFormsModule, SkyPhoneFieldModule], selector: 'sky-test-cmp', templateUrl: './phone-field-reactive.component.fixture.html', - standalone: false, }) export class PhoneFieldReactiveTestComponent implements OnInit { public allowExtensions = true; diff --git a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.component.fixture.ts b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.component.fixture.ts index 64d7d2cbe6..01ce82354d 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.component.fixture.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.component.fixture.ts @@ -1,14 +1,16 @@ import { Component, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { SkyPhoneFieldInputDirective } from '../phone-field-input.directive'; import { SkyPhoneFieldComponent } from '../phone-field.component'; +import { SkyPhoneFieldModule } from '../phone-field.module'; import { SkyPhoneFieldCountry } from '../types/country'; import { SkyPhoneFieldNumberReturnFormat } from '../types/number-return-format'; @Component({ + imports: [FormsModule, SkyPhoneFieldModule], selector: 'sky-test-cmp', templateUrl: './phone-field.component.fixture.html', - standalone: false, }) export class PhoneFieldTestComponent { public allowExtensions = true; diff --git a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.fixture.module.ts b/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.fixture.module.ts deleted file mode 100644 index 3980b25f8d..0000000000 --- a/libs/components/phone-field/src/lib/modules/phone-field/fixtures/phone-field.fixture.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { SkyInputBoxModule } from '@skyux/forms'; - -import { SkyPhoneFieldModule } from '../phone-field.module'; - -import { PhoneFieldInputBoxTestComponent } from './phone-field-input-box.component.fixture'; -import { PhoneFieldReactiveTestComponent } from './phone-field-reactive.component.fixture'; -import { PhoneFieldTestComponent } from './phone-field.component.fixture'; - -@NgModule({ - declarations: [ - PhoneFieldInputBoxTestComponent, - PhoneFieldReactiveTestComponent, - PhoneFieldTestComponent, - ], - imports: [ - SkyPhoneFieldModule, - SkyInputBoxModule, - FormsModule, - ReactiveFormsModule, - ], -}) -export class SkyInlineFormFixtureModule {} diff --git a/libs/components/phone-field/src/lib/modules/phone-field/phone-field-input.directive.ts b/libs/components/phone-field/src/lib/modules/phone-field/phone-field-input.directive.ts index 22f6e83b75..be5168b093 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/phone-field-input.directive.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/phone-field-input.directive.ts @@ -17,11 +17,16 @@ import { Validator, } from '@angular/forms'; -import { PhoneNumberFormat, PhoneNumberUtil } from 'google-libphonenumber'; +import { + PhoneNumber, + PhoneNumberFormat, + PhoneNumberUtil, +} from 'google-libphonenumber'; import { Subject, take, takeUntil } from 'rxjs'; import { SkyPhoneFieldAdapterService } from './phone-field-adapter.service'; import { SkyPhoneFieldComponent } from './phone-field.component'; +import { SkyPhoneFieldNumberReturnFormat } from './types/number-return-format'; /** * Creates a button, search input, and text input for entering and validating @@ -50,7 +55,6 @@ import { SkyPhoneFieldComponent } from './phone-field.component'; multi: true, }, ], - standalone: false, }) export class SkyPhoneFieldInputDirective implements OnInit, OnDestroy, ControlValueAccessor, Validator @@ -207,7 +211,7 @@ export class SkyPhoneFieldInputDirective this.#notifyChange?.(this.#getValue()); } - #formatPhoneNumber(value: string | undefined): string | undefined { + #maybeFormatPhoneNumber(value: string | undefined): string | undefined { if (!value) { return; } @@ -223,39 +227,47 @@ export class SkyPhoneFieldInputDirective ); if (this.#phoneUtils.isPossibleNumber(phoneNumber)) { - switch (returnFormat) { - case 'international': - return this.#phoneUtils.format( - phoneNumber, - PhoneNumberFormat.INTERNATIONAL, - ); - - case 'national': - return this.#phoneUtils.format( - phoneNumber, - PhoneNumberFormat.NATIONAL, - ); - - case 'default': - default: - return regionCode && regionCode !== defaultCountry - ? this.#phoneUtils.format( - phoneNumber, - PhoneNumberFormat.INTERNATIONAL, - ) - : this.#phoneUtils.format( - phoneNumber, - PhoneNumberFormat.NATIONAL, - ); - } + return this.#formatPhoneNumber( + phoneNumber, + returnFormat, + defaultCountry, + regionCode, + ); } - } catch (err) { + } catch { /* */ } return; } + #formatPhoneNumber( + phoneNumber: PhoneNumber, + returnFormat?: SkyPhoneFieldNumberReturnFormat, + defaultCountry?: string, + regionCode?: string, + ): string { + switch (returnFormat) { + case 'international': + return this.#phoneUtils.format( + phoneNumber, + PhoneNumberFormat.INTERNATIONAL, + ); + + case 'national': + return this.#phoneUtils.format(phoneNumber, PhoneNumberFormat.NATIONAL); + + case 'default': + default: + return regionCode && regionCode !== defaultCountry + ? this.#phoneUtils.format( + phoneNumber, + PhoneNumberFormat.INTERNATIONAL, + ) + : this.#phoneUtils.format(phoneNumber, PhoneNumberFormat.NATIONAL); + } + } + #getDefaultCountry(): string | undefined { return this.#phoneFieldComponent?.defaultCountry; } @@ -284,7 +296,7 @@ export class SkyPhoneFieldInputDirective } return this.#phoneUtils.isValidNumberForRegion(phoneNumber, regionCode); - } catch (e) { + } catch { return false; } } @@ -292,7 +304,7 @@ export class SkyPhoneFieldInputDirective #setValue(value: string | undefined): void { /* istanbul ignore else */ if (value !== undefined) { - const formatted = this.#formatPhoneNumber(value); + const formatted = this.#maybeFormatPhoneNumber(value); this.#_value = formatted ?? value; } } diff --git a/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.spec.ts b/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.spec.ts index 1a7dc456c5..1c697ef37a 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.spec.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.spec.ts @@ -4,16 +4,10 @@ import { fakeAsync, tick, } from '@angular/core/testing'; -import { - FormsModule, - NgModel, - ReactiveFormsModule, - UntypedFormControl, -} from '@angular/forms'; +import { NgModel, UntypedFormControl } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { SkyAppTestUtility, expect, expectAsync } from '@skyux-sdk/testing'; -import { SkyInputBoxModule } from '@skyux/forms'; import { SkyTheme, SkyThemeMode, @@ -27,7 +21,6 @@ import { BehaviorSubject } from 'rxjs'; import { PhoneFieldInputBoxTestComponent } from './fixtures/phone-field-input-box.component.fixture'; import { PhoneFieldReactiveTestComponent } from './fixtures/phone-field-reactive.component.fixture'; import { PhoneFieldTestComponent } from './fixtures/phone-field.component.fixture'; -import { SkyPhoneFieldModule } from './phone-field.module'; describe('Phone Field Component', () => { let mockThemeSvc: Partial; @@ -287,8 +280,7 @@ describe('Phone Field Component', () => { }; TestBed.configureTestingModule({ - declarations: [PhoneFieldTestComponent], - imports: [SkyPhoneFieldModule, NoopAnimationsModule, FormsModule], + imports: [PhoneFieldTestComponent, NoopAnimationsModule], providers: [ { provide: SkyThemeService, @@ -1383,13 +1375,7 @@ describe('Phone Field Component', () => { }; TestBed.configureTestingModule({ - declarations: [PhoneFieldReactiveTestComponent], - imports: [ - SkyPhoneFieldModule, - NoopAnimationsModule, - FormsModule, - ReactiveFormsModule, - ], + imports: [PhoneFieldReactiveTestComponent, NoopAnimationsModule], providers: [ { provide: SkyThemeService, @@ -2292,13 +2278,7 @@ describe('Phone Field Component', () => { }; TestBed.configureTestingModule({ - declarations: [PhoneFieldInputBoxTestComponent], - imports: [ - SkyInputBoxModule, - SkyPhoneFieldModule, - NoopAnimationsModule, - FormsModule, - ], + imports: [PhoneFieldInputBoxTestComponent, NoopAnimationsModule], providers: [ { provide: SkyThemeService, diff --git a/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.ts b/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.ts index 67cb6c0587..8d17e048ae 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/phone-field.component.ts @@ -5,6 +5,7 @@ import { transition, trigger, } from '@angular/animations'; +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -21,15 +22,22 @@ import { ViewEncapsulation, inject, } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { + FormBuilder, + FormControl, + FormGroup, + ReactiveFormsModule, +} from '@angular/forms'; import { SkyAppFormat } from '@skyux/core'; import { SkyInputBoxHostService } from '@skyux/forms'; import { SkyLibResourcesService } from '@skyux/i18n'; +import { SkyIconModule } from '@skyux/icon'; import { SKY_COUNTRY_FIELD_CONTEXT, SkyCountryFieldCountry, + SkyCountryFieldModule, } from '@skyux/lookup'; -import { SkyThemeService } from '@skyux/theme'; +import { SkyThemeModule, SkyThemeService } from '@skyux/theme'; import { PhoneNumberFormat, @@ -39,6 +47,8 @@ import { import intlTelInput from 'intl-tel-input'; import { Subject, takeUntil } from 'rxjs'; +import { SkyPhoneFieldResourcesModule } from '../shared/sky-phone-field-resources.module'; + import { cloneCountryData } from './clone-country-data'; import { SkyPhoneFieldAdapterService } from './phone-field-adapter.service'; import { SkyPhoneFieldCountry } from './types/country'; @@ -50,6 +60,14 @@ const DEFAULT_COUNTRY_CODE = 'us'; // from firing on initial load. For more information on this technique you can see // https://www.bennadel.com/blog/3417-using-no-op-transitions-to-prevent-animation-during-the-initial-render-of-ngfor-in-angular-5-2-6.htm @Component({ + imports: [ + CommonModule, + ReactiveFormsModule, + SkyCountryFieldModule, + SkyIconModule, + SkyPhoneFieldResourcesModule, + SkyThemeModule, + ], selector: 'sky-phone-field', templateUrl: './phone-field.component.html', styleUrls: ['./phone-field.component.scss'], @@ -136,7 +154,6 @@ const DEFAULT_COUNTRY_CODE = 'us'; ]), ]), ], - standalone: false, }) export class SkyPhoneFieldComponent implements OnDestroy, OnInit { /** @@ -418,71 +435,72 @@ export class SkyPhoneFieldComponent implements OnDestroy, OnInit { this.#changeDetector.markForCheck(); } - public setCountryByDialCode(phoneNumberRaw: string | undefined): boolean { + public setCountryByDialCode(phoneNumberRaw: string | undefined): void { if (!phoneNumberRaw || !phoneNumberRaw.startsWith('+')) { - return false; + return; } - let dialCode = ''; let foundCountry: SkyPhoneFieldCountry | undefined; - let newCountry: SkyPhoneFieldCountry | undefined; try { const phoneNumberParsed = this.#phoneUtils.parseAndKeepRawInput(phoneNumberRaw); + const regionCode = this.#phoneUtils.getRegionCodeForNumber(phoneNumberParsed); - foundCountry = this.countries.find( - (country) => country.iso2.toUpperCase() === regionCode?.toUpperCase(), - ); - if (foundCountry && !this.#validateSupportedCountry(foundCountry)) { - foundCountry = undefined; - } - } catch (_) { - if ( - !this.selectedCountry?.dialCode || - !phoneNumberRaw.startsWith(`${this.selectedCountry?.dialCode}`) - ) { - for (let i = 1; i < this.#longestDialCodeLength + 1; i++) { - dialCode = phoneNumberRaw.substring(0, i); - - if (this.#defaultCountryData?.dialCode === dialCode) { - foundCountry = this.#defaultCountryData; - } else if (this.selectedCountry?.dialCode !== dialCode) { - const dialCodeCountries = this.countries.filter( - (country) => country.dialCode === dialCode, - ); - - if (dialCodeCountries.length > 0) { - foundCountry = - dialCodeCountries.find((country) => country.priority === 0) ?? - dialCodeCountries[0]; - // TODO: Refactor the logic in this method for readability. - // eslint-disable-next-line max-depth - if ( - foundCountry && - !this.#validateSupportedCountry(foundCountry) - ) { - foundCountry = undefined; - } - } - } - } + if (regionCode !== undefined) { + foundCountry = this.countries.find( + (country) => + country.iso2.toLocaleUpperCase() === regionCode.toLocaleUpperCase(), + ); } + } catch { + foundCountry ??= this.#findCountryByDialCode(phoneNumberRaw); } - if (foundCountry !== this.selectedCountry) { - newCountry = foundCountry; + if (foundCountry && !this.#validateSupportedCountry(foundCountry)) { + foundCountry = undefined; } - if (newCountry) { - this.selectedCountry = newCountry; + if (foundCountry !== this.selectedCountry) { + this.selectedCountry = foundCountry; this.#changeDetector.markForCheck(); - return true; + } + } + + #findCountryByDialCode( + phoneNumberRaw: string, + ): SkyPhoneFieldCountry | undefined { + const defaultDialCode = this.#defaultCountryData?.dialCode; + const selectedCountryDialCode = this.selectedCountry?.dialCode; + + let foundCountry: SkyPhoneFieldCountry | undefined; + + if ( + !selectedCountryDialCode || + !phoneNumberRaw.startsWith(selectedCountryDialCode) + ) { + for (let i = 1; i < this.#longestDialCodeLength + 1; i++) { + const dialCode = phoneNumberRaw.substring(0, i); + + if (defaultDialCode === dialCode) { + foundCountry = this.#defaultCountryData; + } else if (selectedCountryDialCode !== dialCode) { + const dialCodeCountries = this.countries.filter( + (country) => country.dialCode === dialCode, + ); + + if (dialCodeCountries.length > 0) { + foundCountry = + dialCodeCountries.find((country) => country.priority === 0) ?? + dialCodeCountries[0]; + } + } + } } - return false; + return foundCountry; } #getDefaultCountryData(): SkyPhoneFieldCountry | undefined { diff --git a/libs/components/phone-field/src/lib/modules/phone-field/phone-field.module.ts b/libs/components/phone-field/src/lib/modules/phone-field/phone-field.module.ts index 51bb7f8812..6cd0ed776f 100644 --- a/libs/components/phone-field/src/lib/modules/phone-field/phone-field.module.ts +++ b/libs/components/phone-field/src/lib/modules/phone-field/phone-field.module.ts @@ -1,26 +1,10 @@ -import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { SkyIconModule } from '@skyux/icon'; -import { SkyCountryFieldModule } from '@skyux/lookup'; -import { SkyThemeModule } from '@skyux/theme'; - -import { SkyPhoneFieldResourcesModule } from '../shared/sky-phone-field-resources.module'; import { SkyPhoneFieldInputDirective } from './phone-field-input.directive'; import { SkyPhoneFieldComponent } from './phone-field.component'; @NgModule({ - declarations: [SkyPhoneFieldComponent, SkyPhoneFieldInputDirective], - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - SkyCountryFieldModule, - SkyIconModule, - SkyPhoneFieldResourcesModule, - SkyThemeModule, - ], + imports: [SkyPhoneFieldComponent, SkyPhoneFieldInputDirective], exports: [SkyPhoneFieldComponent, SkyPhoneFieldInputDirective], }) export class SkyPhoneFieldModule {}