Skip to content

Commit 2743e97

Browse files
committed
feat(Ref): add component
1 parent 292c595 commit 2743e97

File tree

8 files changed

+142
-0
lines changed

8 files changed

+142
-0
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm';
33
export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal';
44
export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio';
5+
export { default as Ref, RefProps } from './dist/commonjs/addons/Ref';
56
export { default as Select, SelectProps } from './dist/commonjs/addons/Select';
67
export { default as TextArea, TextAreaProps, TextAreaOnChangeData } from './dist/commonjs/addons/TextArea';
78

src/addons/Ref/Ref.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as React from 'react';
2+
import { DOMElement } from 'react';
3+
4+
export interface RefProps {
5+
[key: string]: any;
6+
7+
/** Primary content. */
8+
children: React.ReactNode;
9+
10+
/**
11+
* Called when componentDidMount.
12+
*
13+
* @param {DOMElement} node - Referred node.
14+
*/
15+
innerRef: (node: DOMElement) => void;
16+
}
17+
18+
declare const Ref: React.ComponentClass<RefProps>;
19+
20+
export default Ref;

src/addons/Ref/Ref.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import PropTypes from 'prop-types'
2+
import { Children, Component } from 'react'
3+
import { findDOMNode } from 'react-dom'
4+
5+
import { META } from '../../lib'
6+
7+
/**
8+
* This component exposes a callback prop that always returns the DOM node of both functional and class component
9+
* children.
10+
*/
11+
export default class Ref extends Component {
12+
static propTypes = {
13+
children: PropTypes.element.isRequired,
14+
15+
/**
16+
* Called when componentDidMount.
17+
*
18+
* @param {DOMElement} node - Referred node.
19+
*/
20+
innerRef: PropTypes.func.isRequired,
21+
}
22+
23+
static _meta = {
24+
name: 'Ref',
25+
type: META.TYPES.ADDON,
26+
}
27+
28+
componentDidMount() {
29+
const { innerRef } = this.props
30+
const node = findDOMNode(this)
31+
32+
innerRef(node)
33+
}
34+
35+
render() {
36+
const { children } = this.props
37+
38+
return Children.only(children)
39+
}
40+
}

src/addons/Ref/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default, RefProps } from './Ref';

src/addons/Ref/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default from './Ref'

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
export { default as Confirm } from './addons/Confirm'
33
export { default as Portal } from './addons/Portal'
44
export { default as Radio } from './addons/Radio'
5+
export { default as Ref } from './addons/Ref'
56
export { default as Select } from './addons/Select'
67
export { default as TextArea } from './addons/TextArea'
78

test/specs/addons/Ref/Ref-test.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import _ from 'lodash'
2+
import React from 'react'
3+
4+
import Ref from 'src/addons/Ref/Ref'
5+
import * as common from 'test/specs/commonTests'
6+
import { sandbox } from 'test/utils'
7+
import { CompositeClass, CompositeFunction, DOMClass, DOMFunction } from './fixtures'
8+
9+
const nodeMount = (Component, innerRef) => (
10+
mount(
11+
<Ref innerRef={innerRef}>
12+
<Component />
13+
</Ref>
14+
)
15+
.find('#node')
16+
.getDOMNode()
17+
)
18+
19+
describe('Ref', () => {
20+
common.hasValidTypings(Ref, null, {
21+
requiredProps: {
22+
children: <div />,
23+
innerRef: _.noop,
24+
},
25+
})
26+
27+
describe('innerRef', () => {
28+
it('returns node from a functional component with DOM node', () => {
29+
const innerRef = sandbox.spy()
30+
const node = nodeMount(DOMFunction, innerRef)
31+
32+
innerRef.should.have.been.calledOnce()
33+
innerRef.should.have.been.calledWithMatch(node)
34+
})
35+
36+
it('returns node from a functional component', () => {
37+
const innerRef = sandbox.spy()
38+
const node = nodeMount(CompositeFunction, innerRef)
39+
40+
innerRef.should.have.been.calledOnce()
41+
innerRef.should.have.been.calledWithMatch(node)
42+
})
43+
44+
it('returns node from a class component with DOM node', () => {
45+
const innerRef = sandbox.spy()
46+
const node = nodeMount(DOMClass, innerRef)
47+
48+
innerRef.should.have.been.calledOnce()
49+
innerRef.should.have.been.calledWithMatch(node)
50+
})
51+
52+
it('returns node from a class component', () => {
53+
const innerRef = sandbox.spy()
54+
const node = nodeMount(CompositeClass, innerRef)
55+
56+
innerRef.should.have.been.calledOnce()
57+
innerRef.should.have.been.calledWithMatch(node)
58+
})
59+
})
60+
})

test/specs/addons/Ref/fixtures.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* eslint-disable react/no-multi-comp */
2+
import React, { Component } from 'react'
3+
4+
export const DOMFunction = (props) => <div {...props} id='node' />
5+
6+
export const CompositeFunction = (props) => <DOMFunction {...props} />
7+
8+
export class DOMClass extends Component {
9+
render() {
10+
return <div {...this.props} id='node' />
11+
}
12+
}
13+
14+
export class CompositeClass extends Component {
15+
render() {
16+
return <DOMClass {...this.props} />
17+
}
18+
}

0 commit comments

Comments
 (0)