ngx-mat-input-tel 21.4.2 → 21.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.angulardoc.json +4 -0
- package/.editorconfig +13 -0
- package/.github/FUNDING.yml +13 -0
- package/.github/instructions/copilot-instructions.md +58 -0
- package/.github/workflows/ci.yml +27 -0
- package/.github/workflows/publish.yml +41 -0
- package/.github/workflows/test.yml +39 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +15 -0
- package/angular.json +165 -0
- package/commitlint.config.ts +3 -0
- package/eslint.config.js +43 -0
- package/example-1.png +0 -0
- package/package.json +83 -55
- package/pnpm-workspace.yaml +18 -0
- package/projects/demo/eslint.config.js +3 -0
- package/projects/demo/karma.conf.js +31 -0
- package/projects/demo/src/app/app.html +123 -0
- package/projects/demo/src/app/app.scss +16 -0
- package/projects/demo/src/app/app.spec.ts +35 -0
- package/projects/demo/src/app/app.ts +100 -0
- package/projects/demo/src/app/dialog/dialog.html +12 -0
- package/projects/demo/src/app/dialog/dialog.ts +31 -0
- package/projects/demo/src/environments/environment.prod.ts +3 -0
- package/projects/demo/src/environments/environment.ts +3 -0
- package/projects/demo/src/favicon.ico +0 -0
- package/projects/demo/src/index.html +21 -0
- package/projects/demo/src/main.ts +16 -0
- package/projects/demo/src/styles.scss +32 -0
- package/projects/demo/tsconfig.app.json +9 -0
- package/projects/demo/tsconfig.spec.json +9 -0
- package/projects/ngx-mat-input-tel/eslint.config.js +3 -0
- package/projects/ngx-mat-input-tel/karma.conf.js +31 -0
- package/projects/ngx-mat-input-tel/ng-package.json +8 -0
- package/projects/ngx-mat-input-tel/package.json +46 -0
- package/projects/ngx-mat-input-tel/src/lib/assets/arrow_drop_down_grey600_18dp.png +0 -0
- package/projects/ngx-mat-input-tel/src/lib/assets/flags_sprite_2x.png +0 -0
- package/projects/ngx-mat-input-tel/src/lib/data/country-code.const.ts +792 -0
- package/projects/ngx-mat-input-tel/src/lib/model/country.model.ts +12 -0
- package/projects/ngx-mat-input-tel/src/lib/model/phone-number-format.model.ts +1 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel-dialog/ngx-mat-input-tel.dialog.html +82 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel-dialog/ngx-mat-input-tel.dialog.scss +91 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel-dialog/ngx-mat-input-tel.dialog.ts +128 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel-flag/ngx-mat-input-tel-flag.scss +319 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel-flag/ngx-mat-input-tel-flag.ts +72 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel.html +42 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel.scss +122 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel.spec.ts +318 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel.ts +625 -0
- package/projects/ngx-mat-input-tel/src/lib/ngx-mat-input-tel.validator.ts +35 -0
- package/projects/ngx-mat-input-tel/src/lib/remove-iso.pipe.ts +13 -0
- package/projects/ngx-mat-input-tel/src/public-api.ts +7 -0
- package/projects/ngx-mat-input-tel/src/test.ts +10 -0
- package/projects/ngx-mat-input-tel/tsconfig.lib.json +19 -0
- package/projects/ngx-mat-input-tel/tsconfig.lib.prod.json +9 -0
- package/projects/ngx-mat-input-tel/tsconfig.spec.json +8 -0
- package/tsconfig.json +28 -0
- package/fesm2022/ngx-mat-input-tel.mjs +0 -1603
- package/fesm2022/ngx-mat-input-tel.mjs.map +0 -1
- package/types/ngx-mat-input-tel.d.ts +0 -162
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<div class="ngx-mat-input-tel-container">
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
matRipple
|
|
5
|
+
(click)="openCountrySelector()"
|
|
6
|
+
(focus)="onDialCodeFocus()"
|
|
7
|
+
(blur)="onDialCodeBlur()"
|
|
8
|
+
class="country-selector"
|
|
9
|
+
[class.separate]="separateDialCode"
|
|
10
|
+
[class.focused]="separateDialCode && isDialCodeFocused"
|
|
11
|
+
[disabled]="disabled"
|
|
12
|
+
[attr.aria-label]="ariaLabel"
|
|
13
|
+
>
|
|
14
|
+
<ngx-mat-input-tel-flag
|
|
15
|
+
class="main-flag"
|
|
16
|
+
[country]="{
|
|
17
|
+
iso2: $selectedCountry().iso2,
|
|
18
|
+
dialCode: $selectedCountry().dialCode,
|
|
19
|
+
}"
|
|
20
|
+
></ngx-mat-input-tel-flag>
|
|
21
|
+
</button>
|
|
22
|
+
|
|
23
|
+
<input
|
|
24
|
+
class="ngx-mat-input-tel-input"
|
|
25
|
+
matInput
|
|
26
|
+
type="tel"
|
|
27
|
+
inputmode="tel"
|
|
28
|
+
[name]="name || 'tel'"
|
|
29
|
+
[autocomplete]="autocomplete"
|
|
30
|
+
[ngClass]="cssClass"
|
|
31
|
+
(blur)="onPhoneInputBlurAndTouch()"
|
|
32
|
+
(focus)="onPhoneInputFocus()"
|
|
33
|
+
(keypress)="onInputKeyPress($event)"
|
|
34
|
+
[(ngModel)]="phoneNumber"
|
|
35
|
+
(ngModelChange)="onPhoneNumberChange()"
|
|
36
|
+
[errorStateMatcher]="errorStateMatcher"
|
|
37
|
+
[placeholder]="placeholder || ($selectedCountry() | removeIso) || ''"
|
|
38
|
+
[disabled]="disabled"
|
|
39
|
+
#focusable
|
|
40
|
+
[maxlength]="maxLength"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
&.ngx-floating {
|
|
3
|
+
.country-selector,
|
|
4
|
+
input.ngx-mat-input-tel-input::placeholder {
|
|
5
|
+
opacity: 1;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.ngx-mat-input-tel-container {
|
|
10
|
+
display: flex;
|
|
11
|
+
height: var(--ngxMatInputTel-height, 24px);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
input {
|
|
15
|
+
border: none;
|
|
16
|
+
background: none;
|
|
17
|
+
outline: none;
|
|
18
|
+
font: inherit;
|
|
19
|
+
width: 100%;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
position: relative;
|
|
22
|
+
z-index: 0;
|
|
23
|
+
|
|
24
|
+
&.ngx-mat-input-tel-input {
|
|
25
|
+
&::placeholder {
|
|
26
|
+
opacity: var(--ngxMatInputTel-placeholder-opacity, var(--ngxMatInputTel-opacity, 0));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.mdc-button__label {
|
|
32
|
+
margin-right: auto;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.dialCode-text {
|
|
36
|
+
white-space: nowrap;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.country-selector {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
border-radius: 0;
|
|
43
|
+
flex-shrink: 0;
|
|
44
|
+
height: initial;
|
|
45
|
+
line-height: unset;
|
|
46
|
+
padding: 1px;
|
|
47
|
+
opacity: var(--ngxMatInputTel-selector-opacity, var(--ngxMatInputTel-opacity, 0));
|
|
48
|
+
transition: opacity 200ms;
|
|
49
|
+
border: unset;
|
|
50
|
+
background-color: transparent;
|
|
51
|
+
font: {
|
|
52
|
+
size: inherit;
|
|
53
|
+
weight: inherit;
|
|
54
|
+
}
|
|
55
|
+
background: {
|
|
56
|
+
image: url(assets/arrow_drop_down_grey600_18dp.png);
|
|
57
|
+
position: right center;
|
|
58
|
+
repeat: no-repeat;
|
|
59
|
+
size: 18px auto;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
&:disabled {
|
|
63
|
+
color: rgba(#000, 0.38);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&.separate {
|
|
67
|
+
position: absolute;
|
|
68
|
+
left: 0;
|
|
69
|
+
width: 114px;
|
|
70
|
+
border-color: var(--mat-form-field-outlined-outline-color, var(--mat-sys-outline));
|
|
71
|
+
border-width: var(--mat-form-field-outlined-outline-width, 1px);
|
|
72
|
+
border-style: solid;
|
|
73
|
+
height: 100%;
|
|
74
|
+
padding-top: var(--mat-form-field-container-vertical-padding, 16px);
|
|
75
|
+
padding-bottom: var(--mat-form-field-container-vertical-padding, 16px);
|
|
76
|
+
min-height: var(--mat-form-field-container-height, 56px);
|
|
77
|
+
top: 0;
|
|
78
|
+
padding-right: max(
|
|
79
|
+
16px,
|
|
80
|
+
var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small))
|
|
81
|
+
);
|
|
82
|
+
padding-left: max(
|
|
83
|
+
16px,
|
|
84
|
+
var(--mat-form-field-outlined-container-shape, var(--mat-sys-corner-extra-small)) + 4px
|
|
85
|
+
);
|
|
86
|
+
transform: translate(calc(-100% - var(--ngxMatInputTel-gap, 32px)));
|
|
87
|
+
border-radius: var(
|
|
88
|
+
--mat-form-field-outlined-container-shape,
|
|
89
|
+
var(--mat-sys-corner-extra-small)
|
|
90
|
+
);
|
|
91
|
+
outline: none;
|
|
92
|
+
|
|
93
|
+
&.focused {
|
|
94
|
+
border-color: var(--mat-form-field-outlined-focus-outline-color, var(--mat-sys-primary));
|
|
95
|
+
border-width: var(--mat-form-field-outlined-focus-outline-width, 2px);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.country-selector-code {
|
|
101
|
+
color: var(--mdc-outlined-text-field-input-text-color);
|
|
102
|
+
padding-right: 18px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.country-list-button {
|
|
106
|
+
color: rgba(#000, 0.87);
|
|
107
|
+
direction: ltr;
|
|
108
|
+
font-size: 16px;
|
|
109
|
+
font-weight: 400;
|
|
110
|
+
height: initial;
|
|
111
|
+
line-height: normal;
|
|
112
|
+
min-height: 48px;
|
|
113
|
+
padding: 14px 24px;
|
|
114
|
+
text-align: left;
|
|
115
|
+
text-transform: none;
|
|
116
|
+
width: 100%;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.main-flag {
|
|
120
|
+
padding-right: 20px;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
2
|
+
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
3
|
+
import { MatDividerModule } from "@angular/material/divider";
|
|
4
|
+
import { vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { CommonModule } from "@angular/common";
|
|
7
|
+
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
|
8
|
+
import { MatButtonModule } from "@angular/material/button";
|
|
9
|
+
import { MatDialogModule } from "@angular/material/dialog";
|
|
10
|
+
import { MatInputModule } from "@angular/material/input";
|
|
11
|
+
import { E164Number, NationalNumber } from "libphonenumber-js";
|
|
12
|
+
import { NgxMatInputTelComponent } from "./ngx-mat-input-tel";
|
|
13
|
+
|
|
14
|
+
describe("NgxMatInputTelComponent", () => {
|
|
15
|
+
let component: NgxMatInputTelComponent;
|
|
16
|
+
let fixture: ComponentFixture<NgxMatInputTelComponent>;
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
await TestBed.configureTestingModule({
|
|
20
|
+
imports: [
|
|
21
|
+
CommonModule,
|
|
22
|
+
FormsModule,
|
|
23
|
+
MatInputModule,
|
|
24
|
+
MatDialogModule,
|
|
25
|
+
MatButtonModule,
|
|
26
|
+
MatDividerModule,
|
|
27
|
+
ReactiveFormsModule,
|
|
28
|
+
NgxMatInputTelComponent,
|
|
29
|
+
],
|
|
30
|
+
}).compileComponents();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
fixture = TestBed.createComponent(NgxMatInputTelComponent);
|
|
35
|
+
component = fixture.componentInstance;
|
|
36
|
+
fixture.detectChanges();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should create", () => {
|
|
40
|
+
expect(component).toBeTruthy();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("Separated Dial Code Focus", () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
component.separateDialCode = true;
|
|
46
|
+
fixture.detectChanges();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should set isDialCodeFocused to true when dial code button receives focus", () => {
|
|
50
|
+
expect(component.isDialCodeFocused).toBe(false);
|
|
51
|
+
component.onDialCodeFocus();
|
|
52
|
+
expect(component.isDialCodeFocused).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should set isDialCodeFocused to false when dial code button loses focus", () => {
|
|
56
|
+
component.isDialCodeFocused = true;
|
|
57
|
+
component.onDialCodeBlur();
|
|
58
|
+
expect(component.isDialCodeFocused).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should set isPhoneInputFocused to true when phone input receives focus", () => {
|
|
62
|
+
expect(component.isPhoneInputFocused).toBe(false);
|
|
63
|
+
component.onPhoneInputFocus();
|
|
64
|
+
expect(component.isPhoneInputFocused).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should set isPhoneInputFocused to false when phone input loses focus", () => {
|
|
68
|
+
component.isPhoneInputFocused = true;
|
|
69
|
+
component.onPhoneInputBlur();
|
|
70
|
+
expect(component.isPhoneInputFocused).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should not set component.focused when dial code button gets focus in separated mode", () => {
|
|
74
|
+
component.focused = false;
|
|
75
|
+
component.onDialCodeFocus();
|
|
76
|
+
expect(component.focused).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should set component.focused when phone input gets focus in separated mode", () => {
|
|
80
|
+
component.focused = false;
|
|
81
|
+
component.onPhoneInputFocus();
|
|
82
|
+
expect(component.focused).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should verify separateDialCode and focus state in separated mode", () => {
|
|
86
|
+
component.onDialCodeFocus();
|
|
87
|
+
fixture.detectChanges();
|
|
88
|
+
|
|
89
|
+
// Verify component state
|
|
90
|
+
expect(component.separateDialCode).toBe(true);
|
|
91
|
+
expect(component.isDialCodeFocused).toBe(true);
|
|
92
|
+
expect(component.isPhoneInputFocused).toBe(false);
|
|
93
|
+
expect(component.focused).toBe(false); // mat-form-field should not be focused
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should not have focused class on button when phone input has focus", () => {
|
|
97
|
+
component.onPhoneInputFocus();
|
|
98
|
+
fixture.detectChanges();
|
|
99
|
+
|
|
100
|
+
const button = fixture.nativeElement.querySelector(".country-selector");
|
|
101
|
+
expect(button.classList.contains("focused")).toBe(false);
|
|
102
|
+
expect(component.isDialCodeFocused).toBe(false);
|
|
103
|
+
expect(component.isPhoneInputFocused).toBe(true);
|
|
104
|
+
expect(component.focused).toBe(true); // mat-form-field should be focused
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should handle rapid focus switching between elements", () => {
|
|
108
|
+
// Focus button
|
|
109
|
+
component.onDialCodeFocus();
|
|
110
|
+
expect(component.isDialCodeFocused).toBe(true);
|
|
111
|
+
expect(component.focused).toBe(false);
|
|
112
|
+
|
|
113
|
+
// Switch to input without blur event (edge case)
|
|
114
|
+
component.onPhoneInputFocus();
|
|
115
|
+
expect(component.isPhoneInputFocused).toBe(true);
|
|
116
|
+
expect(component.focused).toBe(true);
|
|
117
|
+
|
|
118
|
+
// Now blur button
|
|
119
|
+
component.onDialCodeBlur();
|
|
120
|
+
expect(component.isDialCodeFocused).toBe(false);
|
|
121
|
+
expect(component.focused).toBe(true); // Should stay true because input is focused
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("enablePlaceholder", () => {
|
|
126
|
+
it("should populate selectedCountry placeholder when enablePlaceholder is set before ngOnInit", () => {
|
|
127
|
+
// Create a new fixture so we can set the input before the component initialises
|
|
128
|
+
const localFixture = TestBed.createComponent(NgxMatInputTelComponent);
|
|
129
|
+
const localComponent = localFixture.componentInstance;
|
|
130
|
+
|
|
131
|
+
localComponent.enablePlaceholder = true;
|
|
132
|
+
localComponent.defaultCountry = "US";
|
|
133
|
+
|
|
134
|
+
// detectChanges triggers ngOnInit
|
|
135
|
+
localFixture.detectChanges();
|
|
136
|
+
|
|
137
|
+
expect(localComponent.$selectedCountry().placeholder).toBeTruthy();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should leave selectedCountry placeholder empty when enablePlaceholder is not set", () => {
|
|
141
|
+
const localFixture = TestBed.createComponent(NgxMatInputTelComponent);
|
|
142
|
+
const localComponent = localFixture.componentInstance;
|
|
143
|
+
|
|
144
|
+
localComponent.defaultCountry = "US";
|
|
145
|
+
localFixture.detectChanges();
|
|
146
|
+
|
|
147
|
+
expect(localComponent.$selectedCountry().placeholder).toBeFalsy();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should populate placeholders when onlyCountries is set and enablePlaceholder is true", () => {
|
|
151
|
+
const localFixture = TestBed.createComponent(NgxMatInputTelComponent);
|
|
152
|
+
const localComponent = localFixture.componentInstance;
|
|
153
|
+
|
|
154
|
+
localComponent.enablePlaceholder = true;
|
|
155
|
+
localComponent.onlyCountries = ["US", "GB"];
|
|
156
|
+
localComponent.defaultCountry = "US";
|
|
157
|
+
localFixture.detectChanges();
|
|
158
|
+
|
|
159
|
+
expect(localComponent.$selectedCountry().placeholder).toBeTruthy();
|
|
160
|
+
expect(localComponent.$availableCountries().US.placeholder).toBeTruthy();
|
|
161
|
+
expect(localComponent.$availableCountries().GB.placeholder).toBeTruthy();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("Input and Label Bindings", () => {
|
|
166
|
+
it("should render input element", () => {
|
|
167
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
168
|
+
expect(input).toBeTruthy();
|
|
169
|
+
expect(input.type).toBe("tel");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should bind placeholder input correctly", () => {
|
|
173
|
+
component.placeholder = "Enter phone number";
|
|
174
|
+
fixture.detectChanges();
|
|
175
|
+
|
|
176
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
177
|
+
// When placeholder is set, it should be used (or country name if empty)
|
|
178
|
+
expect(component.placeholder).toBe("Enter phone number");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should render country selector button", () => {
|
|
182
|
+
const button = fixture.nativeElement.querySelector(".country-selector");
|
|
183
|
+
expect(button).toBeTruthy();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should bind aria-label input correctly", () => {
|
|
187
|
+
const customLabel = "Choose your country";
|
|
188
|
+
component.ariaLabel = customLabel;
|
|
189
|
+
fixture.detectChanges();
|
|
190
|
+
|
|
191
|
+
// Verify the property is set on the component
|
|
192
|
+
expect(component.ariaLabel).toBe(customLabel);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("ControlValueAccessor Implementation", () => {
|
|
197
|
+
it("should implement writeValue to update the phone number", () => {
|
|
198
|
+
const testPhoneNumber = "+33123456789";
|
|
199
|
+
component.writeValue(testPhoneNumber);
|
|
200
|
+
fixture.detectChanges();
|
|
201
|
+
|
|
202
|
+
expect(component.phoneNumber).toBeTruthy();
|
|
203
|
+
expect(component.numberInstance).toBeTruthy();
|
|
204
|
+
expect(component.numberInstance?.country).toBe("FR");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should update component phoneNumber when writeValue is called", () => {
|
|
208
|
+
const testPhoneNumber = "+33123456789";
|
|
209
|
+
component.writeValue(testPhoneNumber);
|
|
210
|
+
fixture.detectChanges();
|
|
211
|
+
|
|
212
|
+
expect(component.phoneNumber).toBeTruthy();
|
|
213
|
+
expect(component.numberInstance).toBeTruthy();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should handle empty value in writeValue", () => {
|
|
217
|
+
// First set a value
|
|
218
|
+
component.writeValue("+33123456789");
|
|
219
|
+
fixture.detectChanges();
|
|
220
|
+
expect(component.phoneNumber).toBeTruthy();
|
|
221
|
+
expect(component.value).toBeTruthy();
|
|
222
|
+
|
|
223
|
+
// writeValue with null/undefined/empty triggers onPhoneNumberChange
|
|
224
|
+
// which calls _setCountry. If phoneNumber is cleared manually, value becomes null
|
|
225
|
+
component.phoneNumber = "" as E164Number | NationalNumber;
|
|
226
|
+
component.onPhoneNumberChange();
|
|
227
|
+
fixture.detectChanges();
|
|
228
|
+
|
|
229
|
+
// After clearing phoneNumber and calling onPhoneNumberChange, value should be null
|
|
230
|
+
expect(component.value).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("should register onChange callback", () => {
|
|
234
|
+
const mockCallback = vi.fn();
|
|
235
|
+
component.registerOnChange(mockCallback);
|
|
236
|
+
|
|
237
|
+
component.phoneNumber = "+33123456789" as E164Number | NationalNumber;
|
|
238
|
+
component.onPhoneNumberChange();
|
|
239
|
+
|
|
240
|
+
expect(mockCallback).toHaveBeenCalled();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("should call onChange when user types in input", () => {
|
|
244
|
+
const mockCallback = vi.fn();
|
|
245
|
+
component.registerOnChange(mockCallback);
|
|
246
|
+
|
|
247
|
+
const input = fixture.nativeElement.querySelector("input");
|
|
248
|
+
input.value = "+33123456789";
|
|
249
|
+
input.dispatchEvent(new Event("input"));
|
|
250
|
+
fixture.detectChanges();
|
|
251
|
+
|
|
252
|
+
expect(mockCallback).toHaveBeenCalled();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should register onTouched callback", () => {
|
|
256
|
+
const mockCallback = vi.fn();
|
|
257
|
+
component.registerOnTouched(mockCallback);
|
|
258
|
+
|
|
259
|
+
expect(component.onTouched).toBe(mockCallback);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe("Country Selection", () => {
|
|
264
|
+
it("should emit countryChanged when country is selected", () => {
|
|
265
|
+
vi.spyOn(component.countryChanged, "emit");
|
|
266
|
+
|
|
267
|
+
const country = component.getCountry("US");
|
|
268
|
+
component.onCountrySelect({
|
|
269
|
+
key: "US",
|
|
270
|
+
value: country,
|
|
271
|
+
});
|
|
272
|
+
fixture.detectChanges();
|
|
273
|
+
|
|
274
|
+
expect(component.countryChanged.emit).toHaveBeenCalledWith(country);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("should update selected country when selecting a different country", () => {
|
|
278
|
+
const usCountry = component.getCountry("US");
|
|
279
|
+
component.onCountrySelect({
|
|
280
|
+
key: "US",
|
|
281
|
+
value: usCountry,
|
|
282
|
+
});
|
|
283
|
+
fixture.detectChanges();
|
|
284
|
+
|
|
285
|
+
expect(component.$selectedCountry().iso2).toBe("US");
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe("Disabled State", () => {
|
|
290
|
+
it("should set disabled state when setDisabledState is called with true", () => {
|
|
291
|
+
component.setDisabledState(true);
|
|
292
|
+
fixture.detectChanges();
|
|
293
|
+
|
|
294
|
+
expect(component.disabled).toBe(true);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("should clear disabled state when setDisabledState is called with false", () => {
|
|
298
|
+
component.setDisabledState(true);
|
|
299
|
+
fixture.detectChanges();
|
|
300
|
+
component.setDisabledState(false);
|
|
301
|
+
fixture.detectChanges();
|
|
302
|
+
|
|
303
|
+
expect(component.disabled).toBe(false);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("should respect disabled state for country selector", () => {
|
|
307
|
+
component.setDisabledState(true);
|
|
308
|
+
fixture.detectChanges();
|
|
309
|
+
|
|
310
|
+
expect(component.disabled).toBe(true);
|
|
311
|
+
|
|
312
|
+
// Verify that openCountrySelector respects disabled state
|
|
313
|
+
vi.spyOn(component["_dialog"], "open");
|
|
314
|
+
component.openCountrySelector();
|
|
315
|
+
expect(component["_dialog"].open).not.toHaveBeenCalled();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|