Skip to content

Commit

Permalink
[added] Breadcrumb component
Browse files Browse the repository at this point in the history
  • Loading branch information
ycdesu authored and AlexKVal committed Sep 25, 2015
1 parent 3edf4a1 commit 3c710f9
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/examples/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"Accordion",
"Alert",
"Badge",
"Breadcrumb",
"BreadcrumbItem",
"Button",
"ButtonGroup",
"ButtonInput",
Expand Down
15 changes: 15 additions & 0 deletions docs/examples/Breadcrumb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const breadcrumbInstance = (
<Breadcrumb>
<BreadcrumbItem href="#">
Home
</BreadcrumbItem>
<BreadcrumbItem href="http://getbootstrap.com/components/#breadcrumbs">
Library
</BreadcrumbItem>
<BreadcrumbItem>
Data
</BreadcrumbItem>
</Breadcrumb>
);

React.render(breadcrumbInstance, mountNode);
18 changes: 18 additions & 0 deletions docs/src/ComponentsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,23 @@ const ComponentsPage = React.createClass({
<PropTable component="Navbar"/>
</div>

{/* Breadcrumb */}
<div className="bs-docs-section">
<h1 className="page-header"><Anchor id="breadcrumbs">Breadcrumbs</Anchor> <small>Breadcrumb, BreadcrumbItems</small></h1>
<p>Breadcrumbs are used to indicate the current page's location. An <code>active</code> class is added to a <code>BreadcrumbItem</code> if there's no <code>href</code> property for it.</p>

<h3><Anchor id="breadcrumbs-example">Breadcrumbs Example</Anchor></h3>
<ReactPlayground codeText={Samples.Breadcrumb} />

<h3><Anchor id="breadcrumbs-props">Props</Anchor></h3>

<h4><Anchor id="breadcrumbs-props-breadcrumb">Breadcrumb</Anchor></h4>
<PropTable component="Breadcrumb"/>

<h4><Anchor id="breadcrumbs-props-breadcrumbItem">BreadcrumbItem</Anchor></h4>
<PropTable component="BreadcrumbItem"/>
</div>

{/* Tabbed Areas */}
<div className="bs-docs-section">
<h1 className="page-header"><Anchor id="tabs">Togglable tabs</Anchor> <small>Tabs, Tab</small></h1>
Expand Down Expand Up @@ -947,6 +964,7 @@ const ComponentsPage = React.createClass({
<NavItem href="#progress" key={8}>Progress bars</NavItem>
<NavItem href="#navs" key={9}>Navs</NavItem>
<NavItem href="#navbars" key={10}>Navbars</NavItem>
<NavItem href="#breadcrumbs" key={29}>Breadcrumbs</NavItem>
<NavItem href="#tabs" key={11}>Tabs</NavItem>
<NavItem href="#pager" key={12}>Pager</NavItem>
<NavItem href="#pagination" key={13}>Pagination</NavItem>
Expand Down
2 changes: 2 additions & 0 deletions docs/src/ReactPlayground.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const React = require('react');
const Accordion = require('../../src/Accordion');
const Alert = require('../../src/Alert');
const Badge = require('../../src/Badge');
const Breadcrumb = require('../../src/Breadcrumb');
const BreadcrumbItem = require('../../src/BreadcrumbItem');
const Button = require('../../src/Button');
const ButtonGroup = require('../../src/ButtonGroup');
const ButtonInput = require('../../src/ButtonInput');
Expand Down
1 change: 1 addition & 0 deletions docs/src/Samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default {
Collapse: require('fs').readFileSync(__dirname + '/../examples/Collapse.js', 'utf8'),
Fade: require('fs').readFileSync(__dirname + '/../examples/Fade.js', 'utf8'),

Breadcrumb: require('fs').readFileSync(__dirname + '/../examples/Breadcrumb.js', 'utf8'),
ButtonTypes: require('fs').readFileSync(__dirname + '/../examples/ButtonTypes.js', 'utf8'),
ButtonSizes: require('fs').readFileSync(__dirname + '/../examples/ButtonSizes.js', 'utf8'),
ButtonBlock: require('fs').readFileSync(__dirname + '/../examples/ButtonBlock.js', 'utf8'),
Expand Down
37 changes: 37 additions & 0 deletions src/Breadcrumb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { cloneElement } from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
import ValidComponentChildren from './utils/ValidComponentChildren';

const Breadcrumb = React.createClass({
mixins: [BootstrapMixin],

getDefaultProps() {
return {
bsClass: 'breadcrumb'
};
},

render() {
const classes = this.getBsClassSet();
const { className, ...props } = this.props;

return (
<ol {...props} role="navigation" aria-label="breadcrumbs" className={classNames(className, classes)}>
{ValidComponentChildren.map(this.props.children, this.renderBreadcrumbItem)}
</ol>
);
},

renderBreadcrumbItem(child, index) {
return cloneElement(
child,
{
key: child.key ? child.key : index,
navItem: true
}
);
}
});

export default Breadcrumb;
61 changes: 61 additions & 0 deletions src/BreadcrumbItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import classNames from 'classnames';
import BootstrapMixin from './BootstrapMixin';
import SafeAnchor from './SafeAnchor';
import warning from 'react/lib/warning';

const BreadcrumbItem = React.createClass({
mixins: [BootstrapMixin],

propTypes: {
id: React.PropTypes.string,
active: React.PropTypes.bool,
linkId: React.PropTypes.string,
href: React.PropTypes.string,
title: React.PropTypes.node,
target: React.PropTypes.string
},

getDefaultProps() {
return {
active: false,
};
},

render() {
warning(!(this.props.href && this.props.active), '[react-bootstrap] href and active properties cannot be set at the same time');

const {
id,
active,
linkId,
children,
href,
title,
target,
...props } = this.props;
const classes = { active };
const linkProps = {
href,
title,
target,
id: linkId
};

return (
<li id={id} className={classNames(props.className, classes)}>
{
active ?
<span {...props}>
{ children }
</span> :
<SafeAnchor {...props} {...linkProps}>
{ children }
</SafeAnchor>
}
</li>
);
}
});

export default BreadcrumbItem;
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export Button from './Button';
export ButtonGroup from './ButtonGroup';
export ButtonInput from './ButtonInput';
export ButtonToolbar from './ButtonToolbar';
export Breadcrumb from './Breadcrumb';
export BreadcrumbItem from './BreadcrumbItem';
export Carousel from './Carousel';
export CarouselItem from './CarouselItem';
export Col from './Col';
Expand Down
1 change: 1 addition & 0 deletions src/styleMaps.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const styleMaps = {
CLASSES: {
'alert': 'alert',
'breadcrumb': 'breadcrumb',
'button': 'btn',
'button-group': 'btn-group',
'button-toolbar': 'btn-toolbar',
Expand Down
137 changes: 137 additions & 0 deletions test/BreadcrumbItemSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React from 'react';
import ReactTestUtils from 'react/lib/ReactTestUtils';
import BreadcrumbItem from '../src/BreadcrumbItem';

describe('BreadcrumbItem', function () {
it('Should add active class', function () {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem active>
Active Crumb
</BreadcrumbItem>
);

assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'active'));
});

it('Should not add active class', function () {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem>
Crumb
</BreadcrumbItem>
);

let liNode = React.findDOMNode(instance);
assert.notInclude(liNode.className, 'active');
});

it('Should add custom classes', function () {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem className="custom-one custom-two" active>
Active Crumb
</BreadcrumbItem>
);

let liNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'active'));

let classes = liNode.className;
assert.include(classes, 'active');
assert.include(classes, 'custom-one');
assert.include(classes, 'custom-two');
});

it('Should spread props onto an active item', function() {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem herpa='derpa' active>
Active Crumb
</BreadcrumbItem>
);

let spanNode = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'span');

spanNode.props.herpa.should.equal('derpa');
});

