Skip to content

Commit ca2d7a0

Browse files
Matt Shwerymshwery
Matt Shwery
authored andcommitted
fix inner ref (#42)
* update innerRef * wip * support React.createRef refs * wip
1 parent 04b8b0b commit ca2d7a0

File tree

4 files changed

+208
-6
lines changed

4 files changed

+208
-6
lines changed

src/box.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ Box.displayName = 'Box'
2121

2222
Box.propTypes = {
2323
...propTypes,
24-
innerRef: PropTypes.func,
24+
innerRef: PropTypes.oneOfType([
25+
PropTypes.func,
26+
PropTypes.shape({ current: PropTypes.element })
27+
]),
2528
is: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
2629
}
2730

src/types/box-types.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import React from 'react'
22
import { EnhancerProps } from './enhancers'
3+
import { DomNodes } from './dom-nodes'
4+
5+
export { EnhancerProps }
36

47
/**
58
* @template T Object
@@ -13,6 +16,15 @@ export type Without<T, K> = Pick<T, Exclude<keyof T, K>>
1316
*/
1417
export type Is<P = any> = React.ElementType<P>
1518

19+
/**
20+
* Custom Ref to handle `is` prop
21+
*/
22+
export type RefType<T> = T extends keyof DomNodes
23+
? DomNodes[T] // Get the DOM node type
24+
: T extends typeof React.Component
25+
? T['prototype'] // Convert component class type back to a class instance
26+
: never // Functional components can't have refs
27+
1628
/**
1729
* Remove box props from object `T` if they're present
1830
* @template T Object
@@ -23,7 +35,7 @@ type WithoutBoxProps<T> = Without<T, "is" | "innerRef">
2335
* Grab components passed to the `is` prop and return their props
2436
* @template T Component type
2537
*/
26-
type InheritedProps<T extends Is> = WithoutBoxProps<React.ComponentProps<T>>
38+
type InheritedProps<T extends Is> = WithoutBoxProps<React.ComponentPropsWithoutRef<T>>
2739

2840
/**
2941
* Generic component props with "is" prop
@@ -41,7 +53,9 @@ export type BoxProps<T extends Is> = InheritedProps<T> &
4153
* Callback that gets passed a ref to inner DOM node (or component if the
4254
* `is` prop is set to a React component type).
4355
*/
44-
innerRef?: React.Ref<T>
56+
innerRef?:
57+
| ((ref: RefType<T>) => void)
58+
| React.RefObject<RefType<T>>
4559
}
4660

