@toptal/picasso-number-input 1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082 → 1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/picasso-number-input",
3
- "version": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
3
+ "version": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
4
4
  "description": "Toptal UI components library - NumberInput",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,14 +22,14 @@
22
22
  },
23
23
  "homepage": "https://github.com/toptal/picasso/tree/master/packages/picasso#readme",
24
24
  "dependencies": {
25
- "@toptal/picasso-container": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
26
- "@toptal/picasso-form": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
27
- "@toptal/picasso-icons": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
28
- "@toptal/picasso-input": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
29
- "@toptal/picasso-input-adornment": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
30
- "@toptal/picasso-outlined-input": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
31
- "@toptal/picasso-shared": "13.1.3-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.25+ca4ef823d",
32
- "@toptal/picasso-utils": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d",
25
+ "@toptal/picasso-container": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
26
+ "@toptal/picasso-form": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
27
+ "@toptal/picasso-icons": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
28
+ "@toptal/picasso-input": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
29
+ "@toptal/picasso-input-adornment": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
30
+ "@toptal/picasso-outlined-input": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
31
+ "@toptal/picasso-shared": "13.1.3-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.27+c6a9da7e9",
32
+ "@toptal/picasso-utils": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9",
33
33
  "classnames": "^2.3.1"
34
34
  },
35
35
  "sideEffects": [
@@ -40,16 +40,11 @@
40
40
  "@material-ui/core": "4.12.4",
41
41
  "react": ">=16.12.0 < 19.0.0"
42
42
  },
43
- "type": "module",
44
43
  "exports": {
45
44
  ".": "./dist-package/src/index.js"
46
45
  },
47
46
  "devDependencies": {
48
- "@toptal/picasso-test-utils": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-ca4ef823d.4082+ca4ef823d"
47
+ "@toptal/picasso-test-utils": "1.0.1-alpha-fx-4861-find-missing-deep-imports-in-staff-portal-c6a9da7e9.4084+c6a9da7e9"
49
48
  },
50
- "files": [
51
- "dist-package/**",
52
- "!dist-package/tsconfig.tsbuildinfo"
53
- ],
54
- "gitHead": "ca4ef823de9250ac91938e59b5156c3c35f0808c"
49
+ "gitHead": "c6a9da7e9866499202631169376c9acb579d4210"
55
50
  }
