Skip to content

Commit

Permalink
feat(DropdownItem): replace react-bootstrap DropdownItem
Browse files Browse the repository at this point in the history
* only render anchor tag if href prop exists
* allow user-provided components with anchors

[#114813379]

Signed-off-by: Dan Dzoan <ddzoan@pivotal.io>
  • Loading branch information
elenasharma authored and pivotal committed Mar 3, 2016
1 parent 824c0f7 commit 442e71a
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 38 deletions.
115 changes: 115 additions & 0 deletions library/spec/pivotal-ui-react/dropdown/dropdown-item_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {DropdownItem} from '../../../src/pivotal-ui-react/dropdowns/dropdowns';

describe('DropdownItem', () => {
let props = {
className: 'test-item-class',
id: 'test-item-id',
style: {
opacity: '1'
}
};

afterEach(() => {
ReactDOM.unmountComponentAtNode(root);
});

it('passes through header', () => {
ReactDOM.render(
<DropdownItem header>HeadText</DropdownItem>,
root);
expect('#root li.dropdown-header').toContainText('HeadText');
});

it('passes through divider', () => {
ReactDOM.render(
<DropdownItem divider />,
root);
expect('#root li.divider').toExist();
});

it('passes through className to the li ', () => {
ReactDOM.render(
<DropdownItem {...props}>Item</DropdownItem>,
root);
expect('#root li').toHaveClass(props.className);
});

it('passes through style to the li', () => {
ReactDOM.render(
<DropdownItem {...props}>Item</DropdownItem>,
root);
expect('#root li').toHaveCss(props.style);
});

describe('href', () => {
it('passes through id to the anchor', () => {
ReactDOM.render(
<DropdownItem href='test' {...props}>Item</DropdownItem>,
root);
expect('#root li a#test-item-id').toExist();
});
});

describe('onSelect handling', () => {
let handleSelectSpy;
describe('with href', () => {
it('passes through onSelect with eventKey', () => {
handleSelectSpy = jasmine.createSpy('handleSelect');
const eventKey = '1';
ReactDOM.render(
<DropdownItem href="/whatever" onSelect={handleSelectSpy} eventKey={eventKey}>Item</DropdownItem>,
root);

$('#root li a').simulate('click');
expect(handleSelectSpy).toHaveBeenCalled();
});
});

describe('without href', () => {
it('passes through onSelect with eventKey', () => {
handleSelectSpy = jasmine.createSpy('handleSelect');
const eventKey = '1';
ReactDOM.render(
<DropdownItem onSelect={handleSelectSpy} eventKey={eventKey}>Item</DropdownItem>,
root);

$('#root li').simulate('click');
expect(handleSelectSpy).toHaveBeenCalled();
});
});

describe('with disabled prop', () => {
it('does not call onSelect handler', () => {
handleSelectSpy = jasmine.createSpy('handleSelect');
ReactDOM.render(
<DropdownItem disabled href="/whatever" onSelect={handleSelectSpy}>Item</DropdownItem>,
root);
expect('#root li').toHaveClass('disabled');
expect('#root li a').toHaveAttr('disabled');
$('#root li a').simulate('click');
expect(handleSelectSpy).not.toHaveBeenCalled();
});
});
});

describe('when href is not passed in as a prop', () => {
describe('when an a tag is passed in as a child', () => {
beforeEach(() => {
ReactDOM.render(
<DropdownItem><a href="custom">link</a></DropdownItem>,
root);
});

it('renders the child link', () => {
expect('#root li a').toHaveAttr('href', 'custom');
});
});

it('does not render an anchor element', () => {
ReactDOM.render(
<DropdownItem>Item</DropdownItem>,
root);
expect('#root li a').not.toExist();
});
});
});
31 changes: 0 additions & 31 deletions library/spec/pivotal-ui-react/dropdown/dropdown_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,37 +69,6 @@ describe('Dropdowns', function() {
});
});

describe('DropdownItem', function() {
var props = {
className: 'test-item-class',
id: 'test-item-id',
style: {
opacity: '1'
}
};
beforeEach(function() {
ReactDOM.render(
<DropdownItem href='test' {...props}>Item</DropdownItem>,
root);
});

afterEach(function() {
ReactDOM.unmountComponentAtNode(root);
});

it('passes through className to the li ', function() {
expect('#root li').toHaveClass(props.className);
});

it('passes through style to the li', function() {
expect('#root li').toHaveCss(props.style);
});

it('passes through id to the anchor', function() {
expect('#root li a#test-item-id').toExist();
});
});

dropdownTestFor('LinkDropdown', 'btn-link');

dropdownTestFor('DefaultAltDropdown', 'btn-default-alt');
Expand Down
66 changes: 59 additions & 7 deletions library/src/pivotal-ui-react/dropdowns/dropdowns.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import 'pui-css-dropdowns';
import 'pui-css-iconography';
import 'pui-css-button-group';

const types = React.PropTypes;

var BsDropdown = require('react-bootstrap/lib/Dropdown');

function defDropdown(props) {
return React.createClass({
propTypes: {
id: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
bsStyle: React.PropTypes.any,
buttonClassName: React.PropTypes.string,
style: React.PropTypes.any,
title: React.PropTypes.any,
border: React.PropTypes.bool
id: types.oneOfType([types.string, types.number]),
bsStyle: types.any,
buttonClassName: types.string,
style: types.any,
title: types.any,
border: types.bool
},
render: function render() {
const {buttonClassName, style, title, children, border, ...others} = this.props;
Expand All @@ -41,10 +43,60 @@ function defDropdown(props) {
});
}

const DropdownItem = React.createClass({
propTypes: {
className: types.string,
style: types.object,
href: types.string,
id: types.string,
header: types.bool,
divider: types.bool,
disabled: types.bool,
eventKey: types.string,
onSelect: types.func
},

handleClick(event) {
const {href, disabled, onSelect, eventKey} = this.props;

if (!href || disabled) {
event.preventDefault();
}

if (disabled) return;

if (onSelect) {
onSelect(event, eventKey);
}
},

render() {
const {children, className, style, href, id, header, divider, disabled} = this.props;

if (header) return (<li role="heading" className="dropdown-header">{children}</li>);
if (divider) return (<li role="separator" className="divider"/>);

let anchor;
if (href) {
anchor = <a {...{href, id}} disabled={disabled} onClick={this.handleClick}>{children}</a>;
} else {
anchor = children;
}

const disabledClass = disabled ? 'disabled' : '';
const dropdownItemClass = classnames(className, disabledClass);
return (
<li {...{style}} className={dropdownItemClass} onClick={href ? '' : this.handleClick}>
{anchor}
</li>
);
}
});

module.exports = {
Dropdown: defDropdown({}),

DropdownItem: require('react-bootstrap/lib/MenuItem'),
DropdownItem: DropdownItem,

LinkDropdown: defDropdown({bsStyle: 'link'}),

Expand Down

0 comments on commit 442e71a

Please sign in to comment.