Skip to content

Commit

Permalink
feat(Ref): add component (#2142)
Browse files Browse the repository at this point in the history
* feat(Ref): add component

(cherry picked from commit 48c6b57)

* feat(Ref): add component

(cherry picked from commit 48c6b57)

* feat(Ref): add component

(cherry picked from commit 48c6b57)
  • Loading branch information
layershifter authored and levithomason committed Sep 30, 2017
1 parent 50ce0ef commit 5a37f28
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {
export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm';
export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal';
export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio';
export { default as Ref, RefProps } from './dist/commonjs/addons/Ref';
export { default as Select, SelectProps } from './dist/commonjs/addons/Select';
export { default as TextArea, TextAreaProps } from './dist/commonjs/addons/TextArea';

Expand Down
20 changes: 20 additions & 0 deletions src/addons/Ref/Ref.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';

export interface RefProps {
[key: string]: any;

/** Primary content. */
children?: React.ReactNode;

/**
* Called when componentDidMount.
*
* @param {HTMLElement} node - Referred node.
*/
innerRef?: (node: HTMLElement) => void;
}

declare class Ref extends React.Component<RefProps, {}> {
}

export default Ref;
43 changes: 43 additions & 0 deletions src/addons/Ref/Ref.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import PropTypes from 'prop-types'
import { Children, Component } from 'react'
import { findDOMNode } from 'react-dom'

import { TYPES } from '../../lib/META'

/**
* This component exposes a callback prop that always returns the DOM node of both functional and class component
* children.
*/
export default class Ref extends Component {
static propTypes = {
/** Primary content. */
children: PropTypes.element,

/**
* Called when componentDidMount.
*
* @param {HTMLElement} node - Referred node.
*/
innerRef: PropTypes.func,
}

static _meta = {
name: 'Ref',
type: TYPES.ADDON,
}

componentDidMount() {
const { innerRef } = this.props

// Heads up! Don't move this condition, it's a short circle that avoids run of `findDOMNode`
// if `innerRef` isn't passed
// eslint-disable-next-line react/no-find-dom-node
if (innerRef) innerRef(findDOMNode(this))
}

render() {
const { children } = this.props

return Children.only(children)
}
}
1 change: 1 addition & 0 deletions src/addons/Ref/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default, RefProps } from './Ref';
1 change: 1 addition & 0 deletions src/addons/Ref/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default from './Ref'
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { default as Responsive } from './addons/Responsive'
export { default as Confirm } from './addons/Confirm'
export { default as Portal } from './addons/Portal'
export { default as Radio } from './addons/Radio'
export { default as Ref } from './addons/Ref'
export { default as Select } from './addons/Select'
export { default as TextArea } from './addons/TextArea'

Expand Down
64 changes: 64 additions & 0 deletions test/specs/addons/Ref/Ref-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import faker from 'faker'
import React from 'react'

import Ref from 'src/addons/Ref/Ref'
import * as common from 'test/specs/commonTests'
import { sandbox } from 'test/utils'
import { CompositeClass, CompositeFunction, DOMClass, DOMFunction } from './fixtures'

const mountNode = (Component, innerRef) => (
mount(
<Ref innerRef={innerRef}>
<Component />
</Ref>,
)
.find('#node')
.getDOMNode()
)

describe('Ref', () => {
common.hasValidTypings(Ref)

describe('children', () => {
it('renders single child', () => {
const child = <div data-child={faker.hacker.noun()} />

shallow(<Ref>{child}</Ref>)
.should.contain(child)
})
})

describe('innerRef', () => {
it('returns node from a functional component with DOM node', () => {
const innerRef = sandbox.spy()
const node = mountNode(DOMFunction, innerRef)

innerRef.should.have.been.calledOnce()
innerRef.should.have.been.calledWithMatch(node)
})

it('returns node from a functional component', () => {
const innerRef = sandbox.spy()
const node = mountNode(CompositeFunction, innerRef)

innerRef.should.have.been.calledOnce()
innerRef.should.have.been.calledWithMatch(node)
})

it('returns node from a class component with DOM node', () => {
const innerRef = sandbox.spy()
const node = mountNode(DOMClass, innerRef)

innerRef.should.have.been.calledOnce()
innerRef.should.have.been.calledWithMatch(node)
})

it('returns node from a class component', () => {
const innerRef = sandbox.spy()
const node = mountNode(CompositeClass, innerRef)

innerRef.should.have.been.calledOnce()
innerRef.should.have.been.calledWithMatch(node)
})
})
})
19 changes: 19 additions & 0 deletions test/specs/addons/Ref/fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable react/no-multi-comp */
/* eslint-disable react/prefer-stateless-function */
import React, { Component } from 'react'

export const DOMFunction = props => <div {...props} id='node' />

export const CompositeFunction = props => <DOMFunction {...props} />

export class DOMClass extends Component {
render() {
return <div {...this.props} id='node' />
}
}

export class CompositeClass extends Component {
render() {
return <DOMClass {...this.props} />
}
}

0 comments on commit 5a37f28

Please sign in to comment.