it('Should spread props onto anchor', function(done) {
const handleClick = () => {
done();
};

let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem href='#' onClick={handleClick} herpa='derpa'>
Crumb 1
</BreadcrumbItem>
);

let anchorNode = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'a');
ReactTestUtils.Simulate.click(anchorNode);

anchorNode.props.herpa.should.equal('derpa');
});

it('Should add id for li element', function() {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem href='#' id='test-li-id'>
Crumb 1
</BreadcrumbItem>
);

let liNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'li'));
assert.equal(liNode.id, 'test-li-id');
});

it('Should add linkId', function() {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem href='#' linkId='test-link-id'>
Crumb 1
</BreadcrumbItem>
);

let linkNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'a'));
assert.equal(linkNode.id, 'test-link-id');
});

it('Should add href', function() {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem href='http://getbootstrap.com/components/#breadcrumbs'>
Crumb 1
</BreadcrumbItem>
);

let linkNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'a'));
assert.equal(linkNode.href, 'http://getbootstrap.com/components/#breadcrumbs');
});

it('Should have a title', function() {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem title='test-title' href='http://getbootstrap.com/components/#breadcrumbs'>
Crumb 1
</BreadcrumbItem>
);

let linkNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'a'));
assert.equal(linkNode.title, 'test-title');
});

it('Should not add anchor properties to li', function() {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem title='test-title' href='/hi'>
Crumb 1
</BreadcrumbItem>
);

let liNode = React.findDOMNode(instance);
assert.notOk(liNode.hasAttribute('href'));
assert.notOk(liNode.hasAttribute('title'));
});

it('Should set target attribute on anchor', function () {
let instance = ReactTestUtils.renderIntoDocument(
<BreadcrumbItem target='_blank' href='http://getbootstrap.com/components/#breadcrumbs'>
Crumb 1
</BreadcrumbItem>
);

let linkNode = React.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'a'));
assert.equal(linkNode.target, '_blank');
});
});

0 comments on commit 3c710f9

Please sign in to comment.