Skip to content

Commit bb7065b

Browse files
committed
fix(input-otp): always start value at first empty input when typing
1 parent 6175dfb commit bb7065b

File tree

3 files changed

+101
-11
lines changed

3 files changed

+101
-11
lines changed

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,25 @@ export class InputOTP implements ComponentInterface {
190190
return;
191191
}
192192

193-
this.inputValues[index] = value;
193+
// Find the first empty box before or at the current index
194+
let targetIndex = index;
195+
for (let i = 0; i < index; i++) {
196+
if (!this.inputValues[i] || this.inputValues[i] === '') {
197+
targetIndex = i;
198+
break;
199+
}
200+
}
201+
202+
// Set the value at the target index
203+
this.inputValues[targetIndex] = value;
204+
205+
// If the value was entered in a later box, clear the current box
206+
if (targetIndex !== index) {
207+
this.inputRefs[index].value = '';
208+
}
194209

195210
if (value.length > 0) {
196-
this.focusNext(index);
211+
this.focusNext(targetIndex);
197212
}
198213
this.updateValue();
199214
}

core/src/components/input-otp/test/basic/index.html

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,18 @@ <h2>Default</h2>
4747
<ion-input-otp value="5893" length="8"> Didn't get a code? <a href="#">Resend the code</a> </ion-input-otp>
4848
</div>
4949

50+
<div class="grid-item">
51+
<h2>Types</h2>
52+
<ion-input-otp id="numberType"> Numbers only <span id="value"></span> </ion-input-otp>
53+
<ion-input-otp id="letterAndNumberType" type="text"> Letters and numbers <span id="value"></span> </ion-input-otp>
54+
<ion-input-otp id="customType" type="text" pattern="[a-fA-F]"> Custom Pattern: a-f and A-F <span id="value"></span> </ion-input-otp>
55+
</div>
56+
5057
<div class="grid-item">
5158
<h2>Disabled</h2>
5259
<ion-input-otp value="1234" disabled>Description</ion-input-otp>
5360
<ion-input-otp value="1234" fill="solid" disabled>Description</ion-input-otp>
54-
</div>
5561

56-
<div class="grid-item">
5762
<h2>Readonly</h2>
5863
<ion-input-otp value="1234" readonly>Description</ion-input-otp>
5964
<ion-input-otp value="1234" fill="solid" readonly>Description</ion-input-otp>
@@ -64,19 +69,23 @@ <h2>Invalid</h2>
6469
<ion-input-otp class="ion-invalid">Description</ion-input-otp>
6570
<ion-input-otp fill="solid" class="ion-invalid">Description</ion-input-otp>
6671
</div>
67-
68-
<div class="grid-item">
69-
<h2>Types</h2>
70-
<ion-input-otp length="6"> Numbers only </ion-input-otp>
71-
<ion-input-otp length="6" type="text"> Letters and numbers </ion-input-otp>
72-
<ion-input-otp type="text" pattern="[a-fA-F]"> Custom Pattern: a-f and A-F </ion-input-otp>
73-
</div>
7472
</div>
7573
</ion-content>
7674

7775
<script>
7876
const numberValue = document.getElementById('numberValue');
7977
numberValue.value = 123;
78+
79+
// Display value under the different input types on ionChange
80+
function addValueListener(id) {
81+
const el = document.getElementById(id);
82+
el.addEventListener('ionChange', (event) => {
83+
const displayValue = event.detail.value != '' ? `(value: ${event.detail.value})` : '';
84+
el.querySelector('#value').textContent = displayValue;
85+
});
86+
}
87+
88+
['numberType', 'letterAndNumberType', 'customType'].forEach(addValueListener);
8089
</script>
8190
</ion-app>
8291
</body>

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,72 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
109109
});
110110
});
111111

112+
test.describe(title('input-otp: input functionality'), () => {
113+
test('should update the input value when typing 4 digits from the 1st box', async ({ page }) => {
114+
await page.setContent(
115+
`
116+
<ion-input-otp>Description</ion-input-otp>
117+
`,
118+
config
119+
);
120+
121+
const firstInput = page.locator('ion-input-otp input').first();
122+
await firstInput.focus();
123+
124+
const inputOtp = page.locator('ion-input-otp');
125+
const inputBoxes = page.locator('ion-input-otp input');
126+
127+
await expect(inputOtp).toHaveJSProperty('value', '');
128+
await expect(inputBoxes.nth(0)).toHaveValue('');
129+
await expect(inputBoxes.nth(1)).toHaveValue('');
130+
await expect(inputBoxes.nth(2)).toHaveValue('');
131+
await expect(inputBoxes.nth(3)).toHaveValue('');
132+
133+
await page.keyboard.type('12');
134+
135+
await expect(inputOtp).toHaveJSProperty('value', '12');
136+
await expect(inputBoxes.nth(0)).toHaveValue('1');
137+
await expect(inputBoxes.nth(1)).toHaveValue('2');
138+
await expect(inputBoxes.nth(2)).toHaveValue('');
139+
await expect(inputBoxes.nth(3)).toHaveValue('');
140+
141+
await page.keyboard.type('34');
142+
143+
await expect(inputOtp).toHaveJSProperty('value', '1234');
144+
await expect(inputBoxes.nth(0)).toHaveValue('1');
145+
await expect(inputBoxes.nth(1)).toHaveValue('2');
146+
await expect(inputBoxes.nth(2)).toHaveValue('3');
147+
await expect(inputBoxes.nth(3)).toHaveValue('4');
148+
});
149+
150+
test('should update the 1st input value when typing in the 3rd box', async ({ page }) => {
151+
await page.setContent(
152+
`
153+
<ion-input-otp>Description</ion-input-otp>
154+
`,
155+
config
156+
);
157+
158+
const thirdInput = page.locator('ion-input-otp input').nth(2);
159+
await thirdInput.focus();
160+
161+
const inputOtp = page.locator('ion-input-otp');
162+
const inputBoxes = page.locator('ion-input-otp input');
163+
164+
await page.keyboard.type('1');
165+
166+
await expect(inputBoxes.nth(0)).toHaveValue('1');
167+
await expect(inputBoxes.nth(1)).toHaveValue('');
168+
await expect(inputBoxes.nth(2)).toHaveValue('');
169+
await expect(inputBoxes.nth(3)).toHaveValue('');
170+
await expect(inputOtp).toHaveJSProperty('value', '1');
171+
172+
// Focus should be on the 2nd input box
173+
const secondInput = page.locator('ion-input-otp input').nth(1);
174+
await expect(secondInput).toBeFocused();
175+
});
176+
});
177+
112178
test.describe(title('input-otp: focus functionality'), () => {
113179
test('should focus the first input box when tabbed to', async ({ page }) => {
114180
await page.setContent(

0 commit comments

Comments
 (0)