Skip to content

Commit

Permalink
Merge pull request #3295 from defencedigital/feat/add-keyboard-contro…
Browse files Browse the repository at this point in the history
…l-numberinput

feat(NumberInput): Add ability to increment decrement with keyboard
  • Loading branch information
m7kvqbe1 authored Jun 7, 2022
2 parents 8d16c3e + 7778dda commit 08bb001
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ComponentSizeType } from '../Forms'
import { StyledInput } from '../TextInput/partials/StyledInput'
import { StyledInputWrapper } from './partials/StyledInputWrapper'
import { StyledLabel } from '../TextInput/partials/StyledLabel'
import { useInputKeys } from './useInputKeys'

export interface InputProps {
hasFocus: boolean
Expand All @@ -14,11 +15,16 @@ export interface InputProps {
name: string
onBeforeInput: (event: React.FormEvent<HTMLInputElement>) => void
onBlur: (event: React.FormEvent<HTMLInputElement>) => void
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
onChange: (
event:
| React.ChangeEvent<HTMLInputElement>
| React.KeyboardEvent<HTMLInputElement>
) => void
onFocus: (event: React.FormEvent<HTMLInputElement>) => void
onPaste: (event: React.ClipboardEvent<HTMLInputElement>) => void
placeholder?: string
size: ComponentSizeType
step: number
value?: string | null
}

Expand All @@ -28,10 +34,13 @@ export const Input: React.FC<InputProps> = ({
id,
label,
size,
step,
value,
onChange,
...rest
}) => {
const hasLabel = !!(label && label.length)
const { handleKeyDown } = useInputKeys(step, onChange)

return (
<StyledInputWrapper>
Expand All @@ -53,6 +62,8 @@ export const Input: React.FC<InputProps> = ({
disabled={isDisabled}
id={id}
value={value || ''}
onKeyDown={handleKeyDown}
onChange={onChange}
{...rest}
/>
</StyledInputWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import { NumberInput } from '.'
const defaultProps = {
name: 'number-input',
onChange: (
_: React.ChangeEvent<HTMLInputElement> | React.MouseEvent<HTMLButtonElement>
_:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLInputElement>
) => true,
}

Expand Down Expand Up @@ -223,6 +226,24 @@ describe('NumberInput', () => {

assertInputValue('123')
assertOnChangeCall(123, 3)

describe('and the user presses the keyboard up arrow', () => {
beforeEach(async () => {
await userEvent.type(input, '{arrowup}')
})

assertInputValue('124')
assertOnChangeCall(124, 4)
})

describe('and the user presses the keyboard down arrow', () => {
beforeEach(async () => {
await userEvent.type(input, '{arrowdown}')
})

assertInputValue('122')
assertOnChangeCall(122, 4)
})
})

describe('and the user types a value with invalid characters', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ interface NumberInputBaseProps
onChange: (
event:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLButtonElement>,
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLInputElement>,
newValue: number | null
) => void
/**
Expand Down Expand Up @@ -243,6 +244,7 @@ export const NumberInput: React.FC<NumberInputProps> = ({
onFocus={onLocalFocus}
placeholder={placeholder}
size={size}
step={step}
value={committedValue}
{...rest}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export function useChangeHandlers(
onChange: (
event:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLButtonElement>,
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLInputElement>,
newValue: number | null
) => void,
setCommittedValue: React.Dispatch<React.SetStateAction<string | null>>
Expand All @@ -19,10 +20,18 @@ export function useChangeHandlers(
event: React.MouseEvent<HTMLButtonElement>,
newValue: string
) => void
handleInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void
handleInputChange: (
event:
| React.ChangeEvent<HTMLInputElement>
| React.KeyboardEvent<HTMLInputElement>
) => void
} {
const handleInputChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
(
event:
| React.ChangeEvent<HTMLInputElement>
| React.KeyboardEvent<HTMLInputElement>
) => {
if (!isValueValid(event.currentTarget.value, isNegativeAllowed)) {
return
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { KeyboardEventHandler, useCallback } from 'react'
import { Decimal } from 'decimal.js'
import { cloneDeep, set } from 'lodash'

const KEY_ARROW_UP = 'ArrowUp'
const KEY_ARROW_DOWN = 'ArrowDown'

export const useInputKeys = (
step: number,
onChange: (
event:
| React.ChangeEvent<HTMLInputElement>
| React.KeyboardEvent<HTMLInputElement>
) => void
): {
handleKeyDown: KeyboardEventHandler<HTMLInputElement>
} => {
const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = useCallback(
(event) => {
const {
key,
currentTarget: { value },
} = event

if (![KEY_ARROW_UP, KEY_ARROW_DOWN].includes(key)) {
return
}

event.preventDefault()

if (value && !Number.isFinite(parseFloat(value))) {
return
}

const decimal = new Decimal(value || 0)

const newEvent = cloneDeep(event)

set(
newEvent,
'currentTarget.value',
String(key === KEY_ARROW_UP ? decimal.plus(step) : decimal.minus(step))
)

onChange(newEvent)
},
[step, onChange]
)

return {
handleKeyDown,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ const Example: React.FC<{ initialValues: FormValues }> = ({
onChange={(
_:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLButtonElement>,
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLInputElement>,
newValue: number | null
) => {
setFieldValue('exampleNumberInput', newValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ const Example: React.FC<{
onChange={(
_:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLButtonElement>,
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLInputElement>,
newValue: number | null
) => {
handleChange({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ const Example: React.FC<{ initialValues: FormValues }> = ({
const handleNumberInputChange = (
_:
| React.ChangeEvent<HTMLInputElement>
| React.MouseEvent<HTMLButtonElement>,
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLInputElement>,
newValue: number | null
) => setValue('exampleNumberInput', newValue)

Expand Down

0 comments on commit 08bb001

Please sign in to comment.