Skip to content

Commit 97649a0

Browse files
committed
fix(input-otp): respect case when checking pattern
1 parent 46a3fb7 commit 97649a0

File tree

3 files changed

+44
-21
lines changed

3 files changed

+44
-21
lines changed

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class InputOTP implements ComponentInterface {
127127
if (this.value != null && String(this.value).length > 0) {
128128
const chars = String(this.value).split('').slice(0, this.length);
129129
chars.forEach((char, index) => {
130-
if (this.validKeys.test(char.toLowerCase())) {
130+
if (this.validKeys.test(char)) {
131131
this.inputValues[index] = char;
132132
}
133133
});
@@ -137,20 +137,23 @@ export class InputOTP implements ComponentInterface {
137137
}
138138

139139
/**
140-
* Get the default allowed keys based on type if not explicitly set
140+
* Get the regex pattern for allowed characters
141+
* If a pattern is provided, use it to create a regex pattern
142+
* Otherwise, use the default regex pattern based on type
141143
*/
142144
private get validKeys(): RegExp {
143145
const { pattern, type } = this;
144146

145147
if (pattern) {
146-
// Create a regex that matches a single character from the provided pattern
147-
return new RegExp(`^${pattern}$`, 'i');
148+
return new RegExp(`^${pattern}$`);
148149
}
149150
return type === 'number' ? /^[0-9]$/ : /^[a-zA-Z0-9]$/i;
150151
}
151152

152153
/**
153-
* Get the default value for inputmode based on type if not explicitly set
154+
* Get the default value for inputmode
155+
* If inputmode is provided, use it
156+
* Otherwise, use the default inputmode based on type
154157
*/
155158
private getInputmode(): string {
156159
const { inputmode } = this;
@@ -183,7 +186,7 @@ export class InputOTP implements ComponentInterface {
183186
const { validKeys } = this;
184187

185188
// Only allow input if it's a single character and matches the pattern
186-
if (value.length > 1 || (value.length > 0 && !validKeys.test(value.toLowerCase()))) {
189+
if (value.length > 1 || (value.length > 0 && !validKeys.test(value))) {
187190
// Reset the input value if not valid
188191
this.inputRefs[index].value = '';
189192
this.inputValues[index] = '';
@@ -287,7 +290,7 @@ export class InputOTP implements ComponentInterface {
287290

288291
const validChars = pastedText
289292
.split('')
290-
.filter((char) => validKeys.test(char.toLowerCase()))
293+
.filter((char) => validKeys.test(char))
291294
.slice(0, length);
292295

293296
// Find the first empty input

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@ <h2>Default</h2>
4949

5050
<div class="grid-item">
5151
<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>
52+
<ion-input-otp class="input-otp-type"> Numbers only <span id="value"></span> </ion-input-otp>
53+
<ion-input-otp class="input-otp-type" type="text"> Letters and numbers <span id="value"></span> </ion-input-otp>
54+
<ion-input-otp class="input-otp-type" type="text" pattern="[a-fA-F]"> Custom Pattern: a-f and A-F <span id="value"></span> </ion-input-otp>
55+
<ion-input-otp class="input-otp-type" type="text" pattern="[D-L]"> Custom Pattern: D-L <span id="value"></span> </ion-input-otp>
5556
</div>
5657

5758
<div class="grid-item">
@@ -76,16 +77,15 @@ <h2>Invalid</h2>
7677
const numberValue = document.getElementById('numberValue');
7778
numberValue.value = 123;
7879

80+
const inputOtpTypes = document.querySelectorAll('.input-otp-type');
81+
7982
// 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+
inputOtpTypes.forEach((inputOtpType) => {
84+
inputOtpType.addEventListener('ionChange', (event) => {
8385
const displayValue = event.detail.value != '' ? `(value: ${event.detail.value})` : '';
84-
el.querySelector('#value').textContent = displayValue;
86+
inputOtpType.querySelector('#value').textContent = displayValue;
8587
});
86-
}
87-
88-
['numberType', 'letterAndNumberType', 'customType'].forEach(addValueListener);
88+
});
8989
</script>
9090
</ion-app>
9191
</body>

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
8888
await expect(inputBoxes.nth(3)).toHaveValue('5');
8989
});
9090

91-
test('should accept custom pattern when pattern is set', async ({ page }) => {
91+
test('should accept custom pattern of lowercase and uppercase letters when pattern is set', async ({ page }) => {
9292
await page.setContent(
9393
`
9494
<ion-input-otp type="text" pattern="[a-fA-F]">Description</ion-input-otp>
@@ -99,14 +99,34 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
9999
const firstInput = page.locator('ion-input-otp input').first();
100100
await firstInput.focus();
101101

102-
await page.keyboard.type('AGBZFD');
102+
await page.keyboard.type('aGBZfD');
103103

104104
const inputBoxes = page.locator('ion-input-otp input');
105-
await expect(inputBoxes.nth(0)).toHaveValue('A');
105+
await expect(inputBoxes.nth(0)).toHaveValue('a');
106106
await expect(inputBoxes.nth(1)).toHaveValue('B');
107-
await expect(inputBoxes.nth(2)).toHaveValue('F');
107+
await expect(inputBoxes.nth(2)).toHaveValue('f');
108108
await expect(inputBoxes.nth(3)).toHaveValue('D');
109109
});
110+
111+
test('should accept custom pattern of uppercase letters only when pattern is set', async ({ page }) => {
112+
await page.setContent(
113+
`
114+
<ion-input-otp type="text" pattern="[D-L]">Description</ion-input-otp>
115+
`,
116+
config
117+
);
118+
119+
const firstInput = page.locator('ion-input-otp input').first();
120+
await firstInput.focus();
121+
122+
await page.keyboard.type('abcdABCDEFG');
123+
124+
const inputBoxes = page.locator('ion-input-otp input');
125+
await expect(inputBoxes.nth(0)).toHaveValue('D');
126+
await expect(inputBoxes.nth(1)).toHaveValue('E');
127+
await expect(inputBoxes.nth(2)).toHaveValue('F');
128+
await expect(inputBoxes.nth(3)).toHaveValue('G');
129+
});
110130
});
111131

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

0 commit comments

Comments
 (0)