@@ -0,0 +1,152 @@
1
+ import type { ReactNode } from 'react'
2
+ import React, { forwardRef, useRef } from 'react'
3
+ import type { Theme } from '@material-ui/core/styles'
4
+ import { makeStyles } from '@material-ui/core/styles'
5
+ import type { BaseProps, OmitInternalProps } from '@toptal/picasso-shared'
6
+ import cx from 'classnames'
7
+ import { OutlinedInput } from '@toptal/picasso-outlined-input'
8
+ import { InputAdornment } from '@toptal/picasso-input-adornment'
9
+ import {
10
+ useCombinedRefs,
11
+ usePropDeprecationWarning,
12
+ } from '@toptal/picasso-utils'
13
+ import type { Props as OutlinedInputProps } from '@toptal/picasso-outlined-input'
14
+ import { useFieldsLayoutContext } from '@toptal/picasso-form'
15
+
16
+ import styles from './styles'
17
+ import { NumberInputEndAdornment } from '../NumberInputEndAdornment'
18
+
19
+ export interface Props
20
+ extends Omit<
21
+ OmitInternalProps<OutlinedInputProps>,
22
+ 'defaultValue' | 'type' | 'multiline' | 'rows'
23
+ >,
24
+ BaseProps {
25
+ /** Value of the `input` element. */
26
+ value?: string | number
27
+ /** Minimum value for the `input` element */
28
+ min?: number | string
29
+ /** Maximum value for the `input` element */
30
+ max?: number | string
31
+ /** Next value of the `input` element will be calculated based on step */
32
+ step?: number | string
33
+ /** Should controls be hidden or not */
34
+ hideControls?: boolean
35
+ /** Specify icon which should be rendered inside NumberInput */
36
+ icon?: ReactNode
37
+ /** Indicates whether component is in disabled state */
38
+ disabled?: boolean
39
+ /** Callback invoked when `NumberInput` changes its state. */
40
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
41
+ highlight?: 'autofill'
42
+ }
43
+
44
+ const useStyles = makeStyles<Theme, Props>(styles, {
45
+ name: 'PicassoNumberInput',
46
+ })
47
+
48
+ export const NumberInput = forwardRef<HTMLInputElement, Props>(
49
+ function NumberInput(props, ref) {
50
+ const {
51
+ step = 1,
52
+ min = -Infinity,
53
+ max = Infinity,
54
+ hideControls,
55
+ value,
56
+ onChange,
57
+ disabled,
58
+ error,
59
+ status,
60
+ onResetClick,
61
+ enableReset,
62
+ width,
63
+ icon,
64
+ size,
65
+ testIds,
66
+ highlight,
67
+ ...rest
68
+ } = props
69
+
70
+ // TODO: [FX-4715]
71
+ usePropDeprecationWarning({
72
+ props,
73
+ name: 'error',
74
+ componentName: 'NumberInput',
75
+ description:
76
+ 'Use the `status` prop instead. `error` is deprecated and will be removed in the next major release.',
77
+ })
78
+
79
+ const { layout } = useFieldsLayoutContext()
80
+
81
+ const classes = useStyles(props)
82
+
83
+ const inputRef = useCombinedRefs<HTMLInputElement>(
84
+ ref,
85
+ useRef<HTMLInputElement>(null)
86
+ )
87
+
88
+ const endAdornment = hideControls ? null : (
89
+ <NumberInputEndAdornment
90
+ step={step}
91
+ min={min}
92
+ max={max}
93
+ value={value}
94
+ disabled={disabled}
95
+ size={size}
96
+ inputRef={inputRef}
97
+ />
98
+ )
99
+
100
+ const startAdornment = icon ? (
101
+ <InputAdornment position='start' disablePointerEvents>
102
+ {icon}
103
+ </InputAdornment>
104
+ ) : null
105
+
106
+ return (
107
+ <OutlinedInput
108
+ classes={{
109
+ root: cx(classes.root, {
110
+ [classes.highlightAutofill]: highlight === 'autofill',
111
+ [classes.horizontalLayout]: layout === 'horizontal',
112
+ }),
113
+ input: classes.input,
114
+ }}
115
+ inputProps={{
116
+ ...rest,
117
+ step,
118
+ min,
119
+ max,
120
+ }}
121
+ width={width}
122
+ onResetClick={onResetClick}
123
+ enableReset={enableReset}
124
+ status={error ? 'error' : status}
125
+ inputRef={inputRef}
126
+ type='number'
127
+ value={value}
128
+ disabled={disabled}
129
+ onChange={onChange}
130
+ endAdornment={endAdornment}
131
+ startAdornment={startAdornment}
132
+ size={size}
133
+ testIds={testIds}
134
+ />
135
+ )
136
+ }
137
+ )
138
+
139
+ NumberInput.defaultProps = {
140
+ onChange: () => {},
141
+ value: 0,
142
+ step: 1,
143
+ min: -Infinity,
144
+ max: Infinity,
145
+ hideControls: false,
146
+ size: 'medium',
147
+ status: 'default',
148
+ }
149
+
150
+ NumberInput.displayName = 'NumberInput'
151
+
152
+ export default NumberInput
@@ -0,0 +1,73 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`NumberInput renders 1`] = `
4
+ <div
5
+ class="Picasso-root"
6
+ >
7
+ <div
8
+ class="MuiInputBase-root MuiOutlinedInput-root PicassoOutlinedInput-root PicassoNumberInput-root PicassoOutlinedInput-rootAuto PicassoOutlinedInput-rootMedium MuiInputBase-adornedEnd MuiOutlinedInput-adornedEnd"
9
+ >
10
+ <input
11
+ aria-invalid="false"
12
+ class="MuiInputBase-input MuiOutlinedInput-input PicassoOutlinedInput-input PicassoNumberInput-input PicassoOutlinedInput-inputMedium MuiInputBase-inputAdornedEnd MuiOutlinedInput-inputAdornedEnd"
13
+ max="100"
14
+ min="-100"
15
+ step="5"
16
+ type="number"
17
+ value="10"
18
+ />
19
+ <div
20
+ class="MuiInputAdornment-root PicassoInputAdornment-root MuiInputAdornment-positionEnd"
21
+ >
22
+ <div
23
+ class="PicassoContainer-flex PicassoContainer-inline PicassoContainer-column"
24
+ >
25
+ <button
26
+ class="MuiButtonBase-root NumberInputEndAdornment-medium NumberInputEndAdornment-root"
27
+ tabindex="0"
28
+ type="button"
29
+ >
30
+ <svg
31
+ class="PicassoSvgArrowUpMinor16-root"
32
+ style="min-width: 16px; min-height: 16px;"
33
+ viewBox="0 0 16 16"
34
+ >
35
+ <path
36
+ d="m7.997 5.29 4.707 4.707-.707.707-4-4-4 4-.707-.707 4-4 .707-.707Z"
37
+ />
38
+ </svg>
39
+ </button>
40
+ <button
41
+ class="MuiButtonBase-root NumberInputEndAdornment-medium NumberInputEndAdornment-root"
42
+ tabindex="0"
43
+ type="button"
44
+ >
45
+ <svg
46
+ class="PicassoSvgArrowDownMinor16-root"
47
+ style="min-width: 16px; min-height: 16px;"
48
+ viewBox="0 0 16 16"
49
+ >
50
+ <path
51
+ d="m11.997 5.29.707.707-4 4-.707.707-.707-.707-4-4 .707-.707 4 4 4-4Z"
52
+ />
53
+ </svg>
54
+ </button>
55
+ </div>
56
+ </div>
57
+ <fieldset
58
+ aria-hidden="true"
59
+ class="PrivateNotchedOutline-root MuiOutlinedInput-notchedOutline PicassoOutlinedInput-notchedOutline"
60
+ style="padding-left: 8px;"
61
+ >
62
+ <legend
63
+ class="PrivateNotchedOutline-legend"
64
+ style="width: 0.01px;"
65
+ >
66
+ <span>
67
+
68
+ </span>
69
+ </legend>
70
+ </fieldset>
71
+ </div>
72
+ </div>
73
+ `;
@@ -0,0 +1,7 @@
1
+ import type { OmitInternalProps } from '@toptal/picasso-shared'
2
+
3
+ import type { Props } from './NumberInput'
4
+
5
+ export { default as NumberInput } from './NumberInput'
6
+ export type NumberInputProps = OmitInternalProps<Props>
7
+ export * from './NumberInput'
@@ -0,0 +1,25 @@
1
+ import type { ChangeEventHandler } from 'react'
2
+ import React, { useState } from 'react'
3
+ import { NumberInput, Container } from '@toptal/picasso'
4
+
5
+ const DefaultExample = () => {
6
+ const [value, setValue] = useState('1')
7
+
8
+ const handleChange: ChangeEventHandler<HTMLInputElement> = event => {
9
+ setValue(event.target.value)
10
+ }
11
+
12
+ return (
13
+ <Container>
14
+ <NumberInput
15
+ value={value}
16
+ onChange={handleChange}
17
+ step='1'
18
+ max='100'
19
+ min='-100'
20
+ />
21
+ </Container>
22
+ )
23
+ }
24
+
25
+ export default DefaultExample
@@ -0,0 +1,26 @@
1
+ import type { ChangeEventHandler } from 'react'
2
+ import React, { useState } from 'react'
3
+ import { NumberInput, Container } from '@toptal/picasso'
4
+
5
+ const DisabledExample = () => {
6
+ const [value, setValue] = useState('1')
7
+
8
+ const handleChange: ChangeEventHandler<HTMLInputElement> = event => {
9
+ setValue(event.target.value)
10
+ }
11
+
12
+ return (
13
+ <Container>
14
+ <NumberInput
15
+ disabled
16
+ value={value}
17
+ onChange={handleChange}
18
+ step='5'
19
+ max='100'
20
+ min='-100'
21
+ />
22
+ </Container>
23
+ )
24
+ }
25
+
26
+ export default DisabledExample
@@ -0,0 +1,57 @@
1
+ import type { ChangeEventHandler } from 'react'
2
+ import React, { useState } from 'react'
3
+ import { NumberInput, Container, FormLabel } from '@toptal/picasso'
4
+ import { SPACING_4 } from '@toptal/picasso-utils'
5
+
6
+ const SizesExample = () => {
7
+ const [value, setValue] = useState('1')
8
+
9
+ const handleChange: ChangeEventHandler<HTMLInputElement> = event => {
10
+ setValue(event.target.value)
11
+ }
12
+
13
+ return (
14
+ <Container>
15
+ <Container padded={SPACING_4}>
16
+ <FormLabel htmlFor='small-number-input'>Small</FormLabel>
17
+ <NumberInput
18
+ id='small-number-input'
19
+ value={value}
20
+ size='small'
21
+ onChange={handleChange}
22
+ step='1'
23
+ max='100'
24
+ min='-100'
25
+ />
26
+ </Container>
27
+
28
+ <Container padded={SPACING_4}>
29
+ <FormLabel htmlFor='medium-number-input'>Medium (default)</FormLabel>
30
+ <NumberInput
31
+ id='medium-number-input'
32
+ value={value}
33
+ size='medium'
34
+ onChange={handleChange}
35
+ step='1'
36
+ max='100'
37
+ min='-100'
38
+ />
39
+ </Container>
40
+
41
+ <Container padded={SPACING_4}>
42
+ <FormLabel htmlFor='large-number-input'>Large</FormLabel>
43
+ <NumberInput
44
+ id='large-number-input'
45
+ value={value}
46
+ size='large'
47
+ onChange={handleChange}
48
+ step='1'
49
+ max='100'
50
+ min='-100'
51
+ />
52
+ </Container>
53
+ </Container>
54
+ )
55
+ }
56
+
57
+ export default SizesExample
@@ -0,0 +1,23 @@
1
+ import React from 'react'
2
+ import { NumberInput, Form } from '@toptal/picasso'
3
+
4
+ const Example = () => {
5
+ return (
6
+ <Form>
7
+ <Form.Field>
8
+ <Form.Label>Default</Form.Label>
9
+ <NumberInput value='100' status='default' />
10
+ </Form.Field>
11
+ <Form.Field>
12
+ <Form.Label>Error</Form.Label>
13
+ <NumberInput value='100' status='error' />
14
+ </Form.Field>
15
+ <Form.Field>
16
+ <Form.Label>Success</Form.Label>
17
+ <NumberInput value='100' status='success' />
18
+ </Form.Field>
19
+ </Form>
20
+ )
21
+ }
22
+
23
+ export default Example
@@ -0,0 +1,27 @@
1
+ import type { ChangeEventHandler } from 'react'
2
+ import React, { useState } from 'react'
3
+ import { NumberInput, Container } from '@toptal/picasso'
4
+ import { ReferralBonus16 } from '@toptal/picasso-icons'
5
+
6
+ const WithIconExample = () => {
7
+ const [value, setValue] = useState('1')
8
+
9
+ const handleChange: ChangeEventHandler<HTMLInputElement> = event => {
10
+ setValue(event.target.value)
11
+ }
12
+
13
+ return (
14
+ <Container>
15
+ <NumberInput
16
+ value={value}
17
+ onChange={handleChange}
18
+ step='5'
19
+ max='100'
20
+ min='-100'
21
+ icon={<ReferralBonus16 />}
22
+ />
23
+ </Container>
24
+ )
25
+ }
26
+
27
+ export default WithIconExample
@@ -0,0 +1,42 @@
1
+ import { NumberInput } from '../NumberInput'
2
+ import PicassoBook from '~/.storybook/components/PicassoBook'
3
+
4
+ const page = PicassoBook.section('Forms').createPage(
5
+ 'NumberInput',
6
+ `Input component for numbers
7
+
8
+ ${PicassoBook.createSourceLink(__filename)}
9
+ `
10
+ )
11
+
12
+ page
13
+ .createTabChapter('Props')
14
+ .addComponentDocs({ component: NumberInput, name: 'NumberInput' })
15
+
16
+ page
17
+ .createChapter()
18
+ .addExample(
19
+ 'NumberInput/story/Default.example.tsx',
20
+ 'Default',
21
+ 'base/NumberInput'
22
+ )
23
+ .addExample(
24
+ 'NumberInput/story/Disabled.example.tsx',
25
+ 'Disabled',
26
+ 'base/NumberInput'
27
+ )
28
+ .addExample(
29
+ 'NumberInput/story/Status.example.tsx',
30
+ 'Status',
31
+ 'base/NumberInput'
32
+ )
33
+ .addExample(
34
+ 'NumberInput/story/WithIcon.example.tsx',
35
+ 'With Icon',
36
+ 'base/NumberInput'
37
+ )
38
+ .addExample(
39
+ 'NumberInput/story/Sizes.example.tsx',
40
+ 'Sizes',
41
+ 'base/NumberInput'
42
+ )
@@ -0,0 +1,23 @@
1
+ import type { Theme } from '@material-ui/core/styles'
2
+ import { createStyles } from '@material-ui/core/styles'
3
+ import { highlightStyles as highlightAutofillStyles } from '@toptal/picasso-input'
4
+
5
+ export default (theme: Theme) =>
6
+ createStyles({
7
+ root: {
8
+ paddingRight: 0,
9
+ cursor: 'text',
10
+ },
11
+ input: {
12
+ '&::-webkit-outer-spin-button, &::-webkit-inner-spin-button': {
13
+ '-webkit-appearance': 'none',
14
+ appearance: 'none',
15
+ margin: 0,
16
+ },
17
+ '-moz-appearance': 'textfield',
18
+ },
19
+ horizontalLayout: {
20
+ width: '100%',
21
+ },
22
+ ...highlightAutofillStyles(theme),
23
+ })
@@ -0,0 +1,153 @@
1
+ import type { ChangeEventHandler } from 'react'
2
+ import React, { useState } from 'react'
3
+ import { render, fireEvent } from '@toptal/picasso-test-utils'
4
+
5
+ import type { Props as NumberInputProps } from './NumberInput'
6
+ import { NumberInput } from './NumberInput'
7
+
8
+ const NumberInputRenderer = (
9
+ props: { initialValue: number | string } & Partial<NumberInputProps>
10
+ ) => {
11
+ const [value, setValue] = useState(props.initialValue)
12
+
13
+ const handleChange: ChangeEventHandler<HTMLInputElement> = e => {
14
+ setValue(e.target.value)
15
+ }
16
+
17
+ return (
18
+ <NumberInput
19
+ step={5}
20
+ max={100}
21
+ min={-100}
22
+ value={value}
23
+ onChange={handleChange}
24
+ status={props.status}
25
+ testIds={props.testIds}
26
+ />
27
+ )
28
+ }
29
+
30
+ const renderNumberInput = (
31
+ props: Partial<NumberInputProps> & { initialValue?: number | string } = {}
32
+ ) => {
33
+ const { initialValue = '10' } = props
34
+
35
+ return render(<NumberInputRenderer initialValue={initialValue} {...props} />)
36
+ }
37
+
38
+ describe('NumberInput', () => {
39
+ it('renders', () => {
40
+ const { container } = renderNumberInput()
41
+
42
+ expect(container.firstChild).toMatchSnapshot()
43
+ })
44
+
45
+ it('increase value', async () => {
46
+ const { getByDisplayValue, queryAllByRole } = renderNumberInput()
47
+
48
+ const input = getByDisplayValue('10') as HTMLInputElement
49
+
50
+ const controls = queryAllByRole('button')
51
+ const controlUp = controls[0]
52
+
53
+ fireEvent.click(controlUp)
54
+
55
+ expect(input.value).toBe('15')
56
+ })
57
+
58
+ it('decrease value', () => {
59
+ const { getByDisplayValue, queryAllByRole } = renderNumberInput()
60
+
61
+ const input = getByDisplayValue('10') as HTMLInputElement
62
+
63
+ const controls = queryAllByRole('button')
64
+ const controlDown = controls[1]
65
+
66
+ fireEvent.click(controlDown)
67
+
68
+ expect(input.value).toBe('5')
69
+ })
70
+
71
+ describe('near max/min limits', () => {
72
+ it('increase value near max limit', () => {
73
+ const { getByDisplayValue, queryAllByRole } = renderNumberInput({
74
+ initialValue: '97',
75
+ })
76
+
77
+ const input = getByDisplayValue('97') as HTMLInputElement
78
+
79
+ const controls = queryAllByRole('button')
80
+ const controlUp = controls[0]
81
+
82
+ fireEvent.click(controlUp)
83
+
84
+ expect(input.value).toBe('100')
85
+ })
86
+
87
+ it('decrease value near min limit', () => {
88
+ const { getByDisplayValue, queryAllByRole } = renderNumberInput({
89
+ initialValue: '-97',
90
+ })
91
+
92
+ const input = getByDisplayValue('-97') as HTMLInputElement
93
+
94
+ const controls = queryAllByRole('button')
95
+ const controlDown = controls[1]
96
+
97
+ fireEvent.click(controlDown)
98
+
99
+ expect(input.value).toBe('-100')
100
+ })
101
+
102
+ it('decrease value when it is closer than step to max', () => {
103
+ const { getByDisplayValue, queryAllByRole } = renderNumberInput({
104
+ initialValue: '97',
105
+ })
106
+
107
+ const input = getByDisplayValue('97') as HTMLInputElement
108
+
109
+ const controls = queryAllByRole('button')
110
+ const controlDown = controls[1]
111
+
112
+ fireEvent.click(controlDown)
113
+
114
+ expect(input.value).toBe('95')
115
+ })
116
+
117
+ it('increase value when it is closer to min than step', () => {
118
+ const { getByDisplayValue, queryAllByRole } = renderNumberInput({
119
+ initialValue: '-97',
120
+ })
121
+
122
+ const input = getByDisplayValue('-97') as HTMLInputElement
123
+
124
+ const controls = queryAllByRole('button')
125
+ const controlUp = controls[0]
126
+
127
+ fireEvent.click(controlUp)
128
+
129
+ expect(input.value).toBe('-95')
130
+ })
131
+ })
132
+
133
+ describe('when in a valid state', () => {
134
+ it('shows valid icon', () => {
135
+ const testProps: NumberInputProps = {
136
+ value: '10',
137
+ status: 'success',
138
+ testIds: { validIcon: 'valid-icon' },
139
+ }
140
+
141
+ const { getByTestId, rerender } = renderNumberInput(testProps)
142
+
143
+ const validIcon = getByTestId('valid-icon')
144
+
145
+ expect(validIcon).toBeVisible()
146
+
147
+ // re-render with different props
148
+ rerender(<NumberInput {...testProps} status='error' />)
149
+
150
+ expect(validIcon).not.toBeVisible()
151
+ })
152
+ })
153
+ })