Skip to content

Commit 9341282

Browse files
Merge pull request #68 from TangibleInc/fix/number-field-decimal-support
fix: enable decimal input in Number field
2 parents 78106ab + 9f5e98d commit 9341282

4 files changed

Lines changed: 155 additions & 29 deletions

File tree

assets/build/example.min.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/build/index.min.js

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/src/components/field/number/Number.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {
1+
import {
22
useRef,
3+
useMemo,
34
useState,
45
useEffect
56
} from 'react'
@@ -22,7 +23,10 @@ const NumberComponent = props => {
2223

2324
const { locale } = useLocale()
2425
const [value, setValue] = useState(props.value ?? '')
25-
const state = useNumberFieldState({ ...props, locale })
26+
27+
const defaultFormatOptions = useMemo(() => ({ maximumFractionDigits: 10, useGrouping: false }), [])
28+
const formatOptions = props.formatOptions ?? defaultFormatOptions
29+
const state = useNumberFieldState({ ...props, formatOptions, locale })
2630
const inputRef = useRef()
2731

2832
const {
@@ -32,7 +36,7 @@ const NumberComponent = props => {
3236
inputProps,
3337
incrementButtonProps,
3438
decrementButtonProps
35-
} = useNumberField(props, state, inputRef)
39+
} = useNumberField({ ...props, formatOptions }, state, inputRef)
3640

3741
useEffect(() => props.onChange && props.onChange(value), [value])
3842

@@ -53,10 +57,10 @@ const NumberComponent = props => {
5357
ref={ inputRef }
5458
inputProps={ inputProps }
5559
>
56-
<input
57-
{ ...inputProps}
58-
value={ Number.isInteger(state.numberValue) ? state.numberValue : 0 }
59-
ref={ inputRef }
60+
<input
61+
{ ...inputProps}
62+
step={ props.step ?? 1 }
63+
ref={ inputRef }
6064
name={ props.name ?? '' }
6165
disabled={ isDisabled }
6266
/>

tests/jest/cases/components/controls/Number.test.tsx

Lines changed: 129 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import * as fields from '../../../../../assets/src/index.tsx'
2-
import {
3-
render,
4-
within
2+
import {
3+
render,
4+
within
55
} from '@testing-library/react'
6-
import {
6+
import userEvent from '@testing-library/user-event'
7+
import {
78
rendersWithMinimal,
89
rendersWithoutLabelThrowWarning,
910
rendersLabelAndDescription,
@@ -37,10 +38,10 @@ describe('Number component', () => {
3738
{ props: { readOnly : false }, result: false }
3839
])('supports readOnly (%p)', async args => {
3940

40-
const { container } = render(
41+
const { container } = render(
4142
fields.render({
42-
name : 'field-name',
43-
type : 'number',
43+
name : 'field-name',
44+
type : 'number',
4445
label : 'Label',
4546
...args.props
4647
})
@@ -53,4 +54,125 @@ describe('Number component', () => {
5354
expect( within(container).getByText('-').hasAttribute('disabled') ).toBe(args.result)
5455
})
5556

57+
test.each([
58+
{ value: 3.14, expected: '3.14' },
59+
{ value: '3.14', expected: '3.14' },
60+
])('accepts a decimal value as initial prop ($value)', ({ value, expected }) => {
61+
62+
const { container } = render(
63+
fields.render({
64+
name : 'field-name',
65+
type : 'number',
66+
label : 'Label',
67+
value : value
68+
})
69+
)
70+
71+
const input = container.querySelector('.tf-number input')
72+
expect( input.value ).toBe(expected)
73+
})
74+
75+
test.each([
76+
{ value: 0.5, expected: '0.5' },
77+
{ value: '0.5', expected: '0.5' },
78+
])('does not collapse decimal values to 0 ($value)', ({ value, expected }) => {
79+
80+
const { container } = render(
81+
fields.render({
82+
name : 'field-name',
83+
type : 'number',
84+
label : 'Label',
85+
value : value
86+
})
87+
)
88+
89+
const input = container.querySelector('.tf-number input')
90+
expect( input.value ).toBe(expected)
91+
})
92+
93+
it('accepts a decimal input typed by the user', async () => {
94+
95+
const user = userEvent.setup()
96+
97+
const { container } = render(
98+
fields.render({
99+
name : 'field-name',
100+
type : 'number',
101+
label : 'Label',
102+
})
103+
)
104+
105+
const input = container.querySelector('.tf-number input')
106+
107+
await user.clear(input)
108+
await user.type(input, '2.75')
109+
110+
expect( input.value ).toBe('2.75')
111+
})
112+
113+
it('increments by step value when clicking +', async () => {
114+
115+
const user = userEvent.setup()
116+
117+
const { container } = render(
118+
fields.render({
119+
name : 'field-name',
120+
type : 'number',
121+
label : 'Label',
122+
value : 1,
123+
step : 0.5
124+
})
125+
)
126+
127+
const input = container.querySelector('.tf-number input')
128+
const incrementButton = within(container).getByText('+')
129+
130+
expect( input.value ).toBe('1')
131+
132+
await user.click(incrementButton)
133+
expect( input.value ).toBe('1.5')
134+
135+
await user.click(incrementButton)
136+
expect( input.value ).toBe('2')
137+
})
138+
139+
it('increments by 1 when step is not specified', async () => {
140+
141+
const user = userEvent.setup()
142+
143+
const { container } = render(
144+
fields.render({
145+
name : 'field-name',
146+
type : 'number',
147+
label : 'Label',
148+
value : 3,
149+
})
150+
)
151+
152+
const input = container.querySelector('.tf-number input')
153+
const incrementButton = within(container).getByText('+')
154+
155+
await user.click(incrementButton)
156+
expect( input.value ).toBe('4')
157+
})
158+
159+
test.each([
160+
{ value: 3.14159, expected: '3.14' },
161+
{ value: '3.14159', expected: '3.14' },
162+
])('truncates decimals when formatOptions limits fraction digits ($value)', ({ value, expected }) => {
163+
164+
const { container } = render(
165+
fields.render({
166+
name : 'field-name',
167+
type : 'number',
168+
label : 'Label',
169+
value : value,
170+
formatOptions : { maximumFractionDigits: 2, useGrouping: false }
171+
})
172+
)
173+
174+
const input = container.querySelector('.tf-number input')
175+
expect( input.value ).toBe(expected)
176+
})
177+
56178
})

0 commit comments

Comments
 (0)