Skip to content

Commit 4382e55

Browse files
authored
chore: Add forwardRef everywhere (#108)
* chore: add `forwardRef` (almost) everywhere * docs: add refs docs
1 parent 23b6714 commit 4382e55

File tree

23 files changed

+398
-301
lines changed

23 files changed

+398
-301
lines changed

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"sort-imports": [
1414
"warn",
1515
{
16+
"ignoreCase": true,
1617
"ignoreDeclarationSort": true,
1718
"memberSyntaxSortOrder": ["single", "all", "multiple", "none"]
1819
}

src/components/Badge/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import React from 'react'
1+
import React, { forwardRef } from 'react'
22

33
import { Tag } from '../Tag'
44

5-
export const Badge = props => <Tag size="sm" {...props} />
5+
export const Badge = forwardRef((props, ref) => <Tag ref={ref} size="sm" {...props} />)
6+
7+
Badge.displayName = 'Badge'

src/components/Button/index.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1-
import React from 'react'
2-
import PropTypes from 'prop-types'
1+
import React, { forwardRef } from 'react'
2+
import { bool, node, oneOf } from 'prop-types'
33

44
import { IconButton } from '../IconButton'
55

66
import * as S from './styles'
77

8-
const Button = ({ children, disabled, size = 'md', variant = 'primary', ...props }) => {
9-
return (
8+
const Button = forwardRef(
9+
({ children, disabled, size = 'md', variant = 'primary', ...rest }, ref) => (
1010
<S.Button
1111
data-testid="button"
1212
disabled={disabled}
13+
ref={ref}
1314
size={size}
1415
variant={disabled ? 'disabled' : variant}
15-
{...props}
16+
{...rest}
1617
>
1718
{children}
1819
</S.Button>
1920
)
20-
}
21+
)
22+
23+
Button.displayName = 'Button'
2124

2225
Button.propTypes = {
23-
children: PropTypes.node,
24-
disabled: PropTypes.bool,
25-
/** To set the button size */
26-
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),
27-
/** To set a rounded button */
28-
variant: PropTypes.oneOf([
26+
children: node,
27+
disabled: bool,
28+
size: oneOf(['xs', 'sm', 'md', 'lg']),
29+
variant: oneOf([
2930
'primary',
3031
'secondary',
3132
'tertiary',

src/components/ConnectedField/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import React from 'react'
1+
import React, { forwardRef } from 'react'
22
import { bool, func, object, shape, string } from 'prop-types'
33
import { Field as FinalField } from 'react-final-form'
44

55
import { Field } from '../Field'
66
import { getBaseType } from '../../utils/fields'
77

8-
export const ConnectedField = ({ component, type, ...rest }) => (
8+
export const ConnectedField = forwardRef(({ component, type, ...rest }, ref) => (
99
<FinalField
1010
type={getBaseType(component.type || type)}
1111
{...rest}
1212
render={({ input, meta }) => (
13-
<Field {...rest} {...input} {...meta} component={component} connected />
13+
<Field {...rest} {...input} {...meta} component={component} connected inputRef={ref} />
1414
)}
1515
/>
16-
)
16+
))
17+
18+
ConnectedField.displayName = 'WelcomeField'
1719

1820
ConnectedField.propTypes = {
1921
component: func,

src/components/Field/index.js

Lines changed: 96 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Fragment } from 'react'
1+
import React, { forwardRef, Fragment } from 'react'
22
import PropTypes from 'prop-types'
33

44
// Common
@@ -12,97 +12,105 @@ import { Hint } from '../Hint'
1212
// Fields
1313
import { StyledField } from './styles'
1414

15-
export const Field = ({
16-
autoFocus,
17-
checked,
18-
children,
19-
component: Component,
20-
connected,
21-
disabled,
22-
disabledIcon,
23-
error,
24-
flexDirection,
25-
hint,
26-
label,
27-
name,
28-
onBlur,
29-
onChange,
30-
onFocus,
31-
onKeyDown,
32-
options,
33-
placeholder,
34-
required,
35-
touched,
36-
type,
37-
value,
38-
warning,
39-
...props
40-
}) => {
41-
// Return early if no component
42-
if (!Component) {
43-
return null
44-
}
15+
export const Field = forwardRef(
16+
(
17+
{
18+
autoFocus,
19+
checked,
20+
children,
21+
component: Component,
22+
connected,
23+
disabled,
24+
disabledIcon,
25+
error,
26+
flexDirection,
27+
hint,
28+
label,
29+
name,
30+
onBlur,
31+
onChange,
32+
onFocus,
33+
onKeyDown,
34+
options,
35+
placeholder,
36+
required,
37+
touched,
38+
type,
39+
value,
40+
warning,
41+
...props
42+
},
43+
ref
44+
) => {
45+
// Return early if no component
46+
if (!Component) {
47+
return null
48+
}
4549

46-
const baseType = getBaseType(Component.type || type)
47-
const variant = getVariant({ connected, touched, warning, error })
48-
const hintText = getHintText({ connected, touched, warning, error, hint })
49-
const isRadio = baseType === 'radio'
50-
const isCheckable = ['checkbox', 'radio'].includes(baseType)
50+
const baseType = getBaseType(Component.type || type)
51+
const variant = getVariant({ connected, touched, warning, error })
52+
const hintText = getHintText({ connected, touched, warning, error, hint })
53+
const isRadio = baseType === 'radio'
54+
const isCheckable = ['checkbox', 'radio'].includes(baseType)
5155

52-
const isShowRequired = isRadio ? null : required
53-
const layout = flexDirection || (isCheckable ? 'row' : 'column')
54-
const Container = layout === 'row' ? RowContainer : Fragment
55-
const htmlFor = isRadio ? value : name
56+
const isShowRequired = isRadio ? null : required
57+
const layout = flexDirection || (isCheckable ? 'row' : 'column')
58+
const Container = layout === 'row' ? RowContainer : Fragment
59+
const htmlFor = isRadio ? value : name
5660

57-
const field = (
58-
<Component
59-
autoFocus={autoFocus}
60-
checked={checked}
61-
disabled={disabled}
62-
flexDirection={layout}
63-
name={name}
64-
onBlur={onBlur}
65-
onChange={onChange}
66-
onFocus={onFocus}
67-
onKeyDown={onKeyDown}
68-
options={options}
69-
placeholder={placeholder}
70-
required={required}
71-
type={baseType}
72-
value={value}
73-
variant={variant}
74-
{...props}
75-
>
76-
{children}
77-
</Component>
78-
)
61+
const field = (
62+
<Component
63+
autoFocus={autoFocus}
64+
checked={checked}
65+
disabled={disabled}
66+
flexDirection={layout}
67+
inputRef={ref}
68+
name={name}
69+
onBlur={onBlur}
70+
onChange={onChange}
71+
onFocus={onFocus}
72+
onKeyDown={onKeyDown}
73+
options={options}
74+
placeholder={placeholder}
75+
required={required}
76+
type={baseType}
77+
value={value}
78+
variant={variant}
79+
{...props}
80+
>
81+
{children}
82+
</Component>
83+
)
7984

80-
return (
81-
<StyledField
82-
checkableField={isCheckable}
83-
checked={checked}
84-
fieldType={Component.type}
85-
flexDirection={layout}
86-
>
87-
<Container>
88-
{label && (
89-
<Label
90-
disabled={disabled}
91-
disabledIcon={disabledIcon}
92-
htmlFor={htmlFor}
93-
required={isShowRequired}
94-
variant={variant}
95-
>
96-
{isCheckable && field}
97-
{label}
98-
</Label>
99-
)}
100-
{!isCheckable && field}
101-
</Container>
102-
{hintText && <Hint variant={variant}>{hintText}</Hint>}
103-
</StyledField>
104-
)
105-
}
85+
return (
86+
<StyledField
87+
checkableField={isCheckable}
88+
checked={checked}
89+
fieldType={Component.type}
90+
flexDirection={layout}
91+
>
92+
<Container>
93+
{label && (
94+
<Label
95+
disabled={disabled}
96+
disabledIcon={disabledIcon}
97+
htmlFor={htmlFor}
98+
required={isShowRequired}
99+
variant={variant}
100+
>
101+
{isCheckable && field}
102+
{label}
103+
</Label>
104+
)}
105+
{!isCheckable && field}
106+
</Container>
107+
{hintText && <Hint variant={variant}>{hintText}</Hint>}
108+
</StyledField>
109+
)
110+
}
111+
)
112+
113+
Field.displayName = 'Field'
106114

107115
Field.propTypes = {
108116
autoFocus: PropTypes.bool,

src/components/FieldGroup/index.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
import React from 'react'
2-
import PropTypes from 'prop-types'
1+
import React, { forwardRef } from 'react'
2+
import { node, string } from 'prop-types'
33

44
import { Label } from '../Label'
55

66
import { StyledFieldGroup } from './styles'
77

8-
export const FieldGroup = ({ children, label }) => (
9-
<StyledFieldGroup>
8+
export const FieldGroup = forwardRef(({ children, label }, ref) => (
9+
<StyledFieldGroup ref={ref}>
1010
<Label as="legend">{label}</Label>
1111
{children}
1212
</StyledFieldGroup>
13-
)
13+
))
1414

15+
FieldGroup.displayName = 'FieldGroup'
1516
FieldGroup.propTypes = {
16-
children: PropTypes.node,
17-
label: PropTypes.string
17+
children: node,
18+
label: string
1819
}

src/components/FileUpload/index.js

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useState } from 'react'
2-
import PropTypes from 'prop-types'
2+
import { bool, func, node, number, oneOfType, string } from 'prop-types'
33
import { useDropzone } from 'react-dropzone'
44

55
// Common
@@ -24,6 +24,7 @@ export const FileUpload = ({
2424
children = DefaultContent,
2525
disabled,
2626
input,
27+
inputRef,
2728
maxSize = DEFAULT_MAX_FILE_SIZE,
2829
multiple,
2930
onAddFile,
@@ -90,7 +91,7 @@ export const FileUpload = ({
9091
<StyledFileUpload
9192
{...getRootProps({ handleRemoveFile, isDragActive, isDragAccept, isDragReject, disabled })}
9293
>
93-
<input {...getInputProps({ name: input && input.name })} />
94+
<input {...getInputProps({ name: input && input.name })} ref={inputRef} />
9495
<FilePreview>
9596
{children({
9697
fileUrl: file && getPreviewUrl(file.preview),
@@ -115,15 +116,16 @@ export const FileUpload = ({
115116
}
116117

117118
FileUpload.propTypes = {
118-
accept: PropTypes.string,
119-
children: PropTypes.func,
120-
disabled: PropTypes.bool,
121-
input: PropTypes.node,
122-
maxSize: PropTypes.number,
123-
multiple: PropTypes.bool,
124-
onAddFile: PropTypes.func,
125-
onChange: PropTypes.func,
126-
onError: PropTypes.func,
127-
onRemoveFile: PropTypes.func,
128-
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
119+
accept: string,
120+
children: func,
121+
disabled: bool,
122+
input: node,
123+
inputRef: node,
124+
maxSize: number,
125+
multiple: bool,
126+
onAddFile: func,
127+
onChange: func,
128+
onError: func,
129+
onRemoveFile: func,
130+
title: oneOfType([string, node])
129131
}

0 commit comments

Comments
 (0)