Skip to content

Commit d0b6244

Browse files
committed
fix(datetime): allow accurate typing of time values in 24-hour format
- Adjusted the `selectMultiColumn` logic to handle keyboard values like 20 and 22 dynamically. - Introduced checks for the maximum column value to enable flexible input behavior. - Added e2e tests to verify correct value selection for both 12-hour and 24-hour formats. Closes #28877
1 parent b71f2e9 commit d0b6244

File tree

2 files changed

+115
-11
lines changed

2 files changed

+115
-11
lines changed

core/src/components/picker/picker.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -432,21 +432,29 @@ export class Picker implements ComponentInterface {
432432
const firstColumn = numericPickers[0];
433433
const lastColumn = numericPickers[1];
434434

435+
// Get the maximum value from the first column's options
436+
const columnOptions = Array.from(firstColumn.querySelectorAll('ion-picker-column-option'));
437+
const maxFirstColumnValue = Math.max(
438+
...columnOptions
439+
.map((option) => parseInt(option.textContent?.trim() || '0', 10)) // Extract and parse text content
440+
.filter((num) => !isNaN(num)) // Ensure valid numbers only
441+
);
442+
443+
// check for 24-hour format
444+
const allowTwo = maxFirstColumnValue >= 20;
445+
435446
let value = inputEl.value;
436447
let minuteValue;
437448
switch (value.length) {
438449
case 1:
439450
this.searchColumn(firstColumn, value);
440451
break;
441452
case 2:
442-
/**
443-
* If the first character is `0` or `1` it is
444-
* possible that users are trying to type `09`
445-
* or `11` into the hour field, so we should look
446-
* at that first.
447-
*/
448453
const firstCharacter = inputEl.value.substring(0, 1);
449-
value = firstCharacter === '0' || firstCharacter === '1' ? inputEl.value : firstCharacter;
454+
value =
455+
firstCharacter === '0' || firstCharacter === '1' || (allowTwo && firstCharacter === '2')
456+
? inputEl.value
457+
: firstCharacter;
450458

451459
this.searchColumn(firstColumn, value);
452460

@@ -462,14 +470,14 @@ export class Picker implements ComponentInterface {
462470
break;
463471
case 3:
464472
/**
465-
* If the first character is `0` or `1` it is
473+
* If the first character is `0` or `1` or allowed '2' it is
466474
* possible that users are trying to type `09`
467475
* or `11` into the hour field, so we should look
468476
* at that first.
469477
*/
470478
const firstCharacterAgain = inputEl.value.substring(0, 1);
471479
value =
472-
firstCharacterAgain === '0' || firstCharacterAgain === '1'
480+
firstCharacterAgain === '0' || firstCharacterAgain === '1' || (allowTwo && firstCharacterAgain === '2')
473481
? inputEl.value.substring(0, 2)
474482
: firstCharacterAgain;
475483

@@ -486,14 +494,16 @@ export class Picker implements ComponentInterface {
486494
break;
487495
case 4:
488496
/**
489-
* If the first character is `0` or `1` it is
497+
* If the first character is `0` or `1` or allowed '2' it is
490498
* possible that users are trying to type `09`
491499
* or `11` into the hour field, so we should look
492500
* at that first.
493501
*/
494502
const firstCharacterAgainAgain = inputEl.value.substring(0, 1);
495503
value =
496-
firstCharacterAgainAgain === '0' || firstCharacterAgainAgain === '1'
504+
firstCharacterAgainAgain === '0' ||
505+
firstCharacterAgainAgain === '1' ||
506+
(allowTwo && firstCharacterAgainAgain === '2')
497507
? inputEl.value.substring(0, 2)
498508
: firstCharacterAgainAgain;
499509
this.searchColumn(firstColumn, value);

core/src/components/picker/test/keyboard-entry/picker.e2e.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,100 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
163163
await expect(ionChange).toHaveReceivedEventDetail({ value: 12 });
164164
await expect(column).toHaveJSProperty('value', 12);
165165
});
166+
167+
test('should allow typing 22 in a column where the max value is 23 and not just set it to 2', async ({ page }) => {
168+
await page.setContent(
169+
`
170+
<ion-picker>
171+
<ion-picker-column></ion-picker-column>
172+
</ion-picker>
173+
174+
<script>
175+
const column = document.querySelector('ion-picker-column');
176+
column.numericInput = true;
177+
const items = [
178+
{ text: '01', value: 1 },
179+
{ text: '02', value: 2},
180+
{ text: '20', value: 20 },
181+
{ text: '21', value: 21 },
182+
{ text: '22', value: 22 },
183+
{ text: '23', value: 23 }
184+
];
185+
186+
items.forEach((item) => {
187+
const option = document.createElement('ion-picker-column-option');
188+
option.value = item.value;
189+
option.textContent = item.text;
190+
191+
column.appendChild(option);
192+
});
193+
</script>
194+
`,
195+
config
196+
);
197+
198+
const column = page.locator('ion-picker-column');
199+
const ionChange = await page.spyOnEvent('ionChange');
200+
await column.evaluate((el: HTMLIonPickerColumnElement) => el.setFocus());
201+
202+
// Simulate typing '22'
203+
await page.keyboard.press('Digit2');
204+
await page.keyboard.press('Digit2');
205+
206+
// Ensure the column value is updated to 22
207+
await expect(ionChange).toHaveReceivedEventDetail({ value: 22 });
208+
await expect(column).toHaveJSProperty('value', 22);
209+
});
210+
211+
test('should set value to 2 and not wait for another digit when max value is 12', async ({ page }) => {
212+
await page.setContent(
213+
`
214+
<ion-picker>
215+
<ion-picker-column></ion-picker-column>
216+
</ion-picker>
217+
218+
<script>
219+
const column = document.querySelector('ion-picker-column');
220+
column.numericInput = true;
221+
const items = [
222+
{ text: '01', value: 1 },
223+
{ text: '02', value: 2 },
224+
{ text: '03', value: 3 },
225+
{ text: '04', value: 4 },
226+
{ text: '05', value: 5 },
227+
{ text: '06', value: 6 },
228+
{ text: '07', value: 7 },
229+
{ text: '08', value: 8 },
230+
{ text: '09', value: 9 },
231+
{ text: '10', value: 10 },
232+
{ text: '11', value: 11 },
233+
{ text: '12', value: 12 }
234+
];
235+
236+
items.forEach((item) => {
237+
const option = document.createElement('ion-picker-column-option');
238+
option.value = item.value;
239+
option.textContent = item.text;
240+
241+
column.appendChild(option);
242+
});
243+
</script>
244+
`,
245+
config
246+
);
247+
248+
const column = page.locator('ion-picker-column');
249+
const ionChange = await page.spyOnEvent('ionChange');
250+
await column.evaluate((el: HTMLIonPickerColumnElement) => el.setFocus());
251+
252+
// Simulate typing '2'
253+
await page.keyboard.press('Digit2');
254+
255+
// Ensure the value is immediately set to 2
256+
await expect(ionChange).toHaveReceivedEventDetail({ value: 2 });
257+
await expect(column).toHaveJSProperty('value', 2);
258+
});
259+
166260
test('pressing Enter should dismiss the keyboard', async ({ page }) => {
167261
test.info().annotations.push({
168262
type: 'issue',

0 commit comments

Comments
 (0)