From 842af74ebf54a9450c0012edf60b737cdac09359 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Tue, 17 Sep 2024 16:44:38 -0400 Subject: [PATCH 1/5] LG-14382: Update intl-tel-input to latest version changelog: Internal, Dependencies, Update dependency to latest version --- .eslintrc | 3 ++ app/components/phone_input_component.scss | 4 ++ .../packages/phone-input/index.spec.ts | 14 ++---- app/javascript/packages/phone-input/index.ts | 49 +++++++++---------- .../packages/phone-input/package.json | 2 +- app/javascript/packs/idv-phone-alert.ts | 4 +- package.json | 3 +- .../idv/doc_auth/hybrid_handoff_spec.rb | 4 +- spec/features/phone/add_phone_spec.rb | 14 +++--- spec/support/matchers/accessibility.rb | 2 - yarn.lock | 27 ++-------- 11 files changed, 52 insertions(+), 74 deletions(-) diff --git a/.eslintrc b/.eslintrc index 015b41f4bdf..a1e609ad95f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,6 +15,9 @@ "globalThis": true }, "rules": { + // `no-unresolved` doesn't support package.json exports + // See: https://github.com/import-js/eslint-plugin-import/issues/1810 + "import/no-unresolved": ["error", { "ignore": ["intl-tel-input"] }], "no-restricted-syntax": [ "error", { diff --git a/app/components/phone_input_component.scss b/app/components/phone_input_component.scss index 5f653cf6ea9..6bb73b90098 100644 --- a/app/components/phone_input_component.scss +++ b/app/components/phone_input_component.scss @@ -15,6 +15,10 @@ lg-phone-input { } } + .iti__search-input { + @extend %block-input-styles; + } + .iti__dial-code { color: color('ink'); } diff --git a/app/javascript/packages/phone-input/index.spec.ts b/app/javascript/packages/phone-input/index.spec.ts index 17fb61d9340..b43edf0e32c 100644 --- a/app/javascript/packages/phone-input/index.spec.ts +++ b/app/javascript/packages/phone-input/index.spec.ts @@ -3,16 +3,11 @@ import userEvent from '@testing-library/user-event'; import { computeAccessibleName } from 'dom-accessibility-api'; import * as analytics from '@18f/identity-analytics'; import { useSandbox } from '@18f/identity-test-helpers'; +import './index.ts'; describe('PhoneInput', () => { const sandbox = useSandbox(); - before(async () => { - await import('intl-tel-input/build/js/utils.js'); - window.intlTelInputUtils = global.intlTelInputUtils; - await import('./index'); - }); - beforeEach(() => { sandbox.stub(analytics, 'trackEvent'); }); @@ -164,6 +159,7 @@ describe('PhoneInput', () => { expect(analytics.trackEvent).to.have.been.calledOnceWith('phone_input_country_changed', { country_code: 'CA', }); + await userEvent.clear(phoneNumber); const dropdownButton = getByRole(iti, 'combobox', { name: 'Country code' }); await userEvent.click(dropdownButton); @@ -180,7 +176,7 @@ describe('PhoneInput', () => { it('renders as an accessible combobox', () => { const phoneInput = createAndConnectElement(); - const comboboxes = getAllByRole(phoneInput, 'combobox'); + const comboboxes = getAllByRole(phoneInput, 'combobox', { name: 'Country code' }); const listbox = getByRole(phoneInput, 'listbox'); // There are two comboboxes, one for no-JavaScript, and the other JavaScript enhanced. Only one @@ -198,7 +194,7 @@ describe('PhoneInput', () => { // // See: https://w3c.github.io/aria/#combobox const value = combobox.textContent; - const controlled = document.getElementById(combobox.getAttribute('aria-controls')!); + const controlled = document.getElementById(combobox.getAttribute('aria-controls')!)!; // "listbox" is the default value for a combobox role. // @@ -213,7 +209,7 @@ describe('PhoneInput', () => { expect(hasPopup).to.be.oneOf([null, 'listbox']); expect(name).to.equal('Country code'); expect(value).to.equal('United States: +1'); - expect(controlled).to.equal(listbox); + expect(controlled.contains(listbox)).to.be.true(); }); context('with constrained delivery options', () => { diff --git a/app/javascript/packages/phone-input/index.ts b/app/javascript/packages/phone-input/index.ts index c193e793bdb..60712f83a50 100644 --- a/app/javascript/packages/phone-input/index.ts +++ b/app/javascript/packages/phone-input/index.ts @@ -1,8 +1,7 @@ import { isValidNumberForRegion, isValidNumber } from 'libphonenumber-js'; -import 'intl-tel-input/build/js/utils.js'; -import intlTelInput from 'intl-tel-input'; +import intlTelInput from 'intl-tel-input/intlTelInputWithUtils'; import type { CountryCode } from 'libphonenumber-js'; -import type { Plugin as IntlTelInputPlugin, Options } from 'intl-tel-input'; +import type { Iti } from 'intl-tel-input'; import { replaceVariables } from '@18f/identity-i18n'; import { trackEvent } from '@18f/identity-analytics'; @@ -16,14 +15,6 @@ interface PhoneInputStrings { unsupported_country: string; } -interface IntlTelInput extends IntlTelInputPlugin { - flagsContainer: HTMLElement; - - selectedFlag: HTMLElement; - - options: Options; -} - const updateInternationalCodeInPhone = (phone, newCode) => phone.replace(new RegExp(`^\\+?(\\d+\\s+|${newCode})?`), `+${newCode} `); @@ -40,7 +31,7 @@ export class PhoneInputElement extends HTMLElement { codeWrapper: Element | null; - iti: IntlTelInput; + iti: Iti; connectedCallback() { const textInput = this.querySelector('.phone-input__number'); @@ -103,7 +94,15 @@ export class PhoneInputElement extends HTMLElement { * @see https://w3c.github.io/aria/#combobox */ get valueText(): HTMLElement { - return this.iti.selectedFlag.querySelector('.usa-sr-only')!; + return this.selectedCountry.querySelector('.iti__a11y-text')!; + } + + get selectedCountry(): HTMLElement { + return this.querySelector('.iti__selected-country')!; + } + + get countryList(): HTMLUListElement | null { + return this.querySelector('.iti__country-list')!; } /** @@ -125,8 +124,8 @@ export class PhoneInputElement extends HTMLElement { this.codeInput.value = countryCode; // Move value text from title attribute to the flag's hidden text element. // See: https://github.com/jackocnr/intl-tel-input/blob/d54b127/src/js/intlTelInput.js#L1191-L1197 - this.valueText.textContent = this.iti.selectedFlag.title; - this.iti.selectedFlag.removeAttribute('title'); + this.valueText.textContent = this.selectedCountry.title; + this.selectedCountry.removeAttribute('title'); if (fireChangeEvent) { this.codeInput.dispatchEvent(new CustomEvent('change', { bubbles: true })); } @@ -137,21 +136,23 @@ export class PhoneInputElement extends HTMLElement { const { supportedCountryCodes, countryCodePairs } = this; const iti = intlTelInput(this.textInput, { - preferredCountries: ['US', 'CA'], + countryOrder: ['US', 'CA'], initialCountry: this.codeInput.value, - localizedCountries: countryCodePairs, + i18n: countryCodePairs, onlyCountries: supportedCountryCodes, autoPlaceholder: 'off', - }) as IntlTelInput; + formatAsYouType: false, + useFullscreenPopup: false, + }); this.iti = iti; // Remove duplicate items in the country list const preferred: NodeListOf = - iti.countryList.querySelectorAll('.iti__preferred'); + this.countryList!.querySelectorAll('.iti__preferred'); preferred.forEach((listItem) => { const { countryCode } = listItem.dataset; - const duplicates: NodeListOf = iti.countryList.querySelectorAll( + const duplicates: NodeListOf = this.countryList!.querySelectorAll( `.iti__standard[data-country-code="${countryCode}"]`, ); duplicates.forEach((duplicateListItem) => { @@ -160,11 +161,9 @@ export class PhoneInputElement extends HTMLElement { }); // Improve base accessibility of intl-tel-input - const valueText = document.createElement('div'); - valueText.classList.add('usa-sr-only'); - iti.selectedFlag.appendChild(valueText); - iti.selectedFlag.setAttribute('aria-label', this.strings.country_code_label); - iti.selectedFlag.removeAttribute('aria-owns'); + this.selectedCountry.setAttribute('aria-haspopup', 'listbox'); + this.selectedCountry.setAttribute('aria-label', this.strings.country_code_label); + this.selectedCountry.removeAttribute('aria-owns'); this.syncCountryToCodeInput({ fireChangeEvent: false }); diff --git a/app/javascript/packages/phone-input/package.json b/app/javascript/packages/phone-input/package.json index 2a9cc2c93ea..76fccbd037b 100644 --- a/app/javascript/packages/phone-input/package.json +++ b/app/javascript/packages/phone-input/package.json @@ -3,7 +3,7 @@ "private": true, "version": "1.0.0", "dependencies": { - "intl-tel-input": "^17.0.19", + "intl-tel-input": "^24.5.0", "libphonenumber-js": "^1.11.4" }, "sideEffects": [ diff --git a/app/javascript/packs/idv-phone-alert.ts b/app/javascript/packs/idv-phone-alert.ts index 0503bf6affc..e5eaa624017 100644 --- a/app/javascript/packs/idv-phone-alert.ts +++ b/app/javascript/packs/idv-phone-alert.ts @@ -5,8 +5,6 @@ const { iti, textInput: input } = document.querySelector('lg-phone-input') as Ph const failedPhoneNumbers: string[] = JSON.parse(alertElement.dataset.failedPhoneNumbers!); input.addEventListener('input', () => { - const isFailedPhoneNumber = failedPhoneNumbers.includes( - iti.getNumber(intlTelInputUtils.numberFormat.E164), - ); + const isFailedPhoneNumber = failedPhoneNumbers.includes(iti.getNumber()); alertElement.hidden = !isFailedPhoneNumber; }); diff --git a/package.json b/package.json index a95a3c5ac8a..677d28c5c72 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "core-js": "^3.21.1", "fast-glob": "^3.3.2", "foundation-emails": "^2.3.1", - "intl-tel-input": "^17.0.19", + "intl-tel-input": "^24.5.0", "react": "^17.0.2", "react-dom": "^17.0.2", "source-map-loader": "^4.0.0", @@ -56,7 +56,6 @@ "@types/cleave.js": "^1.4.7", "@types/dirty-chai": "^2.0.2", "@types/grecaptcha": "^3.0.4", - "@types/intl-tel-input": "^17.0.6", "@types/mocha": "^10.0.0", "@types/node": "^20.2.5", "@types/react": "^17.0.39", diff --git a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb index 1cdff17831d..075ede1f608 100644 --- a/spec/features/idv/doc_auth/hybrid_handoff_spec.rb +++ b/spec/features/idv/doc_auth/hybrid_handoff_spec.rb @@ -87,8 +87,8 @@ end it 'displays error if user selects a country to which we cannot send SMS', js: true do - page.find('div[aria-label="Country code"]').click - within(page.find('.iti__flag-container', visible: :all)) do + click_on t('components.phone_input.country_code_label') + within(page.find('.iti__country-container', visible: :all)) do find('span', text: 'Sri Lanka').click end focused_input = page.find('.phone-input__number:focus') diff --git a/spec/features/phone/add_phone_spec.rb b/spec/features/phone/add_phone_spec.rb index c95e98579aa..38b3badf26a 100644 --- a/spec/features/phone/add_phone_spec.rb +++ b/spec/features/phone/add_phone_spec.rb @@ -80,8 +80,8 @@ expect(error_message).to have_content(t('errors.messages.invalid_phone_number.us')) # Unsupported country should prompt as invalid and hide delivery options immediately - page.find('div[aria-label="Country code"]').click - within(page.find('.iti__flag-container', visible: :all)) do + click_on t('components.phone_input.country_code_label') + within(page.find('.iti__country-container', visible: :all)) do find('span', text: 'Sri Lanka').click end focused_input = page.find('.phone-input__number:focus') @@ -106,8 +106,8 @@ expect(page.find(':focus')).to match_css('.phone-input__number') # Switching to supported country should re-show delivery options, but prompt as invalid number - page.find('div[aria-label="Country code"]').click - within(page.find('.iti__flag-container', visible: :all)) do + click_on t('components.phone_input.country_code_label') + within(page.find('.iti__country-container', visible: :all)) do find('span', text: 'United States').click end expect(page).to have_content(t('two_factor_authentication.otp_delivery_preference.title')) @@ -162,7 +162,7 @@ expect(page).to have_content(I18n.t('errors.messages.phone_duplicate')) # Ensure that phone input initializes with the country flag maintained - expect(page).to have_css('.iti__selected-flag [class^="iti__flag iti__"]', visible: :all) + expect(page).to have_css('.iti__selected-country [class^="iti__flag iti__"]', visible: :all) end end @@ -193,8 +193,8 @@ expect(page.find_field('Text message (SMS)', disabled: false, visible: :all)).to be_present expect(page.find_field('Phone call', disabled: false, visible: :all)).to be_present - page.find('div[aria-label="Country code"]').click - within(page.find('.iti__flag-container', visible: :all)) do + click_on t('components.phone_input.country_code_label') + within(page.find('.iti__country-container', visible: :all)) do find('span', text: 'Australia').click # a country where SMS is disabled currently end diff --git a/spec/support/matchers/accessibility.rb b/spec/support/matchers/accessibility.rb index 457cf193e9e..0f0de51e490 100644 --- a/spec/support/matchers/accessibility.rb +++ b/spec/support/matchers/accessibility.rb @@ -347,8 +347,6 @@ def expect_page_to_have_no_accessibility_violations(page, validate_markup: true) :wcag22a, :wcag22aa, :"best-practice", - ).excluding( - '.iti__selected-flag', # See: LG-14382 ) expect(page).to have_unique_ids expect(page).to have_valid_idrefs diff --git a/yarn.lock b/yarn.lock index 692899c6446..6496a57e5f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1517,13 +1517,6 @@ dependencies: "@types/node" "*" -"@types/intl-tel-input@^17.0.6": - version "17.0.6" - resolved "https://registry.yarnpkg.com/@types/intl-tel-input/-/intl-tel-input-17.0.6.tgz#43d5fa06c3e988747670bc9f8c3ef148faf9e9a2" - integrity sha512-Xqkfun/71N3wqvnwFzZiBacC3JsHHgYWjOEXxzl91nXrm/b/DLhDWM7baXOZksfLwggyOsn/McT1/neJejXmVg== - dependencies: - "@types/jquery" "*" - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -1543,13 +1536,6 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jquery@*": - version "3.5.16" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.16.tgz#632131baf30951915b0317d48c98e9890bdf051d" - integrity sha512-bsI7y4ZgeMkmpG9OM710RRzDFp+w4P1RGiIt30C1mSBT+ExCleeh4HObwgArnDFELmRrOpXgSYN9VF1hj+f1lw== - dependencies: - "@types/sizzle" "*" - "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -1688,11 +1674,6 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== -"@types/sizzle@*": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" - integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== - "@types/sockjs@^0.3.36": version "0.3.36" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" @@ -4272,10 +4253,10 @@ interpret@^3.1.1: resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -intl-tel-input@^17.0.19: - version "17.0.19" - resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-17.0.19.tgz#4c277e3bf02069fac2ef3821a62a3d7e8b55740a" - integrity sha512-GBNoUT4JVgm2e1N+yFMaBQ24g5EQfZhDznGneCM9IEZwfKsMUAUa1dS+v0wOiKpRAZ5IPNLJMIEEFGgqlCI22A== +intl-tel-input@^24.5.0: + version "24.5.0" + resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-24.5.0.tgz#1a7589f046b72a97f8cd30ebae4c0615635f542d" + integrity sha512-utSW+7a2JpUFdRRwo6CAiEQuAMwWxnCurPr2VDkfnW7W9g9ZprvPJPj00vxkiGV8h3GMRAD7aUtX9+oYwD/8OQ== ipaddr.js@1.9.1: version "1.9.1" From bbebeae1393081d53e2384a1bede705b05975e37 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 19 Sep 2024 10:18:27 -0400 Subject: [PATCH 2/5] Remove unnecessary screen reader text assignment Now handled internally in intl-tel-input with a11y-text element. Test coverage already exists here for expected value (see modification) --- app/javascript/packages/phone-input/index.spec.ts | 2 +- app/javascript/packages/phone-input/index.ts | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/app/javascript/packages/phone-input/index.spec.ts b/app/javascript/packages/phone-input/index.spec.ts index b43edf0e32c..a74a0eba6d6 100644 --- a/app/javascript/packages/phone-input/index.spec.ts +++ b/app/javascript/packages/phone-input/index.spec.ts @@ -208,7 +208,7 @@ describe('PhoneInput', () => { // See: https://github.com/jsdom/jsdom/issues/3323 expect(hasPopup).to.be.oneOf([null, 'listbox']); expect(name).to.equal('Country code'); - expect(value).to.equal('United States: +1'); + expect(value).to.equal('United States +1'); expect(controlled.contains(listbox)).to.be.true(); }); diff --git a/app/javascript/packages/phone-input/index.ts b/app/javascript/packages/phone-input/index.ts index 60712f83a50..ab2bd7c33d4 100644 --- a/app/javascript/packages/phone-input/index.ts +++ b/app/javascript/packages/phone-input/index.ts @@ -86,17 +86,6 @@ export class PhoneInputElement extends HTMLElement { return this.#strings; } - /** - * Returns the element which represents the flag dropdown's currently selected value, which is - * rendered as a text element within the combobox. As defined by the ARIA specification, a - * combobox's value is determined by its contents if it is not an input element. - * - * @see https://w3c.github.io/aria/#combobox - */ - get valueText(): HTMLElement { - return this.selectedCountry.querySelector('.iti__a11y-text')!; - } - get selectedCountry(): HTMLElement { return this.querySelector('.iti__selected-country')!; } @@ -122,9 +111,6 @@ export class PhoneInputElement extends HTMLElement { const countryCode = this.getSelectedCountryCode(); if (countryCode) { this.codeInput.value = countryCode; - // Move value text from title attribute to the flag's hidden text element. - // See: https://github.com/jackocnr/intl-tel-input/blob/d54b127/src/js/intlTelInput.js#L1191-L1197 - this.valueText.textContent = this.selectedCountry.title; this.selectedCountry.removeAttribute('title'); if (fireChangeEvent) { this.codeInput.dispatchEvent(new CustomEvent('change', { bubbles: true })); From 386a344437f22bac5c9cea0a60dd876d0d7338f9 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 19 Sep 2024 10:24:14 -0400 Subject: [PATCH 3/5] Remove unnecessary aria-owns removal Removed upstream in https://github.com/jackocnr/intl-tel-input/pull/1421 --- app/javascript/packages/phone-input/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/javascript/packages/phone-input/index.ts b/app/javascript/packages/phone-input/index.ts index ab2bd7c33d4..19b9b024c7c 100644 --- a/app/javascript/packages/phone-input/index.ts +++ b/app/javascript/packages/phone-input/index.ts @@ -149,7 +149,6 @@ export class PhoneInputElement extends HTMLElement { // Improve base accessibility of intl-tel-input this.selectedCountry.setAttribute('aria-haspopup', 'listbox'); this.selectedCountry.setAttribute('aria-label', this.strings.country_code_label); - this.selectedCountry.removeAttribute('aria-owns'); this.syncCountryToCodeInput({ fireChangeEvent: false }); From c7964639b6943e1edf499a8ece1b0f3509ab45ca Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 19 Sep 2024 10:28:48 -0400 Subject: [PATCH 4/5] Use i18n option to customize aria-label Available as of https://github.com/jackocnr/intl-tel-input/commit/fc0fc06f7a0ae0777d1cf199fd02093cc1972094 --- app/javascript/packages/phone-input/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/javascript/packages/phone-input/index.ts b/app/javascript/packages/phone-input/index.ts index 19b9b024c7c..ae9c1cedbad 100644 --- a/app/javascript/packages/phone-input/index.ts +++ b/app/javascript/packages/phone-input/index.ts @@ -124,7 +124,10 @@ export class PhoneInputElement extends HTMLElement { const iti = intlTelInput(this.textInput, { countryOrder: ['US', 'CA'], initialCountry: this.codeInput.value, - i18n: countryCodePairs, + i18n: { + ...countryCodePairs, + selectedCountryAriaLabel: this.strings.country_code_label, + }, onlyCountries: supportedCountryCodes, autoPlaceholder: 'off', formatAsYouType: false, @@ -148,7 +151,6 @@ export class PhoneInputElement extends HTMLElement { // Improve base accessibility of intl-tel-input this.selectedCountry.setAttribute('aria-haspopup', 'listbox'); - this.selectedCountry.setAttribute('aria-label', this.strings.country_code_label); this.syncCountryToCodeInput({ fireChangeEvent: false }); From a6b5d7c9d97ebeca23e4c25107ce926b1661a53b Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 19 Sep 2024 11:29:26 -0400 Subject: [PATCH 5/5] Remove unnecessary duplicate removal logic Latest version has "countryOrder" rather than "preferredCountries", no longer outputs duplicate iitems --- app/javascript/packages/phone-input/index.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/javascript/packages/phone-input/index.ts b/app/javascript/packages/phone-input/index.ts index ae9c1cedbad..118cbfae53d 100644 --- a/app/javascript/packages/phone-input/index.ts +++ b/app/javascript/packages/phone-input/index.ts @@ -136,19 +136,6 @@ export class PhoneInputElement extends HTMLElement { this.iti = iti; - // Remove duplicate items in the country list - const preferred: NodeListOf = - this.countryList!.querySelectorAll('.iti__preferred'); - preferred.forEach((listItem) => { - const { countryCode } = listItem.dataset; - const duplicates: NodeListOf = this.countryList!.querySelectorAll( - `.iti__standard[data-country-code="${countryCode}"]`, - ); - duplicates.forEach((duplicateListItem) => { - duplicateListItem.parentNode?.removeChild(duplicateListItem); - }); - }); - // Improve base accessibility of intl-tel-input this.selectedCountry.setAttribute('aria-haspopup', 'listbox');