From a98e157b2cdcd4a62ec6e812072ee09647a44082 Mon Sep 17 00:00:00 2001 From: Maximilien B Date: Wed, 26 Feb 2025 10:39:21 +0100 Subject: [PATCH] Updated: PhoneNumberInput component now handles paste events --- addon/components/o-s-s/currency-input.ts | 2 +- addon/components/o-s-s/phone-number-input.hbs | 1 + addon/components/o-s-s/phone-number-input.ts | 34 ++++++++--- .../o-s-s/phone-number-input-test.ts | 56 ++++++++++++++++++- 4 files changed, 83 insertions(+), 10 deletions(-) diff --git a/addon/components/o-s-s/currency-input.ts b/addon/components/o-s-s/currency-input.ts index 48ad9d673..7f0c9acd3 100644 --- a/addon/components/o-s-s/currency-input.ts +++ b/addon/components/o-s-s/currency-input.ts @@ -22,7 +22,7 @@ interface OSSCurrencyInputArgs { } const NUMERIC_ONLY = /^\d$/i; -const NOT_NUMERIC_FLOAT = /[^0-9,.]/g; +export const NOT_NUMERIC_FLOAT = /[^\d,.]/g; export const PLATFORM_CURRENCIES: Currency[] = [ { code: 'USD', symbol: '$' }, { code: 'EUR', symbol: '€' }, diff --git a/addon/components/o-s-s/phone-number-input.hbs b/addon/components/o-s-s/phone-number-input.hbs index 2a8d0a160..1686bb411 100644 --- a/addon/components/o-s-s/phone-number-input.hbs +++ b/addon/components/o-s-s/phone-number-input.hbs @@ -13,6 +13,7 @@ name="telephone" placeholder={{this.placeholder}} {{on "keydown" this.onlyNumeric}} + {{on "paste" this.handlePaste}} {{on "blur" this.onlyNumeric}} {{did-insert this.registerInputElement}} /> diff --git a/addon/components/o-s-s/phone-number-input.ts b/addon/components/o-s-s/phone-number-input.ts index 92e47dc76..45d03b805 100644 --- a/addon/components/o-s-s/phone-number-input.ts +++ b/addon/components/o-s-s/phone-number-input.ts @@ -6,6 +6,7 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { countries, type CountryData } from '@upfluence/oss-components/utils/country-codes'; import type IntlService from 'ember-intl/services/intl'; +import { NOT_NUMERIC_FLOAT } from './currency-input'; interface OSSPhoneNumberInputArgs { prefix: string; @@ -17,6 +18,9 @@ interface OSSPhoneNumberInputArgs { validates?(isPassing: boolean): void; } +const AUTHORIZED_KEYS = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab', 'Shift']; +const AUTHORIZED_COMBO_KEYS = ['v', 'a', 'z', 'c', 'x']; + export default class OSSPhoneNumberInput extends Component { @service declare intl: IntlService; @@ -67,13 +71,14 @@ export default class OSSPhoneNumberInput extends Component key === event.key) - ) { + const isAuthorizedKey = AUTHORIZED_KEYS.find((key: string) => key === (event as KeyboardEvent).key); + const isSupportedCombo = + event instanceof KeyboardEvent && + ((event as KeyboardEvent).metaKey || + ((navigator as any).userAgentData?.platform === 'Windows' && event.ctrlKey)) && + AUTHORIZED_COMBO_KEYS.includes(event.key); + + if (event instanceof FocusEvent || /^[0-9]$/i.test(event.key) || isSupportedCombo || isAuthorizedKey) { this.args.onChange('+' + this.selectedCountry.countryCallingCodes[0], this.args.number); } else { event.preventDefault(); @@ -82,6 +87,21 @@ export default class OSSPhoneNumberInput extends Component { diff --git a/tests/integration/components/o-s-s/phone-number-input-test.ts b/tests/integration/components/o-s-s/phone-number-input-test.ts index 044cd44cd..814ba7ce9 100644 --- a/tests/integration/components/o-s-s/phone-number-input-test.ts +++ b/tests/integration/components/o-s-s/phone-number-input-test.ts @@ -1,14 +1,14 @@ import { hbs } from 'ember-cli-htmlbars'; import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render, setupOnerror, triggerKeyEvent } from '@ember/test-helpers'; +import { render, setupOnerror, triggerEvent, triggerKeyEvent } from '@ember/test-helpers'; import click from '@ember/test-helpers/dom/click'; import sinon from 'sinon'; import findAll from '@ember/test-helpers/dom/find-all'; import typeIn from '@ember/test-helpers/dom/type-in'; import settled from '@ember/test-helpers/settled'; -module('Integration | Component | o-s-s/phone-number', function (hooks) { +module('Integration | Component | o-s-s/phone-number-input', function (hooks) { setupRenderingTest(hooks); hooks.beforeEach(function () { @@ -115,6 +115,58 @@ module('Integration | Component | o-s-s/phone-number', function (hooks) { }); }); + module('When the paste event is received', function (hooks) { + hooks.beforeEach(function () { + this.onChange = () => {}; + this.onValidation = sinon.spy(); + this.number = '1234567890'; + }); + + test('The value stored in the clipboard is inserted in the input', async function (assert) { + await render( + hbs`` + ); + assert.dom('input').hasValue('1234567890'); + await triggerEvent('input', 'paste', { + clipboardData: { + getData: sinon.stub().returns('123') + } + }); + + assert.dom('input').hasValue('1234567890123'); + }); + + test('The non-numeric characters are escaped', async function (assert) { + await render( + hbs`` + ); + assert.dom('input').hasValue('1234567890'); + await triggerEvent('input', 'paste', { + clipboardData: { + getData: sinon.stub().returns('1withletter0') + } + }); + + assert.dom('input').hasValue('123456789010'); + }); + + test('When selection is applied, it replaces the selection', async function (assert) { + await render( + hbs`` + ); + assert.dom('input').hasValue('1234567890'); + let input = document.querySelector('input.ember-text-field') as HTMLInputElement; + input.setSelectionRange(4, 6); + await triggerEvent('input', 'paste', { + clipboardData: { + getData: sinon.stub().returns('0') + } + }); + + assert.dom('input').hasValue('123407890'); + }); + }); + module('@hasError parameter', () => { test('A red border is displayed if the parameter is true', async function (assert) { await render(hbs`