Skip to content

Commit 391485b

Browse files
committed
increment or decrement value of number input when pressing arrow keys
1 parent 63ac399 commit 391485b

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

Diff for: src/event/behavior/keydown.ts

+8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ const keydownBehavior: {
2828
if (isElementType(target, 'input', {type: 'radio'} as const)) {
2929
return () => walkRadio(instance, target, 1)
3030
}
31+
32+
if (isElementType(target, 'input', {type: 'number'} as const)) {
33+
return () => input(instance, target, 'ArrowDown', 'changeNumberInput')
34+
}
3135
},
3236
ArrowLeft: (event, target, instance) => {
3337
if (isElementType(target, 'input', {type: 'radio'} as const)) {
@@ -46,6 +50,10 @@ const keydownBehavior: {
4650
if (isElementType(target, 'input', {type: 'radio'} as const)) {
4751
return () => walkRadio(instance, target, -1)
4852
}
53+
54+
if (isElementType(target, 'input', {type: 'number'} as const)) {
55+
return () => input(instance, target, 'ArrowUp', 'changeNumberInput')
56+
}
4957
},
5058
Backspace: (event, target, instance) => {
5159
if (isEditable(target)) {

Diff for: src/event/input.ts

+31
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
EditableInputOrTextarea,
1212
getMaxLength,
1313
getNextCursorPosition,
14+
isDisabled,
15+
isEditable,
1416
isElementType,
1517
isValidDateOrTimeValue,
1618
supportsMaxLength,
@@ -220,6 +222,35 @@ function calculateNewValue(
220222
}
221223
}
222224

225+
if (
226+
isElementType(node, 'input', {type: 'number'} as const) &&
227+
inputType === 'changeNumberInput' &&
228+
!isDisabled(node) &&
229+
!node.readOnly
230+
) {
231+
const step = node.step ? Number(node.step) : 1
232+
233+
const reachedMax = value === node.max
234+
if (inputData === 'ArrowUp' && !reachedMax) {
235+
const exceedsMax = Number(value) + step > Number(node.max)
236+
if (exceedsMax && !!node.max) {
237+
newValue = node.max
238+
} else {
239+
newValue = (Number(value) + step).toString()
240+
}
241+
}
242+
243+
const reachedMin = value === node.min
244+
if (inputData === 'ArrowDown' && !reachedMin) {
245+
const exceedsMin = Number(value) - step < Number(node.min)
246+
if (exceedsMin) {
247+
newValue = node.min
248+
} else {
249+
newValue = (Number(value) - step).toString()
250+
}
251+
}
252+
}
253+
223254
return {
224255
oldValue: value,
225256
newValue,

Diff for: tests/event/behavior/keydown.ts

+140
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,143 @@ cases(
387387
},
388388
},
389389
)
390+
391+
test("increment number input's value when pressing the arrow up key", () => {
392+
const {element} = render<HTMLInputElement>(`<input value="1" type="number"/>`)
393+
394+
const instance = setupInstance()
395+
396+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})
397+
398+
expect(element).toHaveValue(2)
399+
})
400+
401+
test("do not increment number input's value when pressing the arrow up key and it would go above the max value", () => {
402+
const {element} = render<HTMLInputElement>(
403+
`<input value="1" type="number" max="1"/>`,
404+
)
405+
406+
const instance = setupInstance()
407+
408+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})
409+
410+
expect(element).toHaveValue(1)
411+
})
412+
413+
test("decrement number input's value when pressing the arrow down key", () => {
414+
const {element} = render<HTMLInputElement>(`<input value="1" type="number"/>`)
415+
416+
const instance = setupInstance()
417+
418+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})
419+
420+
expect(element).toHaveValue(0)
421+
})
422+
423+
test("do not decrement number input's value when pressing the arrow down key and it would go below the min value", () => {
424+
const {element} = render<HTMLInputElement>(
425+
`<input value="1" type="number" min="1"/>`,
426+
)
427+
428+
const instance = setupInstance()
429+
430+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})
431+
432+
expect(element).toHaveValue(1)
433+
})
434+
435+
test("increments number input's value by the defined steps when pressing the arrow up key", () => {
436+
const {element} = render<HTMLInputElement>(
437+
`<input value="10" type="number" step="10"/>`,
438+
)
439+
440+
const instance = setupInstance()
441+
442+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})
443+
444+
expect(element).toHaveValue(20)
445+
})
446+
447+
test("decrements number input's value by the defined steps when pressing the arrow down key", () => {
448+
const {element} = render<HTMLInputElement>(
449+
`<input value="10" type="number" step="10"/>`,
450+
)
451+
452+
const instance = setupInstance()
453+
454+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})
455+
456+
expect(element).toHaveValue(0)
457+
})
458+
459+
test('decrements only to the min value when pressing the arrow down key and steps are too large', async () => {
460+
const {element} = render<HTMLInputElement>(
461+
`<input value="5" type="number" min="0" step="10"/>`,
462+
)
463+
464+
const instance = setupInstance()
465+
466+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})
467+
468+
expect(element).toHaveValue(0)
469+
})
470+
471+
test('increments only to the max value when pressing the arrow up key and steps are too large', async () => {
472+
const {element} = render<HTMLInputElement>(
473+
`<input value="5" type="number" max="10" step="10"/>`,
474+
)
475+
476+
const instance = setupInstance()
477+
478+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})
479+
480+
expect(element).toHaveValue(10)
481+
})
482+
483+
test("does not increment number input's value when pressing the arrow up key and the input is disabled", () => {
484+
const {element} = render<HTMLInputElement>(
485+
`<input value="1" type="number" disabled/>`,
486+
)
487+
488+
const instance = setupInstance()
489+
490+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})
491+
492+
expect(element).toHaveValue(1)
493+
})
494+
495+
test("does not decrement number input's value when pressing the arrow down key and the input is disabled", () => {
496+
const {element} = render<HTMLInputElement>(
497+
`<input value="1" type="number" disabled/>`,
498+
)
499+
500+
const instance = setupInstance()
501+
502+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})
503+
504+
expect(element).toHaveValue(1)
505+
})
506+
507+
test("does not increment number input's value when pressing the arrow up key and the input is readonly", () => {
508+
const {element} = render<HTMLInputElement>(
509+
`<input value="1" type="number" readonly/>`,
510+
)
511+
512+
const instance = setupInstance()
513+
514+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})
515+
516+
expect(element).toHaveValue(1)
517+
})
518+
519+
test("does not decrement number input's value when pressing the arrow down key and the input is readonly", () => {
520+
const {element} = render<HTMLInputElement>(
521+
`<input value="1" type="number" readonly/>`,
522+
)
523+
524+
const instance = setupInstance()
525+
526+
instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})
527+
528+
expect(element).toHaveValue(1)
529+
})

0 commit comments

Comments
 (0)