Skip to content

Commit

Permalink
Merge pull request react-bootstrap#1948 from react-bootstrap/dropdown…
Browse files Browse the repository at this point in the history
…-refs

Allow function refs on dropdowns
  • Loading branch information
jquense authored Jun 13, 2016
2 parents c0e02fe + 769f210 commit a74a4ae
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 9 deletions.
42 changes: 33 additions & 9 deletions src/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import all from 'react-prop-types/lib/all';
import elementType from 'react-prop-types/lib/elementType';
import isRequiredForA11y from 'react-prop-types/lib/isRequiredForA11y';
import uncontrollable from 'uncontrollable';
import warning from 'warning';

import { prefix } from './utils/bootstrapUtils';
import createChainedFunction from './utils/createChainedFunction';
Expand All @@ -20,7 +21,6 @@ import ButtonGroup from './ButtonGroup';
import DropdownMenu from './DropdownMenu';
import DropdownToggle from './DropdownToggle';

const TOGGLE_REF = 'toggle-btn';
const TOGGLE_ROLE = DropdownToggle.defaultProps.bsRole;
const MENU_ROLE = DropdownMenu.defaultProps.bsRole;

Expand Down Expand Up @@ -63,7 +63,7 @@ class Dropdown extends React.Component {
componentWillUpdate(nextProps) {
if (!nextProps.open && this.props.open) {
this._focusInDropdown = contains(
ReactDOM.findDOMNode(this.refs.menu),
ReactDOM.findDOMNode(this.menu),
activeElement(document)
);
}
Expand Down Expand Up @@ -137,8 +137,8 @@ class Dropdown extends React.Component {
case keycode.codes.down:
if (!this.props.open) {
this.toggleOpen('keydown');
} else if (this.refs.menu.focusNext) {
this.refs.menu.focusNext();
} else if (this.menu.focusNext) {
this.menu.focusNext();
}
event.preventDefault();
break;
Expand All @@ -159,7 +159,8 @@ class Dropdown extends React.Component {
}

focusNextOnOpen() {
const {menu} = this.refs;
const menu = this.menu;

if (!menu.focusNext) {
return;
}
Expand All @@ -173,7 +174,7 @@ class Dropdown extends React.Component {
}

focus() {
let toggle = ReactDOM.findDOMNode(this.refs[TOGGLE_REF]);
let toggle = ReactDOM.findDOMNode(this.toggle);

if (toggle && toggle.focus) {
toggle.focus();
Expand Down Expand Up @@ -201,8 +202,20 @@ class Dropdown extends React.Component {
}

refineMenu(menu, open) {
let ref = r => this.menu = r;

if (typeof menu.ref === 'string') {
warning(false,
'String refs are not supported on `<Dropdown.Menu>` components. ' +
'To apply a ref to the component use the callback signature: \n\n ' +
'https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute'
);
} else {
ref = createChainedFunction(menu.ref, ref);
}

const menuProps = {
ref: 'menu',
ref,
open,
labelledBy: this.props.id,
pullRight: this.props.pullRight,
Expand All @@ -225,10 +238,22 @@ class Dropdown extends React.Component {
}

refineToggle(toggle, open) {
let ref = r => this.toggle = r;

if (typeof toggle.ref === 'string') {
warning(false,
'String refs are not supported on `<Dropdown.Toggle>` components. ' +
'To apply a ref to the component use the callback signature: \n\n ' +
'https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute'
);
} else {
ref = createChainedFunction(toggle.ref, ref);
}

let toggleProps = {
ref,
open,
id: this.props.id,
ref: TOGGLE_REF,
role: this.props.role
};

Expand All @@ -248,7 +273,6 @@ class Dropdown extends React.Component {

Dropdown.Toggle = DropdownToggle;

Dropdown.TOGGLE_REF = TOGGLE_REF;
Dropdown.TOGGLE_ROLE = TOGGLE_ROLE;
Dropdown.MENU_ROLE = MENU_ROLE;

Expand Down
37 changes: 37 additions & 0 deletions test/DropdownSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,43 @@ describe('Dropdown', () => {

});

it('chains refs', () => {
class RefDropdown extends React.Component {
render() {
return (
<Dropdown ref={dropdown => this.dropdown = dropdown.refs.inner} id="test">
<Dropdown.Toggle ref={toggle => this.toggle = toggle} />
<Dropdown.Menu ref={menu => this.menu = menu} />
</Dropdown>
);
}
}

let inst = tsp(<RefDropdown />).render().unwrap();

inst.menu.should.exist;
inst.dropdown.menu.should.exist;

inst.toggle.should.exist;
inst.dropdown.toggle.should.exist;
});

it('warns when a string ref is specified', () => {
class RefDropdown extends React.Component {
render() {
return (
<Dropdown id="test">
<Dropdown.Toggle ref='toggle' />
<Dropdown.Menu />
</Dropdown>
);
}
}

shouldWarn('String refs are not supported');

tsp(<RefDropdown />).render().unwrap();
});

describe('focusable state', () => {
let focusableContainer;
Expand Down

0 comments on commit a74a4ae

Please sign in to comment.