diff --git a/package-lock.json b/package-lock.json
index 71ec6014..b96c2e26 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,8 @@
"license": "GPL-2.0-or-later",
"dependencies": {
"@lit/localize": "^0.12.1",
+ "country-list-with-dial-code-and-flag": "^5.1.1",
+ "i18n-iso-countries": "^7.14.0",
"iconify-icon": "^1.0.2",
"lit": "^3.1.4"
},
@@ -6167,6 +6169,12 @@
"url": "https://opencollective.com/core-js"
}
},
+ "node_modules/country-list-with-dial-code-and-flag": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/country-list-with-dial-code-and-flag/-/country-list-with-dial-code-and-flag-5.1.1.tgz",
+ "integrity": "sha512-dj93ztplLEV2svD4+oOKTnR+8+cQSNf8kB051w+kYIoxcW6K0BPLgWYqwJjEpvoee60FTBufmi7jvcLPEgfpjg==",
+ "license": "ISC"
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -6421,6 +6429,12 @@
"integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==",
"dev": true
},
+ "node_modules/diacritics": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
+ "integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==",
+ "license": "MIT"
+ },
"node_modules/diff": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
@@ -8136,6 +8150,18 @@
"url": "https://github.com/sponsors/typicode"
}
},
+ "node_modules/i18n-iso-countries": {
+ "version": "7.14.0",
+ "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz",
+ "integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==",
+ "license": "MIT",
+ "dependencies": {
+ "diacritics": "1.3.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/iconify-icon": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.2.tgz",
diff --git a/package.json b/package.json
index 82025221..89fc18ba 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,8 @@
},
"dependencies": {
"@lit/localize": "^0.12.1",
+ "country-list-with-dial-code-and-flag": "^5.1.1",
+ "i18n-iso-countries": "^7.14.0",
"iconify-icon": "^1.0.2",
"lit": "^3.1.4"
},
diff --git a/phone-intl-test.html b/phone-intl-test.html
new file mode 100644
index 00000000..9290bf0d
--- /dev/null
+++ b/phone-intl-test.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ ${this._countryOptionsLoading
+ ? html`
Loading...
`
+ : countryOptions.map(
+ country => html`
+
+ `,
+ )}
+
+
${selectedCountry?.dialCode || '+1'}
+
+
+
+ ${when(
+ itemCount > 1 || item.key || this._hasActualValue(item),
() => html`
@@ -287,6 +680,7 @@ export class DtMultiText extends DtText {
class="input-addon btn-add"
@click=${this._addItem}
?disabled=${this.disabled}
+ type="button"
>
@@ -332,6 +726,193 @@ export class DtMultiText extends DtText {
}
}
+ _changeCountry(e) {
+ const key = e?.currentTarget?.dataset?.key;
+ if (key) {
+ const countryCode = e.target.value;
+ const phoneInput = e.target.parentElement.querySelector(
+ 'input[data-type="phone"]',
+ );
+ const dialCodeDisplay =
+ e.target.parentElement.querySelector('.dial-code');
+ const phoneNumber = phoneInput ? phoneInput.value : '';
+
+ // Update the dial code display
+ const countryOptions = this._countryOptions || [];
+ const selectedCountry = countryOptions.find(c => c.code === countryCode);
+ if (dialCodeDisplay && selectedCountry) {
+ dialCodeDisplay.textContent = selectedCountry.dialCode;
+ }
+
+ const newValue = this._formatPhoneValue(countryCode, phoneNumber);
+
+ const event = new CustomEvent('change', {
+ detail: {
+ field: this.name,
+ oldValue: this.value,
+ },
+ });
+
+ // update this item's value in the list
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ event.detail.newValue = this.value;
+
+ this._setFormValue(this.value);
+ this.dispatchEvent(event);
+ }
+ }
+
+ _toggleCountryDropdown(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (this.disabled) return;
+
+ const { key } = e.currentTarget.dataset;
+ if (this._openDropdownKey === key) {
+ this._closeDropdown();
+ } else {
+ this._openDropdownKey = key;
+ // Add click outside listener when dropdown opens
+ setTimeout(() => {
+ document.addEventListener('click', this._handleClickOutside);
+ }, 0);
+ }
+ }
+
+ _selectCountry(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const { key } = e.currentTarget.dataset;
+ const { countryCode } = e.currentTarget.dataset;
+
+ if (key && countryCode) {
+ // Find the phone input for this item
+ const container = e.currentTarget.closest('.phone-intl-container');
+ const phoneInput = container?.querySelector('input[data-type="phone"]');
+ const phoneNumber = phoneInput ? phoneInput.value : '';
+
+ // Get current item to check if it's really changing
+ const currentItem = this.value.find(x => x.key === key || x.tempKey === key);
+ const currentParsed = this._parsePhoneValue(currentItem?.value || '');
+
+ // Update the value for this item
+ const newValue = this._formatPhoneValue(countryCode, phoneNumber);
+
+ // Only trigger change event if phone number is not empty or if country actually changed
+ const shouldTriggerChange = phoneNumber.trim() !== '' || currentParsed.countryCode !== countryCode;
+
+ if (shouldTriggerChange) {
+ const event = new CustomEvent('change', {
+ detail: {
+ field: this.name,
+ oldValue: this.value,
+ },
+ });
+
+ // update this item's value in the list
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ event.detail.newValue = this.value;
+
+ this._setFormValue(this.value);
+ this.dispatchEvent(event);
+ } else {
+ // Just update the value without triggering change event for empty phone numbers
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ this._setFormValue(this.value);
+ // Trigger a re-render to update the UI
+ this.requestUpdate();
+ }
+
+ // Close the dropdown and trigger a re-render to update the flag
+ this._closeDropdown();
+ // No need to manually update dial code display; let Lit re-render
+ }
+ }
+
+ _closeDropdown() {
+ this._openDropdownKey = null;
+ document.removeEventListener('click', this._handleClickOutside);
+ }
+
+ _handleClickOutside(e) {
+ const dropdown = e.target.closest('.phone-intl-container');
+ if (!dropdown) {
+ this._closeDropdown();
+ }
+ }
+
+ _changePhone(e) {
+ const key = e?.currentTarget?.dataset?.key;
+ if (key) {
+ const phoneNumber = e.target.value;
+
+ // Get current item to preserve country code unless phone number contains a country code
+ const currentItem = this.value.find(x => x.key === key || x.tempKey === key);
+ const currentParsed = this._parsePhoneValue(currentItem?.value || '');
+ let countryCode = currentParsed.countryCode;
+
+ // Check if user entered a full international number (starts with +)
+ if (phoneNumber.startsWith('+')) {
+ const parsed = this._parsePhoneValue(phoneNumber);
+ if (parsed.countryCode && parsed.countryCode !== 'US') {
+ // User entered a full international number, use detected country
+ countryCode = parsed.countryCode;
+ // Update the phone number to just the local part
+ const newValue = this._formatPhoneValue(countryCode, parsed.phoneNumber);
+
+ const event = new CustomEvent('change', {
+ detail: {
+ field: this.name,
+ oldValue: this.value,
+ },
+ });
+
+ // update this item's value in the list
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ event.detail.newValue = this.value;
+
+ this._setFormValue(this.value);
+ this.dispatchEvent(event);
+ return;
+ }
+ }
+
+ // Normal case: just update the phone number part, keep existing country
+ const newValue = this._formatPhoneValue(countryCode, phoneNumber);
+
+ const event = new CustomEvent('change', {
+ detail: {
+ field: this.name,
+ oldValue: this.value,
+ },
+ });
+
+ // update this item's value in the list
+ this.value = this.value.map(x => ({
+ ...x,
+ value: x.key === key || x.tempKey === key ? newValue : x.value,
+ }));
+ event.detail.newValue = this.value;
+
+ this._setFormValue(this.value);
+ this.dispatchEvent(event);
+ }
+ }
+
get classes() {
const classes = {
'text-input': true,
diff --git a/src/components/form/dt-multi-text/dt-multi-text.stories.js b/src/components/form/dt-multi-text/dt-multi-text.stories.js
index 0f28c52a..244dcc2c 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.stories.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.stories.js
@@ -22,7 +22,7 @@ export default {
placeholder: { control: 'text' },
type: {
control: 'select',
- options: ['text', 'password', 'email', 'number', 'tel', 'url'],
+ options: ['text', 'password', 'email', 'number', 'tel', 'url', 'phone-intl'],
defaultValue: 'text',
},
disabled: { control: 'boolean' },
@@ -196,3 +196,50 @@ LocalizeRTL.args = {
},
],
};
+
+export const PhoneIntl = Template.bind({});
+PhoneIntl.args = {
+ type: 'phone-intl',
+ label: 'Phone Numbers',
+ placeholder: 'Enter phone number',
+ value: [
+ {
+ verified: false,
+ value: '+1 555-123-4567',
+ key: 'phone_1',
+ },
+ {
+ verified: false,
+ value: '+44 20 7946 0958',
+ key: 'phone_2',
+ },
+ ],
+};
+
+export const PhoneIntlEmpty = Template.bind({});
+PhoneIntlEmpty.args = {
+ type: 'phone-intl',
+ label: 'Phone Numbers',
+ placeholder: 'Enter phone number',
+};
+
+export const PhoneIntlLocalized = Template.bind({});
+PhoneIntlLocalized.decorators = [LocaleDecorator, FormDecorator];
+PhoneIntlLocalized.args = {
+ type: 'phone-intl',
+ lang: 'fr',
+ label: 'Numéros de téléphone',
+ placeholder: 'Entrez le numéro de téléphone',
+ value: [
+ {
+ verified: false,
+ value: '+33 1 42 86 83 26',
+ key: 'phone_1',
+ },
+ {
+ verified: false,
+ value: '+49 30 12345678',
+ key: 'phone_2',
+ },
+ ],
+};
diff --git a/src/components/form/dt-multi-text/dt-multi-text.test.js b/src/components/form/dt-multi-text/dt-multi-text.test.js
index 9aa08ea1..47b830ed 100644
--- a/src/components/form/dt-multi-text/dt-multi-text.test.js
+++ b/src/components/form/dt-multi-text/dt-multi-text.test.js
@@ -1,13 +1,18 @@
import { html } from 'lit';
-import { fixture, expect, oneEvent, aTimeout, nextFrame } from '@open-wc/testing';
+import {
+ fixture,
+ expect,
+ oneEvent,
+ aTimeout,
+ nextFrame,
+} from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import './dt-multi-text.js';
describe('DtMultiText', () => {
-
it('sets placeholder', async () => {
const el = await fixture(
- html`
`
+ html`
`,
);
const input = el.shadowRoot.querySelector('input');
@@ -17,41 +22,47 @@ describe('DtMultiText', () => {
it('sets value from attribute', async () => {
const el = await fixture(
html`
`
+ value="${JSON.stringify([
+ {
+ key: 'cc01',
+ value: 'Value 1',
+ verified: true,
+ },
+ {
+ key: 'cc02',
+ value: 'Value 2',
+ verified: true,
+ },
+ ])}"
+ >`,
);
const inputGroup = el.shadowRoot.querySelector('.input-group');
- expect(inputGroup.querySelector('input[data-key="cc01"]'))
- .to.exist
- .and.have.value('Value 1');
- expect(inputGroup.querySelector('input[data-key="cc02"]'))
- .to.exist
- .and.have.value('Value 2');
+ expect(
+ inputGroup.querySelector('input[data-key="cc01"]'),
+ ).to.exist.and.have.value('Value 1');
+ expect(
+ inputGroup.querySelector('input[data-key="cc02"]'),
+ ).to.exist.and.have.value('Value 2');
});
it('resets value', async () => {
const el = await fixture(
html`
`
+ value="${JSON.stringify([
+ {
+ key: 'cc01',
+ value: 'Value 1',
+ verified: true,
+ },
+ {
+ key: 'cc02',
+ value: 'Value 2',
+ verified: true,
+ },
+ ])}"
+ >`,
);
el.reset();
@@ -67,9 +78,7 @@ describe('DtMultiText', () => {
});
it('adds a new item on add button click', async () => {
- const el = await fixture(
- html`
`
- );
+ const el = await fixture(html`
`);
expect(el.shadowRoot.querySelectorAll('input')).to.have.length(1);
@@ -84,16 +93,19 @@ describe('DtMultiText', () => {
it('deletes an item on remove button click', async () => {
const el = await fixture(
html`
`
+ value="${JSON.stringify([
+ {
+ key: 'cc01',
+ value: 'Value 1',
+ verified: true,
+ },
+ {
+ key: 'cc02',
+ value: 'Value 2',
+ verified: true,
+ },
+ ])}"
+ >`,
);
expect(el.shadowRoot.querySelectorAll('input')).to.have.length(2);
@@ -121,9 +133,7 @@ describe('DtMultiText', () => {
});
it('triggers a change event - item added', async () => {
- const el = await fixture(
- html`
`
- );
+ const el = await fixture(html`
`);
const input = el.shadowRoot.querySelector('input');
input.focus();
@@ -131,28 +141,30 @@ describe('DtMultiText', () => {
setTimeout(async () => {
await sendKeys({ type: 'Test' });
input.blur();
- })
+ });
const { detail } = await oneEvent(el, 'change');
expect(detail.newValue.length).to.equal(1);
- expect(detail.newValue[0].value).to.equal('Test')
-
+ expect(detail.newValue[0].value).to.equal('Test');
});
it('triggers a change event - item removed', async () => {
const el = await fixture(
html`
`
+ value="${JSON.stringify([
+ {
+ key: 'cc01',
+ value: 'Value 1',
+ verified: true,
+ },
+ {
+ key: 'cc02',
+ value: 'Value 2',
+ verified: true,
+ },
+ ])}"
+ >`,
);
expect(el.shadowRoot.querySelectorAll('input')).to.have.length(2);
@@ -165,7 +177,141 @@ describe('DtMultiText', () => {
expect(detail.oldValue).to.have.length(2);
expect(detail.newValue).to.have.length(2);
- expect(detail.newValue[0].value).to.equal('Value 1')
+ expect(detail.newValue[0].value).to.equal('Value 1');
expect(detail.newValue[0].delete).to.equal(true);
});
+
+ describe('phone-intl type', () => {
+ it('renders country code dropdown for phone-intl type', async () => {
+ const el = await fixture(
+ html`
`,
+ );
+
+ const countryButton = el.shadowRoot.querySelector('.country-button');
+ expect(countryButton).to.exist;
+ countryButton.click();
+ await nextFrame();
+ const dropdown = el.shadowRoot.querySelector('.country-dropdown');
+ expect(dropdown).to.exist;
+ expect(dropdown.classList.contains('open')).to.be.true;
+ const options = dropdown.querySelectorAll('.country-option');
+ expect(options.length).to.be.greaterThan(0);
+ });
+
+ it('renders phone number input for phone-intl type', async () => {
+ const el = await fixture(
+ html`
`,
+ );
+
+ const phoneInput = el.shadowRoot.querySelector(
+ 'input[data-type="phone"]',
+ );
+ expect(phoneInput).to.exist;
+ });
+
+ it('handles phone-intl value format', async () => {
+ const el = await fixture(
+ html`
`,
+ );
+
+ const button = el.shadowRoot.querySelector('.country-button');
+ expect(button).to.exist;
+ const dialCode = el.shadowRoot.querySelector('.dial-code');
+ expect(dialCode).to.exist;
+ expect(dialCode.textContent.trim()).to.equal('+1');
+ const phoneInput = el.shadowRoot.querySelector(
+ 'input[data-type="phone"]',
+ );
+ expect(phoneInput.value).to.equal('555-123-4567');
+ });
+
+ it('detects country code when entering full international number', async () => {
+ const el = await fixture(
+ html`
`,
+ );
+
+ await nextFrame(); // Allow country options to load
+
+ const phoneInput = el.shadowRoot.querySelector(
+ 'input[data-type="phone"]',
+ );
+
+ // Simulate typing a full international number
+ phoneInput.value = '+961 12345678';
+ phoneInput.dispatchEvent(new Event('change', { bubbles: true }));
+
+ await nextFrame();
+
+ // Check that Lebanon (+961) was detected
+ const dialCode = el.shadowRoot.querySelector('.dial-code');
+ expect(dialCode.textContent.trim()).to.equal('+961');
+ expect(phoneInput.value).to.equal('12345678');
+ });
+
+ it('allows country selection when field is empty', async () => {
+ const el = await fixture(
+ html`
`,
+ );
+
+ await nextFrame(); // Allow country options to load
+
+ // Click the country button to open dropdown
+ const countryButton = el.shadowRoot.querySelector('.country-button');
+ countryButton.click();
+ await nextFrame();
+
+ // Select a different country (e.g., UK)
+ const ukOption = el.shadowRoot.querySelector(
+ '.country-option[data-country-code="GB"]',
+ );
+ if (ukOption) {
+ ukOption.click();
+ await nextFrame();
+
+ // Check that the dial code was updated
+ const dialCode = el.shadowRoot.querySelector('.dial-code');
+ expect(dialCode.textContent.trim()).to.equal('+44');
+
+ // Check that a change event was triggered with the dial code
+ expect(el.value[0].value).to.equal('+44 ');
+ }
+ });
+
+ it('handles various international phone number formats', async () => {
+ const el = await fixture(
+ html`
`,
+ );
+
+ await nextFrame(); // Allow country options to load
+
+ // Test various phone number formats
+ const testCases = [
+ { input: '+96112345678', expectedDialCode: '+961', expectedPhone: '12345678' },
+ { input: '+1 555 555 5555', expectedDialCode: '+1', expectedPhone: '555 555 5555' }, // Stored format preserves spaces
+ { input: '+15555555555', expectedDialCode: '+1', expectedPhone: '5555555555' }, // Compact format strips spaces
+ { input: '0096112345678', expectedDialCode: '+961', expectedPhone: '12345678' },
+ { input: '001 555 555 5555', expectedDialCode: '+1', expectedPhone: '5555555555' }, // Cleaned format strips spaces
+ { input: '00155555555555', expectedDialCode: '+1', expectedPhone: '55555555555' },
+ ];
+
+ for (const testCase of testCases) {
+ const parsed = el._parsePhoneValue(testCase.input);
+ // For +1 dial code, always expect US as it's marked as preferred
+ const expectedCountryCode = testCase.expectedDialCode === '+1' ? 'US' :
+ el._countries.find(c => c.data.dial_code === testCase.expectedDialCode)?.data?.code || 'US';
+ expect(parsed.countryCode).to.equal(expectedCountryCode, `Failed for input: ${testCase.input}`);
+ expect(parsed.phoneNumber).to.equal(testCase.expectedPhone, `Failed for input: ${testCase.input}`);
+ }
+ });
+ });
});
diff --git a/src/i18n/country-names.js b/src/i18n/country-names.js
new file mode 100644
index 00000000..491f60b0
--- /dev/null
+++ b/src/i18n/country-names.js
@@ -0,0 +1,40 @@
+// This file statically imports all country name JSONs from i18n-iso-countries/langs/
+// and exports them as a single object keyed by language code.
+// Update this list if you add/remove supported languages.
+
+import en from 'i18n-iso-countries/langs/en.json';
+import fr from 'i18n-iso-countries/langs/fr.json';
+import es from 'i18n-iso-countries/langs/es.json';
+import de from 'i18n-iso-countries/langs/de.json';
+import it from 'i18n-iso-countries/langs/it.json';
+import ru from 'i18n-iso-countries/langs/ru.json';
+import zh from 'i18n-iso-countries/langs/zh.json';
+import ar from 'i18n-iso-countries/langs/ar.json';
+import pt from 'i18n-iso-countries/langs/pt.json';
+import tr from 'i18n-iso-countries/langs/tr.json';
+import ja from 'i18n-iso-countries/langs/ja.json';
+import nl from 'i18n-iso-countries/langs/nl.json';
+import pl from 'i18n-iso-countries/langs/pl.json';
+import sv from 'i18n-iso-countries/langs/sv.json';
+import uk from 'i18n-iso-countries/langs/uk.json';
+import ko from 'i18n-iso-countries/langs/ko.json';
+import cs from 'i18n-iso-countries/langs/cs.json';
+import da from 'i18n-iso-countries/langs/da.json';
+import fi from 'i18n-iso-countries/langs/fi.json';
+import nb from 'i18n-iso-countries/langs/nb.json';
+import ro from 'i18n-iso-countries/langs/ro.json';
+import hu from 'i18n-iso-countries/langs/hu.json';
+import el from 'i18n-iso-countries/langs/el.json';
+import he from 'i18n-iso-countries/langs/he.json';
+import fa from 'i18n-iso-countries/langs/fa.json';
+import hi from 'i18n-iso-countries/langs/hi.json';
+import id from 'i18n-iso-countries/langs/id.json';
+import th from 'i18n-iso-countries/langs/th.json';
+import vi from 'i18n-iso-countries/langs/vi.json';
+
+export const countryNames = {
+ en, fr, es, de, it, ru, zh, ar, pt, tr, ja, nl, pl, sv, uk, ko, cs, da, fi, nb, ro, hu, el, he, fa, hi, id, th, vi
+};
+
+// Usage: import { countryNames } from './country-names.js';
+// countryNames['en'].countries['US'] // "United States"
diff --git a/test-phone-fixes.html b/test-phone-fixes.html
new file mode 100644
index 00000000..f9654ead
--- /dev/null
+++ b/test-phone-fixes.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+
Phone International Component Test
+
+
+
+
Phone International Component Test
+
+
+
Test 1: Empty Field Country Selection
+
This field should allow country selection when empty and trigger change events.
+
+
+
+
+
+
Test 2: Full Width Layout
+
This field should take the full width of its container.
+
+
+
+
+
+
Test 3: Country Code Auto-Detection
+
Try typing "+96112345678" in the phone input - it should detect Lebanon and switch to +961.
+
+
+
+
+
+
Test 4: Existing Values
+
Pre-populated values showing dial code sizing and alignment.
+
+
+
+
+
+
Test 5: Lebanon Number Test
+
Testing with Lebanon (+961) dial code to check 40px min-width.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test-phone-intl.html b/test-phone-intl.html
new file mode 100644
index 00000000..64a8ae77
--- /dev/null
+++ b/test-phone-intl.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+
Phone International Test
+
+
+
+
+
Phone International Component Test
+
+
+
Empty Phone International
+
+
+
+
+
+
Populated Phone International
+
+
+
+
+
+
French Localized Phone International
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test-phone-parsing.html b/test-phone-parsing.html
new file mode 100644
index 00000000..e6aad19d
--- /dev/null
+++ b/test-phone-parsing.html
@@ -0,0 +1,126 @@
+
+
+
+
+
+
Phone International Parsing Test
+
+
+
+
+
Phone International Component Test
+
+
Country Code Auto-Detection Tests
+
+
+
Interactive Test
+
+
Empty Phone International
+
+
+
+
+
With Lebanon Number for Testing (+961)
+
+
+
+
+
Test Instructions:
+
+ - Try typing: +96112345678 in the empty field
+ - Expected: Should detect Lebanon (+961) and separate the dial code
+ - Try selecting different countries when the field is empty
+ - Expected: Should not show remove button for empty phone number
+ - Try various formats: +1 555 555 5555, +15555555555, 001 555 555 5555
+
+
+
+
+
\ No newline at end of file