Skip to content

Commit

Permalink
fix: test on NumberInputField and weird NumberInput behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
DorianMaliszewski committed Oct 27, 2023
1 parent 64def02 commit 0e02316
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,7 @@ exports[`NumberInputField should render correctly 1`] = `
min-height: 26px;
}
<form
novalidate=""
>
<form>
<div
class="cache-1civgmb ehpbis70"
>
Expand Down Expand Up @@ -582,9 +580,7 @@ exports[`NumberInputField should render correctly disabled 1`] = `
margin-right: 8px;
}
<form
novalidate=""
>
<form>
<div
class="cache-1civgmb ehpbis70"
>
Expand Down Expand Up @@ -624,9 +620,9 @@ exports[`NumberInputField should render correctly disabled 1`] = `
disabled=""
id=":r5:"
name="test"
style="width: 16px;"
style="width: 32px;"
type="number"
value="0"
value="10"
/>
<span
class="cache-epkb7s exvap481"
Expand Down Expand Up @@ -944,9 +940,7 @@ exports[`NumberInputField should trigger event onMinCrossed & onMaxCrossed 1`] =
min-height: 26px;
}
<form
novalidate=""
>
<form>
<div
class="cache-1civgmb ehpbis70"
>
Expand Down Expand Up @@ -1247,9 +1241,7 @@ exports[`NumberInputField should trigger events correctly 1`] = `
margin-right: 8px;
}
<form
novalidate=""
>
<form>
<div
class="cache-1civgmb ehpbis70"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, jest, test } from '@jest/globals'
import { act, screen, waitFor } from '@testing-library/react'
import { act, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { NumberInputField } from '../..'
import {
Expand All @@ -12,12 +12,18 @@ import { Form } from '../../Form'
describe('NumberInputField', () => {
test('should render correctly', () =>
shouldMatchEmotionSnapshotFormWrapper(
<NumberInputField name="test" value={0} />,
<NumberInputField name="test" />,
undefined,
{
initialValues: {
test: 0,
},
},
))

test('should render correctly disabled', () =>
shouldMatchEmotionSnapshotFormWrapper(
<NumberInputField name="test" value={10} disabled />,
<NumberInputField name="test" disabled />,
{
transform: () => {
const input = screen.getByLabelText('Number Input')
Expand All @@ -30,14 +36,19 @@ describe('NumberInputField', () => {
expect(inputPlus).toBeDisabled()
},
},
{
initialValues: {
test: 10,
},
},
))

test('should trigger events correctly', () => {
const onFocus = jest.fn(() => {})
const onChange = jest.fn(() => {})
const onBlur = jest.fn(() => {})

return shouldMatchEmotionSnapshotFormWrapper(
return shouldMatchEmotionSnapshot(
<Form
onRawSubmit={() => {}}
errors={mockErrors}
Expand Down Expand Up @@ -92,30 +103,24 @@ describe('NumberInputField', () => {
</Form>,
{
transform: async () => {
const input =
screen.getByLabelText<HTMLTextAreaElement>('Number Input')
// eslint-disable-next-line testing-library/no-node-access
if (input.parentElement) await userEvent.click(input.parentElement)
const input = screen.getByRole<HTMLInputElement>('spinbutton')

// trigger onMinCrossed
await userEvent.clear(input)
expect(input.value).toBe('')
await userEvent.type(input, '1')
await waitFor(() => expect(input.value).toBe('1'))
act(() => {
input.blur()
})
await waitFor(() => expect(input.value).toBe('5'))
expect(input.value).toBe('1')
await userEvent.click(document.body)
expect(input.value).toBe('5')
expect(onMinCrossed).toBeCalledTimes(1)

// trigger onMaxCrossed
await userEvent.clear(input)
await userEvent.type(input, '100')
await waitFor(() => expect(input.value).toBe('100'))
act(() => {
input.blur()
})
await waitFor(() => expect(input.value).toBe('20'))
expect(onMinCrossed).toBeCalledTimes(1)
expect(input.value).toBe('100')
await userEvent.click(document.body)
expect(input.value).toBe('20')
expect(onMaxCrossed).toBeCalledTimes(1)
},
},
)
Expand Down
82 changes: 47 additions & 35 deletions packages/form/src/components/NumberInputField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NumberInput } from '@ultraviolet/ui'
import type { ComponentProps, FocusEvent, FocusEventHandler } from 'react'
import type { FieldValues } from 'react-hook-form'
import { Controller } from 'react-hook-form'
import { useErrors } from '../../providers'
import type { BaseFieldProps } from '../../types'

type NumberInputValueFieldProps<TFieldValues extends FieldValues> =
Expand Down Expand Up @@ -42,38 +43,49 @@ export const NumberInputField = <TFieldValues extends FieldValues>({
text,
rules,
className,
}: NumberInputValueFieldProps<TFieldValues>) => (
<Controller
name={name}
rules={{
required,
max: maxValue,
min: minValue,
...rules,
}}
render={({ field }) => (
<NumberInput
name={field.name}
value={field.value}
disabled={disabled}
onBlur={(event: FocusEvent<HTMLInputElement>) => {
field.onBlur()
onBlur?.(event)
}}
onChange={event => {
field.onChange(event)
onChange?.(event)
}}
onFocus={onFocus}
maxValue={maxValue}
minValue={minValue}
onMinCrossed={onMinCrossed}
onMaxCrossed={onMaxCrossed}
size={size}
step={step}
text={text}
className={className}
/>
)}
/>
)
label,
}: NumberInputValueFieldProps<TFieldValues>) => {
const { getError } = useErrors()

return (
<Controller
name={name}
rules={{
required,
max: maxValue,
min: minValue,
...rules,
}}
render={({ field, fieldState: { error } }) => (
<NumberInput
name={field.name}
value={field.value}
disabled={disabled}
onBlur={(event: FocusEvent<HTMLInputElement>) => {
field.onBlur()
onBlur?.(event)
}}
onChange={event => {
// React hook form doesnt allow undefined values after definition https://react-hook-form.com/docs/usecontroller/controller (that make sense)
field.onChange(event ?? null)
onChange?.(event)
}}
onFocus={onFocus}
maxValue={maxValue}
minValue={minValue}
onMinCrossed={onMinCrossed}
onMaxCrossed={onMaxCrossed}
size={size}
step={step}
text={text}
className={className}
label={label}
error={getError(
{ label: label ?? '', max: maxValue, min: minValue },
error,
)}
/>
)}
/>
)
}
2 changes: 1 addition & 1 deletion packages/form/src/mocks/mockErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export const mockErrors: FormErrors = {
pattern: () => `This field should match the regex`,
required: () => 'This field is required',
max: ({ max }) => `This field is too high (maximum is : ${max})`,
min: () => 'This field is too low',
min: ({ min }) => `This field is too low (minimum is: ${min})`,
}
46 changes: 19 additions & 27 deletions packages/ui/src/components/NumberInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ type NumberInputProps = {
*/
text?: string
defaultValue?: number
value?: number
value?: number | null
disabledTooltip?: string
className?: string
'data-testid'?: string
Expand Down Expand Up @@ -209,7 +209,8 @@ export const NumberInput = ({
return defaultValue
})

const currentValue = value !== undefined ? value : inputValue
const currentValue =
value !== undefined && value !== null ? value : inputValue

const setValue = (
newValue: number | undefined,
Expand All @@ -218,28 +219,22 @@ export const NumberInput = ({
*/
hasMinMaxVerification = true,
) => {
if (value === undefined) {
if (hasMinMaxVerification) {
if (newValue !== undefined && newValue < minValue) {
setInputValue(minValue)

return
}

if (
newValue !== undefined &&
maxValue !== undefined &&
newValue > maxValue
) {
setInputValue(maxValue)

return
}
let nextValue = newValue
if (value === undefined && hasMinMaxVerification) {
if (newValue !== undefined && newValue < minValue) {
nextValue = minValue
}

setInputValue(newValue)
if (
newValue !== undefined &&
maxValue !== undefined &&
newValue > maxValue
) {
nextValue = maxValue
}
}
onChange?.(newValue)
setInputValue(nextValue)
onChange?.(nextValue)
}

const offsetFn = (direction: number) => () => {
Expand All @@ -258,12 +253,8 @@ export const NumberInput = ({
)
}

const handleOnFocus: FocusEventHandler<HTMLInputElement> = event => {
if (onFocus) onFocus(event)
}

const handleOnBlur: FocusEventHandler<HTMLInputElement> = event => {
if (currentValue) {
if (currentValue !== undefined) {
const boundedValue = bounded(
currentValue,
minValue ?? currentValue,
Expand Down Expand Up @@ -392,11 +383,12 @@ export const NumberInput = ({
role="status"
>
<StyledInput
aria-invalid={!!error}
disabled={disabled}
name={name}
onBlur={handleOnBlur}
onChange={handleChange}
onFocus={handleOnFocus}
onFocus={onFocus}
onKeyDown={onKeyDown}
ref={inputRef}
style={{
Expand Down

0 comments on commit 0e02316

Please sign in to comment.