4761
export interface BoxComponent {

src/types/dom-nodes.ts

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
export interface DomNodes {
2+
// HTML
3+
a: HTMLAnchorElement
4+
abbr: HTMLElement
5+
address: HTMLElement
6+
area: HTMLAreaElement
7+
article: HTMLElement
8+
aside: HTMLElement
9+
audio: HTMLAudioElement
10+
b: HTMLElement
11+
base: HTMLBaseElement
12+
bdi: HTMLElement
13+
bdo: HTMLElement
14+
big: HTMLElement
15+
blockquote: HTMLElement
16+
body: HTMLBodyElement
17+
br: HTMLBRElement
18+
button: HTMLButtonElement
19+
canvas: HTMLCanvasElement
20+
caption: HTMLElement
21+
cite: HTMLElement
22+
code: HTMLElement
23+
col: HTMLTableColElement
24+
colgroup: HTMLTableColElement
25+
data: HTMLElement
26+
datalist: HTMLDataListElement
27+
dd: HTMLElement
28+
del: HTMLElement
29+
details: HTMLElement
30+
dfn: HTMLElement
31+
dialog: HTMLDialogElement
32+
div: HTMLDivElement
33+
dl: HTMLDListElement
34+
dt: HTMLElement
35+
em: HTMLElement
36+
embed: HTMLEmbedElement
37+
fieldset: HTMLFieldSetElement
38+
figcaption: HTMLElement
39+
figure: HTMLElement
40+
footer: HTMLElement
41+
form: HTMLFormElement
42+
h1: HTMLHeadingElement
43+
h2: HTMLHeadingElement
44+
h3: HTMLHeadingElement
45+
h4: HTMLHeadingElement
46+
h5: HTMLHeadingElement
47+
h6: HTMLHeadingElement
48+
head: HTMLHeadElement
49+
header: HTMLElement
50+
hgroup: HTMLElement
51+
hr: HTMLHRElement
52+
html: HTMLHtmlElement
53+
i: HTMLElement
54+
iframe: HTMLIFrameElement
55+
img: HTMLImageElement
56+
input: HTMLInputElement
57+
ins: HTMLModElement
58+
kbd: HTMLElement
59+
keygen: HTMLElement
60+
label: HTMLLabelElement
61+
legend: HTMLLegendElement
62+
li: HTMLLIElement
63+
link: HTMLLinkElement
64+
main: HTMLElement
65+
map: HTMLMapElement
66+
mark: HTMLElement
67+
menu: HTMLElement
68+
menuitem: HTMLElement
69+
meta: HTMLMetaElement
70+
meter: HTMLElement
71+
nav: HTMLElement
72+
noindex: HTMLElement
73+
noscript: HTMLElement
74+
object: HTMLObjectElement
75+
ol: HTMLOListElement
76+
optgroup: HTMLOptGroupElement
77+
option: HTMLOptionElement
78+
output: HTMLElement
79+
p: HTMLParagraphElement
80+
param: HTMLParamElement
81+
picture: HTMLElement
82+
pre: HTMLPreElement
83+
progress: HTMLProgressElement
84+
q: HTMLQuoteElement
85+
rp: HTMLElement
86+
rt: HTMLElement
87+
ruby: HTMLElement
88+
s: HTMLElement
89+
samp: HTMLElement
90+
script: HTMLScriptElement
91+
section: HTMLElement
92+
select: HTMLSelectElement
93+
small: HTMLElement
94+
source: HTMLSourceElement
95+
span: HTMLSpanElement
96+
strong: HTMLElement
97+
style: HTMLStyleElement
98+
sub: HTMLElement
99+
summary: HTMLElement
100+
sup: HTMLElement
101+
table: HTMLTableElement
102+
tbody: HTMLTableSectionElement
103+
td: HTMLTableDataCellElement
104+
textarea: HTMLTextAreaElement
105+
tfoot: HTMLTableSectionElement
106+
th: HTMLTableHeaderCellElement
107+
thead: HTMLTableSectionElement
108+
time: HTMLElement
109+
title: HTMLTitleElement
110+
tr: HTMLTableRowElement
111+
track: HTMLTrackElement
112+
u: HTMLElement
113+
ul: HTMLUListElement
114+
var: HTMLElement
115+
video: HTMLVideoElement
116+
wbr: HTMLElement
117+
webview: HTMLWebViewElement
118+
119+
// SVG
120+
svg: SVGSVGElement
121+
122+
animate: SVGElement
123+
animateMotion: SVGElement
124+
animateTransform: SVGElement
125+
circle: SVGCircleElement
126+
clipPath: SVGClipPathElement
127+
defs: SVGDefsElement
128+
desc: SVGDescElement
129+
ellipse: SVGEllipseElement
130+
feBlend: SVGFEBlendElement
131+
feColorMatrix: SVGFEColorMatrixElement
132+
feComponentTransfer: SVGFEComponentTransferElement
133+
feComposite: SVGFECompositeElement
134+
feConvolveMatrix: SVGFEConvolveMatrixElement
135+
feDiffuseLighting: SVGFEDiffuseLightingElement
136+
feDisplacementMap: SVGFEDisplacementMapElement
137+
feDistantLight: SVGFEDistantLightElement
138+
feFlood: SVGFEFloodElement
139+
feFuncA: SVGFEFuncAElement
140+
feFuncB: SVGFEFuncBElement
141+
feFuncG: SVGFEFuncGElement
142+
feFuncR: SVGFEFuncRElement
143+
feGaussianBlur: SVGFEGaussianBlurElement
144+
feImage: SVGFEImageElement
145+
feMerge: SVGFEMergeElement
146+
feMergeNode: SVGFEMergeNodeElement
147+
feMorphology: SVGFEMorphologyElement
148+
feOffset: SVGFEOffsetElement
149+
fePointLight: SVGFEPointLightElement
150+
feSpecularLighting: SVGFESpecularLightingElement
151+
feSpotLight: SVGFESpotLightElement
152+
feTile: SVGFETileElement
153+
feTurbulence: SVGFETurbulenceElement
154+
filter: SVGFilterElement
155+
foreignObject: SVGForeignObjectElement
156+
g: SVGGElement
157+
image: SVGImageElement
158+
line: SVGLineElement
159+
linearGradient: SVGLinearGradientElement
160+
marker: SVGMarkerElement
161+
mask: SVGMaskElement
162+
metadata: SVGMetadataElement
163+
mpath: SVGElement
164+
path: SVGPathElement
165+
pattern: SVGPatternElement
166+
polygon: SVGPolygonElement
167+
polyline: SVGPolylineElement
168+
radialGradient: SVGRadialGradientElement
169+
rect: SVGRectElement
170+
stop: SVGStopElement
171+
switch: SVGSwitchElement
172+
symbol: SVGSymbolElement
173+
text: SVGTextElement
174+
textPath: SVGTextPathElement
175+
tspan: SVGTSpanElement
176+
use: SVGUseElement
177+
view: SVGViewElement
178+
}

tools/story.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import React from 'react'
22
import Box from '../src'
33
import {storiesOf} from '@storybook/react'
44
import allPropertiesComponent from './all-properties-component'
5+
import { BoxProps } from '../src/types/box-types'
56

6-
const RedBox = redBoxProps => (
7+
const RedBox: React.FunctionComponent<BoxProps<'div'>> = redBoxProps => (
78
<Box background="red" width="100px" height="100px" margin="20px" {...redBoxProps} />
89
)
910

10-
const logRef = ref => console.log(ref)
11+
const logRef = (ref: Element | null) => console.log(ref)
12+
const reactRef = React.createRef<unknown>()
1113

1214
interface CustomProps { children: React.ReactNode }
1315
const CustomComp: React.FunctionComponent<CustomProps> = props => {
@@ -66,7 +68,7 @@ storiesOf('Box', module)
6668
<RedBox border="10px solid grey" borderColor="black" />
6769
<RedBox borderTop="10px solid grey" />
6870
<RedBox borderTop="10px solid grey" borderTopColor="black" />
69-
<RedBox borderTop="5px solid" borderTopStyle="dashed !important" />
71+
<RedBox borderTop="5px solid" borderTopStyle="dashed" />
7072
<RedBox borderBottom="10px solid grey" />
7173
<RedBox borderLeft="10px solid grey" />
7274
</Box>
@@ -151,6 +153,11 @@ storiesOf('Box', module)
151153
<Box innerRef={logRef}>innerRef</Box>
152154
</Box>
153155
))
156+
.add('innerRef as React ref', () => (
157+
<Box>
158+
<Box innerRef={reactRef}>React ref</Box>
159+
</Box>
160+
))
154161
.add('props pass through', () => (
155162
<Box>
156163
<Box is="input" type="file" />

0 commit comments

Comments
 (0)