Skip to content

Commit c87cd44

Browse files
Fix forwarded select ref (#1435)
* Forward ref to underlying <select /> instead of wrapping div * Add mockRef test util, separate out TextInput/TextInputField tests
1 parent ec0e180 commit c87cd44

File tree

5 files changed

+77
-37
lines changed

5 files changed

+77
-37
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react'
2+
import { render } from '@testing-library/react'
3+
import { Select } from '../'
4+
import { mockRef } from '../../test/utils'
5+
6+
const makeSelectFixture = (props = {}) => <Select data-testid="select" {...props} />
7+
8+
describe('Select', () => {
9+
it('should forward ref to underlying <select />', () => {
10+
const ref = mockRef()
11+
12+
render(makeSelectFixture({ ref }))
13+
14+
expect(ref.current).toBeInstanceOf(HTMLSelectElement)
15+
})
16+
})

src/select/src/Select.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ const Select = memo(
7373

7474
return (
7575
<Box
76-
ref={ref}
7776
display="inline-flex"
7877
flex={1}
7978
position="relative"
@@ -84,6 +83,7 @@ const Select = memo(
8483
>
8584
<Box
8685
is="select"
86+
ref={ref}
8787
className={themedClassName}
8888
id={id}
8989
name={name}

src/test/utils.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,11 @@ export const buildFileRejection = file => ({
4646
reason: faker.random.arrayElement(Object.values(FileRejectionReason)),
4747
message: faker.random.words()
4848
})
49+
50+
/**
51+
* Returns a mock ref object in the shape of `{ current: jest.fn() }`
52+
*/
53+
export const mockRef = () => ({
54+
// eslint-disable-next-line no-undef
55+
current: jest.fn()
56+
})
Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
11
import React, { useState } from 'react'
22
import { render } from '@testing-library/react'
33
import userEvent from '@testing-library/user-event'
4-
import { TextInput, TextInputField } from '../'
4+
import { TextInput } from '../'
5+
import { mockRef } from '../../test/utils'
56

67
function makeTextInputFixture(props = {}) {
78
return <TextInput data-testid="input" {...props} />
89
}
910

10-
function makeTextInputFieldFixture(props = {}) {
11-
return <TextInputField data-testid="input" label="Name" {...props} />
12-
}
13-
1411
describe('TextInput', () => {
15-
it('Should render without crashing', () => {
12+
it('should forward ref to underlying <input />', () => {
13+
const ref = mockRef()
14+
15+
render(makeTextInputFixture({ ref }))
16+
17+
expect(ref.current).toBeInstanceOf(HTMLInputElement)
18+
})
19+
20+
it('should render without crashing', () => {
1621
expect(() => render(makeTextInputFixture())).not.toThrow()
1722
})
1823

19-
it('Should accept placeholder text', () => {
24+
it('should accept placeholder text', () => {
2025
const { getByPlaceholderText } = render(makeTextInputFixture({ placeholder: 'Enter text here' }))
26+
2127
expect(getByPlaceholderText('Enter text here')).toBeInTheDocument()
2228
})
2329

24-
it('Should set an invalid state if `isInvalid` is `true`', () => {
30+
it('should set an invalid state if `isInvalid` is `true`', () => {
2531
const { getByTestId } = render(makeTextInputFixture({ isInvalid: true }))
2632
const input = getByTestId('input')
33+
2734
expect(input).toHaveAttribute('aria-invalid', 'true')
2835
})
2936

30-
it('Should accept an `onChange` handler to be a controlled component', () => {
37+
it('should accept an `onChange` handler to be a controlled component', () => {
3138
function ControlledTextInput() {
3239
const [value, setValue] = useState('')
3340
return (
@@ -43,42 +50,18 @@ describe('TextInput', () => {
4350
const { getByDisplayValue, getByTestId } = render(<ControlledTextInput />)
4451
const input = getByTestId('input')
4552
userEvent.click(input)
53+
4654
expect(document.activeElement).toEqual(input)
4755
userEvent.type(input, 'Testing')
4856
expect(getByDisplayValue('Testing')).toEqual(input)
4957
})
5058

51-
it('Should not be interactive if `disabled` is passed in', () => {
59+
it('should not be interactive if `disabled` is passed in', () => {
5260
const { getByDisplayValue, getByTestId } = render(makeTextInputFixture({ disabled: true }))
5361
const input = getByTestId('input')
5462
userEvent.type(input, 'Testing')
63+
5564
expect(() => getByDisplayValue('Testing')).toThrowError()
5665
expect(getByDisplayValue('')).toEqual(input)
5766
})
5867
})
59-
60-
describe('TextInputField', () => {
61-
it('Should render without crashing', () => {
62-
expect(() => render(makeTextInputFieldFixture())).not.toThrow()
63-
})
64-
65-
it('Should render a required `label` when passed in', () => {
66-
const { getByLabelText } = render(makeTextInputFieldFixture())
67-
expect(getByLabelText('Name')).toBeInTheDocument()
68-
})
69-
70-
it('Should render a `hint` underneath the input', () => {
71-
const { getByText } = render(makeTextInputFieldFixture({ hint: 'Enter a value in the input' }))
72-
expect(getByText('Enter a value in the input')).toBeInTheDocument()
73-
})
74-
75-
it('Should render an astrix when `required` is passed in', () => {
76-
const { getByTitle } = render(makeTextInputFieldFixture({ required: true }))
77-
expect(getByTitle('This field is required.')).toBeInTheDocument()
78-
})
79-
80-
it('Should not render a `validationMessage` when passed in', () => {
81-
const { getByText } = render(makeTextInputFieldFixture({ validationMessage: 'Please enter a value' }))
82-
expect(getByText('Please enter a value')).toBeInTheDocument()
83-
})
84-
})
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react'
2+
import { render } from '@testing-library/react'
3+
import { TextInputField } from '../'
4+
5+
function makeTextInputFieldFixture(props = {}) {
6+
return <TextInputField data-testid="input" label="Name" {...props} />
7+
}
8+
9+
describe('TextInputField', () => {
10+
it('should render without crashing', () => {
11+
expect(() => render(makeTextInputFieldFixture())).not.toThrow()
12+
})
13+
14+
it('should render a required `label` when passed in', () => {
15+
const { getByLabelText } = render(makeTextInputFieldFixture())
16+
expect(getByLabelText('Name')).toBeInTheDocument()
17+
})
18+
19+
it('should render a `hint` underneath the input', () => {
20+
const { getByText } = render(makeTextInputFieldFixture({ hint: 'Enter a value in the input' }))
21+
expect(getByText('Enter a value in the input')).toBeInTheDocument()
22+
})
23+
24+
it('should render an astrix when `required` is passed in', () => {
25+
const { getByTitle } = render(makeTextInputFieldFixture({ required: true }))
26+
expect(getByTitle('This field is required.')).toBeInTheDocument()
27+
})
28+
29+
it('should not render a `validationMessage` when passed in', () => {
30+
const { getByText } = render(makeTextInputFieldFixture({ validationMessage: 'Please enter a value' }))
31+
expect(getByText('Please enter a value')).toBeInTheDocument()
32+
})
33+
})

0 commit comments

Comments
 (0)