Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change value of number input when pressing arrow up or down #1272

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/event/behavior/keydown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const keydownBehavior: {
if (isElementType(target, 'input', {type: 'radio'} as const)) {
return () => walkRadio(instance, target, 1)
}

if (isElementType(target, 'input', {type: 'number'} as const)) {
return () => input(instance, target, 'ArrowDown', 'changeNumberInput')
}
},
ArrowLeft: (event, target, instance) => {
if (isElementType(target, 'input', {type: 'radio'} as const)) {
Expand All @@ -46,6 +50,10 @@ const keydownBehavior: {
if (isElementType(target, 'input', {type: 'radio'} as const)) {
return () => walkRadio(instance, target, -1)
}

if (isElementType(target, 'input', {type: 'number'} as const)) {
return () => input(instance, target, 'ArrowUp', 'changeNumberInput')
}
},
Backspace: (event, target, instance) => {
if (isEditable(target)) {
Expand Down
31 changes: 31 additions & 0 deletions src/event/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
EditableInputOrTextarea,
getMaxLength,
getNextCursorPosition,
isDisabled,
isEditable,
isElementType,
isValidDateOrTimeValue,
supportsMaxLength,
Expand Down Expand Up @@ -220,6 +222,35 @@ function calculateNewValue(
}
}

if (
isElementType(node, 'input', {type: 'number'} as const) &&
inputType === 'changeNumberInput' &&
!isDisabled(node) &&
!node.readOnly
) {
const step = node.step ? Number(node.step) : 1

const reachedMax = value === node.max
if (inputData === 'ArrowUp' && !reachedMax) {
const exceedsMax = Number(value) + step > Number(node.max)
if (exceedsMax && !!node.max) {
newValue = node.max
} else {
newValue = (Number(value) + step).toString()
}
}

const reachedMin = value === node.min
if (inputData === 'ArrowDown' && !reachedMin) {
const exceedsMin = Number(value) - step < Number(node.min)
if (exceedsMin && !!node.min) {
newValue = node.min
} else {
newValue = (Number(value) - step).toString()
}
}
}

return {
oldValue: value,
newValue,
Expand Down
150 changes: 150 additions & 0 deletions tests/event/behavior/keydown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,153 @@ cases(
},
},
)

test("increment number input's value when pressing the arrow up key", () => {
const {element} = render<HTMLInputElement>(`<input value="1" type="number"/>`)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})

expect(element).toHaveValue(2)
})

test("do not increment number input's value when pressing the arrow up key and it would go above the max value", () => {
const {element} = render<HTMLInputElement>(
`<input value="1" type="number" max="1"/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})

expect(element).toHaveValue(1)
})

test("decrement number input's value when pressing the arrow down key", () => {
const {element} = render<HTMLInputElement>(`<input value="1" type="number"/>`)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(0)
})

test("do not decrement number input's value when pressing the arrow down key and it would go below the min value", () => {
const {element} = render<HTMLInputElement>(
`<input value="1" type="number" min="1"/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(1)
})

test("increments number input's value by the defined steps when pressing the arrow up key", () => {
const {element} = render<HTMLInputElement>(
`<input value="10" type="number" step="10"/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})

expect(element).toHaveValue(20)
})

test("decrements number input's value by the defined steps when pressing the arrow down key", () => {
const {element} = render<HTMLInputElement>(
`<input value="10" type="number" step="10"/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(0)
})

test('decrements only to the min value when pressing the arrow down key and steps are too large', async () => {
const {element} = render<HTMLInputElement>(
`<input value="5" type="number" min="0" step="10"/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(0)
})

test('increments only to the max value when pressing the arrow up key and steps are too large', async () => {
const {element} = render<HTMLInputElement>(
`<input value="5" type="number" max="10" step="10"/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})

expect(element).toHaveValue(10)
})

test("does not increment number input's value when pressing the arrow up key and the input is disabled", () => {
const {element} = render<HTMLInputElement>(
`<input value="1" type="number" disabled/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})

expect(element).toHaveValue(1)
})

test("does not decrement number input's value when pressing the arrow down key and the input is disabled", () => {
const {element} = render<HTMLInputElement>(
`<input value="1" type="number" disabled/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(1)
})

test("does not increment number input's value when pressing the arrow up key and the input is readonly", () => {
const {element} = render<HTMLInputElement>(
`<input value="1" type="number" readonly/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowUp'})

expect(element).toHaveValue(1)
})

test("does not decrement number input's value when pressing the arrow down key and the input is readonly", () => {
const {element} = render<HTMLInputElement>(
`<input value="1" type="number" readonly/>`,
)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(1)
})

test('decrements to a negative value when pressing the arrow down key', () => {
const {element} = render<HTMLInputElement>(`<input value="0" type="number"/>`)

const instance = setupInstance()

instance.dispatchUIEvent(element, 'keydown', {key: 'ArrowDown'})

expect(element).toHaveValue(-1)
})