@meetelise/chat 1.51.0 → 1.51.1
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/dist/src/WebComponent/Scheduler/tour-scheduler.d.ts +7 -3
- package/dist/src/WebComponent/actions/email-us-window.d.ts +5 -4
- package/dist/src/WebComponent/actions/formatPhoneNumber.d.ts +22 -3
- package/dist/src/WebComponent/actions/international-phone-input.d.ts +30 -0
- package/package.json +2 -1
- package/public/dist/index.js +695 -575
- package/src/WebComponent/Scheduler/tour-scheduler.ts +37 -189
- package/src/WebComponent/actions/call-us-window.ts +2 -2
- package/src/WebComponent/actions/email-us-window.ts +27 -54
- package/src/WebComponent/actions/formatPhoneNumber.ts +146 -12
- package/src/WebComponent/actions/international-phone-input.ts +308 -0
- package/src/formatPhoneNumber.test.ts +91 -0
- package/src/internationalPhoneInput.test.ts +253 -0
- package/src/internationalPhoneSubmission.test.ts +145 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
4
|
+
import { InputStyles } from "./InputStyles";
|
|
5
|
+
import {
|
|
6
|
+
formatToPhoneInput,
|
|
7
|
+
getCountryFromPhoneInput,
|
|
8
|
+
getE164PhoneNumber,
|
|
9
|
+
getNationalPhoneNumber,
|
|
10
|
+
getPhoneCountry,
|
|
11
|
+
phoneCountryOptions,
|
|
12
|
+
} from "./formatPhoneNumber";
|
|
13
|
+
|
|
14
|
+
export type InternationalPhoneChangeDetail = {
|
|
15
|
+
country: string;
|
|
16
|
+
e164: string | null;
|
|
17
|
+
formattedValue: string;
|
|
18
|
+
isValid: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
@customElement("international-phone-input")
|
|
22
|
+
export class InternationalPhoneInput extends LitElement {
|
|
23
|
+
static styles = [
|
|
24
|
+
InputStyles,
|
|
25
|
+
css`
|
|
26
|
+
:host {
|
|
27
|
+
display: block;
|
|
28
|
+
width: 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.phone-input-container {
|
|
32
|
+
align-items: center;
|
|
33
|
+
background: #d9d9d9;
|
|
34
|
+
border: 1px solid #efefef;
|
|
35
|
+
border-radius: 100px;
|
|
36
|
+
box-sizing: border-box;
|
|
37
|
+
display: flex;
|
|
38
|
+
min-height: 44px;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
width: 100%;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.phone-input-container:focus-within {
|
|
44
|
+
border-color: #202020;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.phone-input-container--invalid {
|
|
48
|
+
border-color: #ff0000;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.country-select-wrapper {
|
|
52
|
+
align-items: center;
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
display: flex;
|
|
56
|
+
flex: 0 0 96px;
|
|
57
|
+
height: 42px;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
position: relative;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.country-select-wrapper::after {
|
|
63
|
+
border-bottom: 1.5px solid #4b4b4b;
|
|
64
|
+
border-right: 1.5px solid #4b4b4b;
|
|
65
|
+
content: "";
|
|
66
|
+
height: 5px;
|
|
67
|
+
margin-left: 8px;
|
|
68
|
+
margin-top: -3px;
|
|
69
|
+
transform: rotate(45deg);
|
|
70
|
+
width: 5px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.country-select-label {
|
|
74
|
+
color: #202020;
|
|
75
|
+
font-family: "Helvetica Neue", Arial;
|
|
76
|
+
font-size: 14px;
|
|
77
|
+
font-weight: 400;
|
|
78
|
+
line-height: 22px;
|
|
79
|
+
white-space: nowrap;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
select {
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
inset: 0;
|
|
85
|
+
opacity: 0;
|
|
86
|
+
position: absolute;
|
|
87
|
+
width: 100%;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.phone-input-divider {
|
|
91
|
+
background: rgba(32, 32, 32, 0.18);
|
|
92
|
+
flex: 0 0 1px;
|
|
93
|
+
height: 22px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.phone-input-container input.webchat-input {
|
|
97
|
+
background: transparent;
|
|
98
|
+
border: 0;
|
|
99
|
+
border-radius: 0;
|
|
100
|
+
box-sizing: border-box;
|
|
101
|
+
flex: 1;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
padding: 10px 16px 10px 12px;
|
|
104
|
+
width: 100%;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.phone-input-container input.webchat-input:active,
|
|
108
|
+
.phone-input-container input.webchat-input:focus-within {
|
|
109
|
+
border: 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@media screen and (max-width: 767px) {
|
|
113
|
+
input {
|
|
114
|
+
font-size: 16px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.country-select-wrapper {
|
|
118
|
+
flex-basis: 92px;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
`,
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
@property({ attribute: false })
|
|
125
|
+
country: string | null = null;
|
|
126
|
+
|
|
127
|
+
@property({ type: String })
|
|
128
|
+
value = "";
|
|
129
|
+
|
|
130
|
+
@property({ type: Boolean })
|
|
131
|
+
invalid = false;
|
|
132
|
+
|
|
133
|
+
@state()
|
|
134
|
+
private selectedCountry = "US";
|
|
135
|
+
|
|
136
|
+
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
|
137
|
+
if (changedProperties.has("country")) {
|
|
138
|
+
this.selectedCountry = getPhoneCountry(this.country);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
get e164(): string | null {
|
|
143
|
+
return getE164PhoneNumber(this.value, this.selectedCountry);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get isValid(): boolean {
|
|
147
|
+
return this.e164 !== null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private dispatchPhoneChange(): void {
|
|
151
|
+
this.dispatchEvent(
|
|
152
|
+
new CustomEvent<InternationalPhoneChangeDetail>("phone-change", {
|
|
153
|
+
bubbles: true,
|
|
154
|
+
composed: true,
|
|
155
|
+
detail: {
|
|
156
|
+
country: this.selectedCountry,
|
|
157
|
+
e164: this.e164,
|
|
158
|
+
formattedValue: this.value,
|
|
159
|
+
isValid: this.isValid,
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private setFormattedInputValue(
|
|
166
|
+
input: HTMLInputElement,
|
|
167
|
+
rawValue: string,
|
|
168
|
+
country: string
|
|
169
|
+
): void {
|
|
170
|
+
const selectionStart = input.selectionStart;
|
|
171
|
+
const digitsAfterCursor =
|
|
172
|
+
selectionStart === null
|
|
173
|
+
? 0
|
|
174
|
+
: rawValue.slice(selectionStart).replace(/\D/g, "").length;
|
|
175
|
+
this.value = formatToPhoneInput(rawValue, country);
|
|
176
|
+
input.value = this.value;
|
|
177
|
+
|
|
178
|
+
let cursorPosition = selectionStart === 0 ? 0 : this.value.length;
|
|
179
|
+
let remainingDigits = digitsAfterCursor;
|
|
180
|
+
while (remainingDigits > 0 && cursorPosition > 0) {
|
|
181
|
+
cursorPosition -= 1;
|
|
182
|
+
if (/\d/.test(this.value[cursorPosition])) remainingDigits -= 1;
|
|
183
|
+
}
|
|
184
|
+
input.setSelectionRange(cursorPosition, cursorPosition);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private handleInput(event: Event): void {
|
|
188
|
+
const input = event.target as HTMLInputElement;
|
|
189
|
+
const inferredCountry = getCountryFromPhoneInput(
|
|
190
|
+
input.value,
|
|
191
|
+
this.selectedCountry
|
|
192
|
+
);
|
|
193
|
+
this.selectedCountry = inferredCountry;
|
|
194
|
+
this.setFormattedInputValue(input, input.value, inferredCountry);
|
|
195
|
+
this.dispatchPhoneChange();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private handleCountryChange(event: Event): void {
|
|
199
|
+
const previousCountry = this.selectedCountry;
|
|
200
|
+
this.selectedCountry = (event.target as HTMLSelectElement).value;
|
|
201
|
+
const nationalNumber = getNationalPhoneNumber(this.value, previousCountry);
|
|
202
|
+
this.value = formatToPhoneInput(nationalNumber, this.selectedCountry);
|
|
203
|
+
this.dispatchPhoneChange();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private handleKeydown(event: KeyboardEvent): void {
|
|
207
|
+
const input = event.target as HTMLInputElement;
|
|
208
|
+
if (
|
|
209
|
+
event.key !== "Backspace" ||
|
|
210
|
+
input.selectionStart === null ||
|
|
211
|
+
input.selectionEnd !== input.selectionStart ||
|
|
212
|
+
input.selectionStart === 0 ||
|
|
213
|
+
(input.selectionStart === 1 && input.value.startsWith("+")) ||
|
|
214
|
+
/\d/.test(input.value[input.selectionStart - 1])
|
|
215
|
+
) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const digitsBeforeCursor = input.value
|
|
220
|
+
.slice(0, input.selectionStart)
|
|
221
|
+
.replace(/\D/g, "");
|
|
222
|
+
const digitsAfterCursor = input.value
|
|
223
|
+
.slice(input.selectionStart)
|
|
224
|
+
.replace(/\D/g, "");
|
|
225
|
+
const internationalPrefix = input.value.trimStart().startsWith("+")
|
|
226
|
+
? "+"
|
|
227
|
+
: "";
|
|
228
|
+
const digitsBeforeUpdatedCursor = digitsBeforeCursor.slice(0, -1);
|
|
229
|
+
const updatedValue = `${internationalPrefix}${digitsBeforeUpdatedCursor}${digitsAfterCursor}`;
|
|
230
|
+
const updatedCursorPosition =
|
|
231
|
+
internationalPrefix.length + digitsBeforeUpdatedCursor.length;
|
|
232
|
+
const inferredCountry = getCountryFromPhoneInput(
|
|
233
|
+
updatedValue,
|
|
234
|
+
this.selectedCountry
|
|
235
|
+
);
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
input.setSelectionRange(updatedCursorPosition, updatedCursorPosition);
|
|
238
|
+
this.selectedCountry = inferredCountry;
|
|
239
|
+
this.setFormattedInputValue(input, updatedValue, inferredCountry);
|
|
240
|
+
this.dispatchPhoneChange();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private handleBlur(): void {
|
|
244
|
+
this.dispatchEvent(
|
|
245
|
+
new Event("phone-blur", { bubbles: true, composed: true })
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private get selectedCountryLabel(): string {
|
|
250
|
+
return (
|
|
251
|
+
phoneCountryOptions.find(
|
|
252
|
+
(option) => option.value === this.selectedCountry
|
|
253
|
+
)?.compactLabel ?? this.selectedCountry
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
render(): TemplateResult {
|
|
258
|
+
return html`
|
|
259
|
+
<div
|
|
260
|
+
class=${classMap({
|
|
261
|
+
"phone-input-container": true,
|
|
262
|
+
"phone-input-container--invalid": this.invalid,
|
|
263
|
+
})}
|
|
264
|
+
>
|
|
265
|
+
<div class="country-select-wrapper">
|
|
266
|
+
<span class="country-select-label" aria-hidden="true"
|
|
267
|
+
>${this.selectedCountryLabel}</span
|
|
268
|
+
>
|
|
269
|
+
<select
|
|
270
|
+
aria-label="Country calling code"
|
|
271
|
+
.value=${this.selectedCountry}
|
|
272
|
+
@change=${this.handleCountryChange}
|
|
273
|
+
>
|
|
274
|
+
${phoneCountryOptions.map(
|
|
275
|
+
(option) =>
|
|
276
|
+
html`<option
|
|
277
|
+
value=${option.value}
|
|
278
|
+
?selected=${option.value === this.selectedCountry}
|
|
279
|
+
>
|
|
280
|
+
${option.label}
|
|
281
|
+
</option>`
|
|
282
|
+
)}
|
|
283
|
+
</select>
|
|
284
|
+
</div>
|
|
285
|
+
<span class="phone-input-divider" aria-hidden="true"></span>
|
|
286
|
+
<input
|
|
287
|
+
class="webchat-input"
|
|
288
|
+
type="tel"
|
|
289
|
+
inputmode="tel"
|
|
290
|
+
placeholder="Phone"
|
|
291
|
+
name="phone"
|
|
292
|
+
autocomplete="tel-national"
|
|
293
|
+
maxlength="30"
|
|
294
|
+
.value=${this.value}
|
|
295
|
+
@keydown=${this.handleKeydown}
|
|
296
|
+
@input=${this.handleInput}
|
|
297
|
+
@blur=${this.handleBlur}
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
declare global {
|
|
305
|
+
interface HTMLElementTagNameMap {
|
|
306
|
+
"international-phone-input": InternationalPhoneInput;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { expect } from "@esm-bundle/chai";
|
|
2
|
+
import {
|
|
3
|
+
formatPhoneNumber,
|
|
4
|
+
formatToPhoneInput,
|
|
5
|
+
getCountryFromPhoneInput,
|
|
6
|
+
formatToUsPhoneInput,
|
|
7
|
+
getE164PhoneNumber,
|
|
8
|
+
getPhoneCountry,
|
|
9
|
+
isValidInternationalPhoneNumber,
|
|
10
|
+
phoneCountryOptions,
|
|
11
|
+
} from "./WebComponent/actions/formatPhoneNumber";
|
|
12
|
+
|
|
13
|
+
describe("international phone numbers", () => {
|
|
14
|
+
it("preserves and formats a UK E.164 number instead of truncating it", () => {
|
|
15
|
+
expect(formatToPhoneInput("+44 20 7946 0958")).to.equal("+44 20 7946 0958");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("keeps existing US formatting and E.164 output", () => {
|
|
19
|
+
expect(formatToPhoneInput("2025550123", "US")).to.equal("(202) 555-0123");
|
|
20
|
+
expect(getE164PhoneNumber("(202) 555-0123", "US")).to.equal("+12025550123");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("normalizes national and international UK input", () => {
|
|
24
|
+
expect(getE164PhoneNumber("07400 123456", "GB")).to.equal("+447400123456");
|
|
25
|
+
expect(getE164PhoneNumber("+44 7400 123456", "US")).to.equal(
|
|
26
|
+
"+447400123456"
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("handles shared calling codes and significant leading zeroes", () => {
|
|
31
|
+
expect(getE164PhoneNumber("416 555 0123", "CA")).to.equal("+14165550123");
|
|
32
|
+
expect(getE164PhoneNumber("02 1234 5678", "IT")).to.equal("+390212345678");
|
|
33
|
+
expect(getCountryFromPhoneInput("2025550123", "CA")).to.equal("CA");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("normalizes representative national numbers across regions", () => {
|
|
37
|
+
const samples = [
|
|
38
|
+
["US", "2025550123", "+12025550123"],
|
|
39
|
+
["CA", "4165550123", "+14165550123"],
|
|
40
|
+
["MX", "5512345678", "+525512345678"],
|
|
41
|
+
["GB", "07400123456", "+447400123456"],
|
|
42
|
+
["IT", "0212345678", "+390212345678"],
|
|
43
|
+
["AU", "0412345678", "+61412345678"],
|
|
44
|
+
["DE", "030123456", "+4930123456"],
|
|
45
|
+
["IN", "9876543210", "+919876543210"],
|
|
46
|
+
["JP", "09012345678", "+819012345678"],
|
|
47
|
+
["BR", "11912345678", "+5511912345678"],
|
|
48
|
+
["ZA", "0821234567", "+27821234567"],
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
for (const [country, nationalNumber, expectedE164] of samples) {
|
|
52
|
+
expect(getE164PhoneNumber(nationalNumber, country)).to.equal(
|
|
53
|
+
expectedE164,
|
|
54
|
+
country
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("rejects impossible numbers", () => {
|
|
60
|
+
expect(isValidInternationalPhoneNumber("123", "GB")).to.equal(false);
|
|
61
|
+
expect(getE164PhoneNumber("123", "GB")).to.equal(null);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("offers every supported country with common regions pinned", () => {
|
|
65
|
+
expect(phoneCountryOptions.length).to.be.greaterThan(200);
|
|
66
|
+
expect(
|
|
67
|
+
phoneCountryOptions.slice(0, 4).map(({ value }) => value)
|
|
68
|
+
).to.deep.equal(["US", "CA", "MX", "GB"]);
|
|
69
|
+
expect(phoneCountryOptions.find(({ value }) => value === "MX")).to.include({
|
|
70
|
+
compactLabel: "🇲🇽 +52",
|
|
71
|
+
label: "🇲🇽 +52 – Mexico",
|
|
72
|
+
});
|
|
73
|
+
expect(
|
|
74
|
+
new Set(phoneCountryOptions.map(({ value }) => value)).size
|
|
75
|
+
).to.equal(phoneCountryOptions.length);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("falls back to the US for missing or unsupported building countries", () => {
|
|
79
|
+
expect(getPhoneCountry(null)).to.equal("US");
|
|
80
|
+
expect(getPhoneCountry("unknown")).to.equal("US");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("does not change the separate leasing-office display formatter", () => {
|
|
84
|
+
expect(formatPhoneNumber("+12025550123")).to.equal("+1 (202) 555-0123");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("keeps Text Us limited to its existing ten-digit US format", () => {
|
|
88
|
+
expect(formatToUsPhoneInput("2025550123")).to.equal("(202) 555-0123");
|
|
89
|
+
expect(formatToUsPhoneInput("+44 20 7946 0958")).to.equal("(442) 079-4609");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { expect } from "@esm-bundle/chai";
|
|
2
|
+
import "../public/dist/index";
|
|
3
|
+
import type {
|
|
4
|
+
InternationalPhoneChangeDetail,
|
|
5
|
+
InternationalPhoneInput,
|
|
6
|
+
} from "./WebComponent/actions/international-phone-input";
|
|
7
|
+
import type { EmailUsWindow } from "./WebComponent/actions/email-us-window";
|
|
8
|
+
import type { CallUsWindow } from "./WebComponent/actions/call-us-window";
|
|
9
|
+
|
|
10
|
+
describe("international-phone-input", () => {
|
|
11
|
+
let phoneInput: InternationalPhoneInput;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
if (!customElements.get("international-phone-input")) {
|
|
15
|
+
throw new Error("Expected international-phone-input to be registered");
|
|
16
|
+
}
|
|
17
|
+
phoneInput = document.createElement(
|
|
18
|
+
"international-phone-input"
|
|
19
|
+
) as InternationalPhoneInput;
|
|
20
|
+
phoneInput.country = "US";
|
|
21
|
+
document.body.appendChild(phoneInput);
|
|
22
|
+
await phoneInput.updateComplete;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
phoneInput.remove();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("infers the country and E.164 value when an international number is pasted", async () => {
|
|
30
|
+
let detail: InternationalPhoneChangeDetail | null = null;
|
|
31
|
+
phoneInput.addEventListener("phone-change", (event) => {
|
|
32
|
+
detail = (event as CustomEvent<InternationalPhoneChangeDetail>).detail;
|
|
33
|
+
});
|
|
34
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
35
|
+
if (!input) throw new Error("Expected phone input");
|
|
36
|
+
|
|
37
|
+
input.value = "+44 7400 123456";
|
|
38
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
39
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
40
|
+
await phoneInput.updateComplete;
|
|
41
|
+
|
|
42
|
+
expect(phoneInput.e164).to.equal("+447400123456");
|
|
43
|
+
expect(phoneInput.isValid).to.equal(true);
|
|
44
|
+
expect(
|
|
45
|
+
phoneInput.shadowRoot?.querySelector<HTMLSelectElement>("select")?.value
|
|
46
|
+
).to.equal("GB");
|
|
47
|
+
expect(detail).to.deep.include({
|
|
48
|
+
country: "GB",
|
|
49
|
+
e164: "+447400123456",
|
|
50
|
+
isValid: true,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("uses the building country for national input", async () => {
|
|
55
|
+
phoneInput.country = "GB";
|
|
56
|
+
await phoneInput.updateComplete;
|
|
57
|
+
expect(
|
|
58
|
+
phoneInput.shadowRoot?.querySelector<HTMLSelectElement>("select")?.value
|
|
59
|
+
).to.equal("GB");
|
|
60
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
61
|
+
if (!input) throw new Error("Expected phone input");
|
|
62
|
+
|
|
63
|
+
input.value = "07400123456";
|
|
64
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
65
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
66
|
+
await phoneInput.updateComplete;
|
|
67
|
+
|
|
68
|
+
expect(phoneInput.value).to.equal("07400 123456");
|
|
69
|
+
expect(phoneInput.e164).to.equal("+447400123456");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("displays a non-US building country on the first render", async () => {
|
|
73
|
+
phoneInput.remove();
|
|
74
|
+
phoneInput = document.createElement(
|
|
75
|
+
"international-phone-input"
|
|
76
|
+
) as InternationalPhoneInput;
|
|
77
|
+
phoneInput.country = "GB";
|
|
78
|
+
document.body.appendChild(phoneInput);
|
|
79
|
+
await phoneInput.updateComplete;
|
|
80
|
+
|
|
81
|
+
expect(
|
|
82
|
+
phoneInput.shadowRoot?.querySelector<HTMLSelectElement>("select")?.value
|
|
83
|
+
).to.equal("GB");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("retains existing US behavior by default", async () => {
|
|
87
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
88
|
+
if (!input) throw new Error("Expected phone input");
|
|
89
|
+
|
|
90
|
+
input.value = "2025550123";
|
|
91
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
92
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
93
|
+
await phoneInput.updateComplete;
|
|
94
|
+
|
|
95
|
+
expect(phoneInput.value).to.equal("(202) 555-0123");
|
|
96
|
+
expect(phoneInput.e164).to.equal("+12025550123");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("uses a compact selected-country label while keeping full menu labels", async () => {
|
|
100
|
+
const select = phoneInput.shadowRoot?.querySelector("select");
|
|
101
|
+
if (!select) throw new Error("Expected country select");
|
|
102
|
+
select.value = "MX";
|
|
103
|
+
select.dispatchEvent(new Event("change", { bubbles: true }));
|
|
104
|
+
await phoneInput.updateComplete;
|
|
105
|
+
|
|
106
|
+
expect(
|
|
107
|
+
phoneInput.shadowRoot?.querySelector(".country-select-label")?.textContent
|
|
108
|
+
).to.contain("🇲🇽 +52");
|
|
109
|
+
expect(select.selectedOptions[0].textContent).to.contain("🇲🇽 +52 – Mexico");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("deletes the preceding digit when backspacing over formatting", async () => {
|
|
113
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
114
|
+
if (!input) throw new Error("Expected phone input");
|
|
115
|
+
input.value = "2025550123";
|
|
116
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
117
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
118
|
+
await phoneInput.updateComplete;
|
|
119
|
+
|
|
120
|
+
input.setSelectionRange(5, 5);
|
|
121
|
+
input.dispatchEvent(
|
|
122
|
+
new KeyboardEvent("keydown", {
|
|
123
|
+
key: "Backspace",
|
|
124
|
+
bubbles: true,
|
|
125
|
+
composed: true,
|
|
126
|
+
cancelable: true,
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
await phoneInput.updateComplete;
|
|
130
|
+
|
|
131
|
+
expect(phoneInput.value).to.equal("(205) 550-123");
|
|
132
|
+
expect(input.selectionStart).to.equal(3);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("keeps the caret at the start when input is reformatted", async () => {
|
|
136
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
137
|
+
if (!input) throw new Error("Expected phone input");
|
|
138
|
+
input.value = "2025550123";
|
|
139
|
+
input.setSelectionRange(0, 0);
|
|
140
|
+
|
|
141
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
142
|
+
await phoneInput.updateComplete;
|
|
143
|
+
|
|
144
|
+
expect(phoneInput.value).to.equal("(202) 555-0123");
|
|
145
|
+
expect(input.selectionStart).to.equal(0);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("preserves an international prefix when backspacing over formatting", async () => {
|
|
149
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
150
|
+
if (!input) throw new Error("Expected phone input");
|
|
151
|
+
input.value = "+44 7400 123456";
|
|
152
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
153
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
154
|
+
await phoneInput.updateComplete;
|
|
155
|
+
|
|
156
|
+
input.setSelectionRange(9, 9);
|
|
157
|
+
input.dispatchEvent(
|
|
158
|
+
new KeyboardEvent("keydown", {
|
|
159
|
+
key: "Backspace",
|
|
160
|
+
bubbles: true,
|
|
161
|
+
composed: true,
|
|
162
|
+
cancelable: true,
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
await phoneInput.updateComplete;
|
|
166
|
+
|
|
167
|
+
expect(phoneInput.value.startsWith("+")).to.equal(true);
|
|
168
|
+
expect(phoneInput.value.replace(/\D/g, "")).to.equal("44740123456");
|
|
169
|
+
expect(input.selectionStart).to.equal(6);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("allows backspace to remove the leading international prefix", async () => {
|
|
173
|
+
const input = phoneInput.shadowRoot?.querySelector("input");
|
|
174
|
+
if (!input) throw new Error("Expected phone input");
|
|
175
|
+
input.value = "+";
|
|
176
|
+
input.setSelectionRange(1, 1);
|
|
177
|
+
const keydown = new KeyboardEvent("keydown", {
|
|
178
|
+
key: "Backspace",
|
|
179
|
+
bubbles: true,
|
|
180
|
+
composed: true,
|
|
181
|
+
cancelable: true,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
input.dispatchEvent(keydown);
|
|
185
|
+
|
|
186
|
+
expect(keydown.defaultPrevented).to.equal(false);
|
|
187
|
+
input.value = "";
|
|
188
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
189
|
+
await phoneInput.updateComplete;
|
|
190
|
+
expect(phoneInput.value).to.equal("");
|
|
191
|
+
expect(phoneInput.e164).to.equal(null);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("normalizes international numbers through the Email Us form", async () => {
|
|
196
|
+
const emailUs = document.createElement("email-us-window") as EmailUsWindow;
|
|
197
|
+
emailUs.country = "GB";
|
|
198
|
+
document.body.appendChild(emailUs);
|
|
199
|
+
await emailUs.updateComplete;
|
|
200
|
+
|
|
201
|
+
const phoneInput = emailUs.shadowRoot?.querySelector<InternationalPhoneInput>(
|
|
202
|
+
"international-phone-input"
|
|
203
|
+
);
|
|
204
|
+
expect(phoneInput).to.exist;
|
|
205
|
+
expect(phoneInput?.country).to.equal("GB");
|
|
206
|
+
const input = phoneInput?.shadowRoot?.querySelector("input");
|
|
207
|
+
if (!phoneInput || !input) throw new Error("Expected phone input");
|
|
208
|
+
|
|
209
|
+
input.value = "07400123456";
|
|
210
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
211
|
+
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
212
|
+
await phoneInput.updateComplete;
|
|
213
|
+
await emailUs.updateComplete;
|
|
214
|
+
|
|
215
|
+
expect(emailUs.phoneNumber).to.equal("07400 123456");
|
|
216
|
+
expect(emailUs.phoneNumberE164).to.equal("+447400123456");
|
|
217
|
+
expect(emailUs.phoneCountry).to.equal("GB");
|
|
218
|
+
|
|
219
|
+
emailUs.firstName = "Local";
|
|
220
|
+
emailUs.lastName = "Test";
|
|
221
|
+
emailUs.email = "local.test@example.com";
|
|
222
|
+
emailUs.validateFormFields();
|
|
223
|
+
expect(emailUs.hasPhoneNumberError).to.equal(false);
|
|
224
|
+
|
|
225
|
+
emailUs.remove();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("keeps Text Us on the existing US-only phone input", async () => {
|
|
229
|
+
const callUs = document.createElement("call-us-window") as CallUsWindow;
|
|
230
|
+
callUs.hasTextUsEnabled = "true";
|
|
231
|
+
document.body.appendChild(callUs);
|
|
232
|
+
await callUs.updateComplete;
|
|
233
|
+
|
|
234
|
+
expect(callUs.shadowRoot?.querySelector("international-phone-input")).to.not
|
|
235
|
+
.exist;
|
|
236
|
+
expect(
|
|
237
|
+
callUs.shadowRoot?.querySelector('[aria-label="Country calling code"]')
|
|
238
|
+
).to.not.exist;
|
|
239
|
+
|
|
240
|
+
const input = callUs.shadowRoot?.querySelector<HTMLInputElement>(
|
|
241
|
+
'input[placeholder="Enter phone"]'
|
|
242
|
+
);
|
|
243
|
+
if (!input) throw new Error("Expected Text Us phone input");
|
|
244
|
+
input.value = "2025550123";
|
|
245
|
+
input.dispatchEvent(new KeyboardEvent("keyup", { bubbles: true }));
|
|
246
|
+
await callUs.updateComplete;
|
|
247
|
+
|
|
248
|
+
expect(callUs.phoneNumberToText).to.equal("(202) 555-0123");
|
|
249
|
+
expect(input.value).to.equal("(202) 555-0123");
|
|
250
|
+
expect(input.maxLength).to.equal(14);
|
|
251
|
+
|
|
252
|
+
callUs.remove();
|
|
253
|
+
});
|