Skip to content

Commit e31c3ad

Browse files
committed
fix(input-otp): correctly handle autofill by splitting the values into all inputs
1 parent 4d6a067 commit e31c3ad

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

core/src/components/input-otp/input-otp.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,21 @@ export class InputOTP implements ComponentInterface {
619619

620620
const value = (event.target as HTMLInputElement).value;
621621

622+
// If the value is longer than 1 character, it's likely from
623+
// autofill, so we need to split the value up
624+
if (value.length > 1) {
625+
const chars = value.split('').slice(0, this.length);
626+
chars.forEach((char, index) => {
627+
if (this.validKeyPattern.test(char)) {
628+
this.inputRefs[index].value = char;
629+
this.inputValues[index] = char;
630+
}
631+
});
632+
this.value = chars.join('');
633+
this.updateValue(event);
634+
return;
635+
}
636+
622637
// Only allow input if it's a single character and matches the pattern
623638
if (value.length > 1 || (value.length > 0 && !validKeyPattern.test(value))) {
624639
// Reset the input value if not valid
@@ -760,7 +775,7 @@ export class InputOTP implements ComponentInterface {
760775
readOnly={readonly}
761776
tabIndex={index === tabbableIndex ? 0 : -1}
762777
value={inputValues[index] || ''}
763-
autocomplete={index === 0 ? 'one-time-code' : 'off'}
778+
autocomplete="one-time-code"
764779
ref={(el) => (inputRefs[index] = el as HTMLInputElement)}
765780
onInput={this.onInput(index)}
766781
onBlur={this.onBlur}

core/src/components/input-otp/test/basic/input-otp.e2e.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,60 @@ configs({ modes: ['ios'] }).forEach(({ title, config }) => {
429429

430430
await verifyInputValues(inputOtp, ['1', '9', '3', '']);
431431
});
432+
433+
test('should handle autofill correctly', async ({ page }) => {
434+
await page.setContent(`<ion-input-otp>Description</ion-input-otp>`, config);
435+
436+
const firstInput = page.locator('ion-input-otp input').first();
437+
438+
// Set the value in the 1st input directly and trigger input event
439+
// this simulates the value being set by autofill
440+
await firstInput.evaluate((input) => {
441+
(input as HTMLInputElement).value = '1234';
442+
input.dispatchEvent(new Event('input', { bubbles: true }));
443+
});
444+
445+
const inputOtp = page.locator('ion-input-otp');
446+
await verifyInputValues(inputOtp, ['1', '2', '3', '4']);
447+
});
448+
449+
test('should handle autofill correctly when it exceeds the length', async ({ page }) => {
450+
await page.setContent(`<ion-input-otp>Description</ion-input-otp>`, config);
451+
452+
const firstInput = page.locator('ion-input-otp input').first();
453+
454+
// Set the value in the 1st input directly and trigger input event
455+
// this simulates the value being set by autofill
456+
await firstInput.evaluate((input) => {
457+
(input as HTMLInputElement).value = '123456';
458+
input.dispatchEvent(new Event('input', { bubbles: true }));
459+
});
460+
461+
const inputOtp = page.locator('ion-input-otp');
462+
await verifyInputValues(inputOtp, ['1', '2', '3', '4']);
463+
});
464+
465+
test('should handle autofill correctly when using autofill after typing 1 character', async ({ page }) => {
466+
await page.setContent(`<ion-input-otp>Description</ion-input-otp>`, config);
467+
468+
const firstInput = page.locator('ion-input-otp input').first();
469+
await firstInput.focus();
470+
471+
await page.keyboard.type('9');
472+
473+
const secondInput = page.locator('ion-input-otp input').nth(1);
474+
await secondInput.focus();
475+
476+
// Set the value in the 2nd input directly and trigger input event
477+
// this simulates the value being set by autofill from the 2nd input
478+
await secondInput.evaluate((input) => {
479+
(input as HTMLInputElement).value = '1234';
480+
input.dispatchEvent(new Event('input', { bubbles: true }));
481+
});
482+
483+
const inputOtp = page.locator('ion-input-otp');
484+
await verifyInputValues(inputOtp, ['1', '2', '3', '4']);
485+
});
432486
});
433487

434488
test.describe(title('input-otp: focus functionality'), () => {

0 commit comments

Comments
 (0)