This repository has been archived by the owner on Mar 4, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Ref): support of
forwardRef()
API (#491)
* feat(Ref): support of `forwardRef()` API * fix styling * update yarn.lock * add entry to changelog * rename examples * fix changelog * clean up test * regenerate lock * fix review comments * fix tests * fix types * add entry to changelog * update changelog
- Loading branch information
1 parent
b355abd
commit 5dc926b
Showing
13 changed files
with
261 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
docs/src/examples/components/Ref/Types/RefForwardingExample.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import React from 'react' | ||
import { Grid, Ref, Segment } from '@stardust-ui/react' | ||
|
||
const ExampleButton = React.forwardRef<HTMLButtonElement>((props, ref) => ( | ||
<div> | ||
<button {...props} ref={ref} /> | ||
</div> | ||
)) | ||
|
||
class RefForwardingExample extends React.Component { | ||
forwardedRef = React.createRef<HTMLButtonElement>() | ||
state = { isMounted: false } | ||
|
||
componentDidMount() { | ||
this.setState({ isMounted: true }) | ||
} | ||
|
||
render() { | ||
const { isMounted } = this.state | ||
const buttonNode = this.forwardedRef.current | ||
|
||
return ( | ||
<Grid columns={2}> | ||
<Segment> | ||
<p> | ||
A button below uses <code>forwardRef</code> API. | ||
</p> | ||
|
||
<Ref innerRef={this.forwardedRef}> | ||
<ExampleButton>A button</ExampleButton> | ||
</Ref> | ||
</Segment> | ||
|
||
{isMounted && ( | ||
<code style={{ margin: 10 }}> | ||
<pre> | ||
{JSON.stringify( | ||
{ | ||
nodeName: buttonNode.nodeName, | ||
nodeType: buttonNode.nodeType, | ||
textContent: buttonNode.textContent, | ||
}, | ||
null, | ||
2, | ||
)} | ||
</pre> | ||
</code> | ||
)} | ||
</Grid> | ||
) | ||
} | ||
} | ||
|
||
export default RefForwardingExample |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import * as PropTypes from 'prop-types' | ||
import * as React from 'react' | ||
import { findDOMNode } from 'react-dom' | ||
|
||
import { ChildrenComponentProps, handleRef } from '../../lib' | ||
|
||
export interface RefFindNodeProps extends ChildrenComponentProps<React.ReactElement<any>> { | ||
/** | ||
* Called when a child component will be mounted or updated. | ||
* | ||
* @param {HTMLElement} node - Referred node. | ||
*/ | ||
innerRef?: React.Ref<any> | ||
} | ||
|
||
export default class RefFindNode extends React.Component<RefFindNodeProps> { | ||
static propTypes = { | ||
children: PropTypes.element.isRequired, | ||
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), | ||
} | ||
|
||
componentDidMount() { | ||
handleRef(this.props.innerRef, findDOMNode(this)) | ||
} | ||
|
||
componentWillUnmount() { | ||
handleRef(this.props.innerRef, null) | ||
} | ||
|
||
render() { | ||
const { children } = this.props | ||
|
||
return children | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import * as PropTypes from 'prop-types' | ||
import * as React from 'react' | ||
|
||
import { ChildrenComponentProps, handleRef } from '../../lib' | ||
|
||
export interface RefForwardProps | ||
extends ChildrenComponentProps<React.ReactElement<any> & { ref: React.Ref<any> }> { | ||
/** | ||
* Called when a child component will be mounted or updated. | ||
* | ||
* @param {HTMLElement} node - Referred node. | ||
*/ | ||
innerRef?: React.Ref<any> | ||
} | ||
|
||
export default class RefForward extends React.Component<RefForwardProps> { | ||
static propTypes = { | ||
children: PropTypes.element.isRequired, | ||
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), | ||
} | ||
|
||
private handleRefOverride = (node: HTMLElement) => { | ||
const { children, innerRef } = this.props | ||
|
||
handleRef(children.ref, node) | ||
handleRef(innerRef, node) | ||
} | ||
|
||
render() { | ||
const { children } = this.props | ||
|
||
return React.cloneElement(children, { | ||
ref: this.handleRefOverride, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { mount } from 'enzyme' | ||
import * as React from 'react' | ||
|
||
import Ref from 'src/components/Ref/Ref' | ||
import { CompositeClass, CompositeFunction, DOMClass, DOMFunction } from './fixtures' | ||
|
||
const testInnerRef = Component => { | ||
const innerRef = jest.fn() | ||
const node = mount( | ||
<Ref innerRef={innerRef}> | ||
<Component /> | ||
</Ref>, | ||
).getDOMNode() | ||
|
||
expect(innerRef).toHaveBeenCalledTimes(1) | ||
expect(innerRef).toHaveBeenCalledWith(node) | ||
} | ||
|
||
describe('Ref', () => { | ||
describe('innerRef', () => { | ||
it('returns node from a functional component with DOM node', () => { | ||
testInnerRef(DOMFunction) | ||
}) | ||
|
||
it('returns node from a functional component', () => { | ||
testInnerRef(CompositeFunction) | ||
}) | ||
|
||
it('returns node from a class component with DOM node', () => { | ||
testInnerRef(DOMClass) | ||
}) | ||
|
||
it('returns node from a class component', () => { | ||
testInnerRef(CompositeClass) | ||
}) | ||
|
||
it('returns "null" after unmount', () => { | ||
const innerRef = jest.fn() | ||
const wrapper = mount( | ||
<Ref innerRef={innerRef}> | ||
<CompositeClass /> | ||
</Ref>, | ||
) | ||
|
||
innerRef.mockClear() | ||
wrapper.unmount() | ||
|
||
expect(innerRef).toHaveBeenCalledTimes(1) | ||
expect(innerRef).toHaveBeenCalledWith(null) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { mount } from 'enzyme' | ||
import * as React from 'react' | ||
|
||
import RefForward from 'src/components/Ref/RefForward' | ||
import { ForwardedRef } from './fixtures' | ||
|
||
describe('RefForward', () => { | ||
describe('innerRef', () => { | ||
it('works with "forwardRef" API', () => { | ||
const forwardedRef = React.createRef<HTMLButtonElement>() | ||
const innerRef = React.createRef() | ||
|
||
mount( | ||
<RefForward innerRef={innerRef}>{<ForwardedRef ref={forwardedRef} /> as any}</RefForward>, | ||
) | ||
|
||
expect(forwardedRef.current).toBeInstanceOf(Element) | ||
expect(innerRef.current).toBeInstanceOf(Element) | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.