Setup

-

You can import the lib with as AMD modules, CommonJS modules as a global JS script.

+

You can import the lib as AMD modules, CommonJS modules, or as a global JS script.

First add the Bootstrap CSS to your project; check here if you have not already done that. Then:

From de6f7dd6476c0287cabc7feeb160ce5b6b7e2396 Mon Sep 17 00:00:00 2001 From: Joe McBride Date: Mon, 2 Mar 2015 16:50:00 -0800 Subject: [PATCH 22/44] [fixed] CollapsableMixin fixed size * Fixes expand/collapse animation for stand-alone panel * Fixes #399, where the panel would stay a fixed size * Added basic CollapsableParagraph example * Added aria-expanded attributes to Panel Fixes #399 --- docs/examples/CollapsableParagraph.js | 37 +++++ docs/src/ComponentsPage.js | 4 + docs/src/ReactPlayground.js | 1 + src/CollapsableMixin.js | 166 ++++++++++++------- src/Panel.jsx | 54 ++++--- src/PanelGroup.jsx | 4 +- test/CollapsableMixinSpec.jsx | 220 ++++++++++++++++++++++++++ test/PanelSpec.jsx | 24 ++- 8 files changed, 424 insertions(+), 86 deletions(-) create mode 100644 docs/examples/CollapsableParagraph.js create mode 100644 test/CollapsableMixinSpec.jsx diff --git a/docs/examples/CollapsableParagraph.js b/docs/examples/CollapsableParagraph.js new file mode 100644 index 0000000000..5725be5baf --- /dev/null +++ b/docs/examples/CollapsableParagraph.js @@ -0,0 +1,37 @@ +var CollapsableParagraph = React.createClass({ + mixins: [CollapsableMixin], + + getCollapsableDOMNode: function(){ + return this.refs.panel.getDOMNode(); + }, + + getCollapsableDimensionValue: function(){ + return this.refs.panel.getDOMNode().scrollHeight; + }, + + onHandleToggle: function(e){ + e.preventDefault(); + this.setState({expanded:!this.state.expanded}); + }, + + render: function(){ + var styles = this.getCollapsableClassSet(); + var text = this.isExpanded() ? 'Hide' : 'Show'; + return ( +
+ +
+ {this.props.children} +
+
+ ); + } +}); + +var panelInstance = ( + + Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. + +); + +React.render(panelInstance, mountNode); diff --git a/docs/src/ComponentsPage.js b/docs/src/ComponentsPage.js index bd55a8e2ca..7a611b42c6 100644 --- a/docs/src/ComponentsPage.js +++ b/docs/src/ComponentsPage.js @@ -207,6 +207,10 @@ var ComponentsPage = React.createClass({

Accordions

<Accordion /> aliases <PanelGroup accordion />.

+ +

Collapsable Mixin

+

CollapsableMixin can be used to create your own components with collapse functionality.

+
diff --git a/docs/src/ReactPlayground.js b/docs/src/ReactPlayground.js index 7ff19f1b02..8a23add10d 100644 --- a/docs/src/ReactPlayground.js +++ b/docs/src/ReactPlayground.js @@ -8,6 +8,7 @@ var Badge = require('../../lib/Badge'); var Button = require('../../lib/Button'); var ButtonGroup = require('../../lib/ButtonGroup'); var ButtonToolbar = require('../../lib/ButtonToolbar'); +var CollapsableMixin = require('../../lib/CollapsableMixin'); var Carousel = require('../../lib/Carousel'); var CarouselItem = require('../../lib/CarouselItem'); var Col = require('../../lib/Col'); diff --git a/src/CollapsableMixin.js b/src/CollapsableMixin.js index 6cd877de33..201ed69fe4 100644 --- a/src/CollapsableMixin.js +++ b/src/CollapsableMixin.js @@ -1,101 +1,149 @@ var React = require('react'); -var TransitionEvents = require('./utils/TransitionEvents'); +var TransitionEvents = require('react/lib/ReactTransitionEvents'); var CollapsableMixin = { propTypes: { - collapsable: React.PropTypes.bool, defaultExpanded: React.PropTypes.bool, expanded: React.PropTypes.bool }, - getInitialState: function () { + getInitialState: function(){ + var defaultExpanded = this.props.defaultExpanded != null ? + this.props.defaultExpanded : + this.props.expanded != null ? + this.props.expanded : + false; + return { - expanded: this.props.defaultExpanded != null ? this.props.defaultExpanded : null, + expanded: defaultExpanded, collapsing: false }; }, - handleTransitionEnd: function () { - this._collapseEnd = true; - this.setState({ - collapsing: false - }); - }, - - componentWillReceiveProps: function (newProps) { - if (this.props.collapsable && newProps.expanded !== this.props.expanded) { - this._collapseEnd = false; - this.setState({ - collapsing: true - }); + componentWillUpdate: function(nextProps, nextState){ + var willExpanded = nextProps.expanded != null ? nextProps.expanded : nextState.expanded; + if (willExpanded === this.isExpanded()) { + return; } - }, - _addEndTransitionListener: function () { + // if the expanded state is being toggled, ensure node has a dimension value + // this is needed for the animation to work and needs to be set before + // the collapsing class is applied (after collapsing is applied the in class + // is removed and the node's dimension will be wrong) + var node = this.getCollapsableDOMNode(); + var dimension = this.dimension(); + var value = '0'; - if (node) { - TransitionEvents.addEndEventListener( - node, - this.handleTransitionEnd - ); + if(!willExpanded){ + value = this.getCollapsableDimensionValue(); } + + node.style[dimension] = value + 'px'; + + this._afterWillUpdate(); }, - _removeEndTransitionListener: function () { - var node = this.getCollapsableDOMNode(); + componentDidUpdate: function(prevProps, prevState){ + // check if expanded is being toggled; if so, set collapsing + this._checkToggleCollapsing(prevProps, prevState); - if (node) { - TransitionEvents.removeEndEventListener( - node, - this.handleTransitionEnd - ); - } + // check if collapsing was turned on; if so, start animation + this._checkStartAnimation(); + }, + + // helps enable test stubs + _afterWillUpdate: function(){ }, - componentDidMount: function () { - this._afterRender(); + _checkStartAnimation: function(){ + if(!this.state.collapsing) { + return; + } + + var node = this.getCollapsableDOMNode(); + var dimension = this.dimension(); + var value = this.getCollapsableDimensionValue(); + + // setting the dimension here starts the transition animation + var result; + if(this.isExpanded()) { + result = value + 'px'; + } else { + result = '0px'; + } + node.style[dimension] = result; }, - componentWillUnmount: function () { - this._removeEndTransitionListener(); + _checkToggleCollapsing: function(prevProps, prevState){ + var wasExpanded = prevProps.expanded != null ? prevProps.expanded : prevState.expanded; + var isExpanded = this.isExpanded(); + if(wasExpanded !== isExpanded){ + if(wasExpanded) { + this._handleCollapse(); + } else { + this._handleExpand(); + } + } }, - componentWillUpdate: function (nextProps) { - var dimension = (typeof this.getCollapsableDimension === 'function') ? - this.getCollapsableDimension() : 'height'; + _handleExpand: function(){ var node = this.getCollapsableDOMNode(); + var dimension = this.dimension(); + + var complete = (function (){ + this._removeEndEventListener(node, complete); + // remove dimension value - this ensures the collapsable item can grow + // in dimension after initial display (such as an image loading) + node.style[dimension] = ''; + this.setState({ + collapsing:false + }); + }).bind(this); + + this._addEndEventListener(node, complete); - this._removeEndTransitionListener(); + this.setState({ + collapsing: true + }); }, - componentDidUpdate: function (prevProps, prevState) { - this._afterRender(); + _handleCollapse: function(){ + var node = this.getCollapsableDOMNode(); + + var complete = (function (){ + this._removeEndEventListener(node, complete); + this.setState({ + collapsing: false + }); + }).bind(this); + + this._addEndEventListener(node, complete); + + this.setState({ + collapsing: true + }); }, - _afterRender: function () { - if (!this.props.collapsable) { - return; - } + // helps enable test stubs + _addEndEventListener: function(node, complete){ + TransitionEvents.addEndEventListener(node, complete); + }, - this._addEndTransitionListener(); - setTimeout(this._updateDimensionAfterRender, 0); + // helps enable test stubs + _removeEndEventListener: function(node, complete){ + TransitionEvents.removeEndEventListener(node, complete); }, - _updateDimensionAfterRender: function () { - var node = this.getCollapsableDOMNode(); - if (node) { - var dimension = (typeof this.getCollapsableDimension === 'function') ? - this.getCollapsableDimension() : 'height'; - node.style[dimension] = this.isExpanded() ? - this.getCollapsableDimensionValue() + 'px' : '0px'; - } + dimension: function(){ + return (typeof this.getCollapsableDimension === 'function') ? + this.getCollapsableDimension() : + 'height'; }, - isExpanded: function () { - return (this.props.expanded != null) ? - this.props.expanded : this.state.expanded; + isExpanded: function(){ + return this.props.expanded != null ? this.props.expanded : this.state.expanded; }, getCollapsableClassSet: function (className) { diff --git a/src/Panel.jsx b/src/Panel.jsx index 703ba75bed..36ed087612 100644 --- a/src/Panel.jsx +++ b/src/Panel.jsx @@ -10,6 +10,7 @@ var Panel = React.createClass({ mixins: [BootstrapMixin, CollapsableMixin], propTypes: { + collapsable: React.PropTypes.bool, onSelect: React.PropTypes.func, header: React.PropTypes.node, footer: React.PropTypes.node, @@ -23,22 +24,22 @@ var Panel = React.createClass({ }; }, - handleSelect: function (e) { + handleSelect: function(e){ + e.selected = true; + if (this.props.onSelect) { - this._isChanging = true; - this.props.onSelect(this.props.eventKey); - this._isChanging = false; + this.props.onSelect(e, this.props.eventKey); + } else { + e.preventDefault(); } - e.preventDefault(); - - this.setState({ - expanded: !this.state.expanded - }); + if (e.selected) { + this.handleToggle(); + } }, - shouldComponentUpdate: function () { - return !this._isChanging; + handleToggle: function(){ + this.setState({expanded:!this.state.expanded}); }, getCollapsableDimensionValue: function () { @@ -69,7 +70,11 @@ var Panel = React.createClass({ renderCollapsableBody: function () { return ( -
+
{this.renderBody()}
); @@ -78,6 +83,7 @@ var Panel = React.createClass({ renderBody: function () { var allChildren = this.props.children; var bodyElements = []; + var panelBodyChildren = []; function getProps() { return {key: bodyElements.length}; @@ -95,24 +101,23 @@ var Panel = React.createClass({ ); } + function maybeRenderPanelBody () { + if (panelBodyChildren.length === 0) { + return; + } + + addPanelBody(panelBodyChildren); + panelBodyChildren = []; + } + // Handle edge cases where we should not iterate through children. - if (!Array.isArray(allChildren) || allChildren.length == 0) { + if (!Array.isArray(allChildren) || allChildren.length === 0) { if (this.shouldRenderFill(allChildren)) { addPanelChild(allChildren); } else { addPanelBody(allChildren); } } else { - var panelBodyChildren = []; - - function maybeRenderPanelBody () { - if (panelBodyChildren.length == 0) { - return; - } - - addPanelBody(panelBodyChildren); - panelBodyChildren = []; - } allChildren.forEach(function(child) { if (this.shouldRenderFill(child)) { @@ -132,7 +137,7 @@ var Panel = React.createClass({ }, shouldRenderFill: function (child) { - return React.isValidElement(child) && child.props.fill != null + return React.isValidElement(child) && child.props.fill != null; }, renderHeading: function () { @@ -168,6 +173,7 @@ var Panel = React.createClass({ {header} diff --git a/src/PanelGroup.jsx b/src/PanelGroup.jsx index eb177b4a4d..1c0d4b1b07 100644 --- a/src/PanelGroup.jsx +++ b/src/PanelGroup.jsx @@ -66,7 +66,9 @@ var PanelGroup = React.createClass({ return !this._isChanging; }, - handleSelect: function (key) { + handleSelect: function (e, key) { + e.preventDefault(); + if (this.props.onSelect) { this._isChanging = true; this.props.onSelect(key); diff --git a/test/CollapsableMixinSpec.jsx b/test/CollapsableMixinSpec.jsx new file mode 100644 index 0000000000..1c613ea800 --- /dev/null +++ b/test/CollapsableMixinSpec.jsx @@ -0,0 +1,220 @@ +/*global describe, it, assert */ + +var React = require('react'); +var ReactTestUtils = require('react/lib/ReactTestUtils'); +var CollapsableMixin = require('../lib/CollapsableMixin'); +var classSet = require('../lib/utils/classSet'); + +describe('CollapsableMixin', function () { + + var Component, instance; + + beforeEach(function(){ + Component = React.createClass({ + mixins: [CollapsableMixin], + + getCollapsableDOMNode: function(){ + return this.refs.panel.getDOMNode(); + }, + + getCollapsableDimensionValue: function(){ + return 15; + }, + + render: function(){ + var styles = this.getCollapsableClassSet(); + return ( +
+
+ {this.props.children} +
+
+ ); + } + }); + }); + + describe('getInitialState', function(){ + it('Should check defaultExpanded', function () { + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + var state = instance.getInitialState(); + assert.ok(state.expanded === true); + }); + + it('Should default collapsing to false', function () { + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + var state = instance.getInitialState(); + assert.ok(state.collapsing === false); + }); + }); + + describe('collapsed', function(){ + it('Should have collapse class', function () { + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'collapse')); + }); + }); + + describe('from collapsed to expanded', function(){ + beforeEach(function(){ + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + }); + + it('Should have collapsing class', function () { + instance.setProps({expanded:true}); + var node = instance.getCollapsableDOMNode(); + assert.equal(node.className, 'collapsing'); + }); + + it('Should set initial 0px height', function () { + var node = instance.getCollapsableDOMNode(); + assert.equal(node.style['height'], ''); + + instance._afterWillUpdate = function(){ + assert.equal(node.style['height'], '0px'); + }; + + instance.setProps({expanded:true}); + }); + + it('Should set transition to height', function () { + var node = instance.getCollapsableDOMNode(); + assert.equal(node.style['height'], ''); + + instance.setProps({expanded:true}); + assert.equal(node.style['height'], '15px'); + }); + + it('Should transition from collapsing to not collapsing', function (done) { + instance._addEndEventListener = function(node, complete){ + setTimeout(function(){ + complete(); + assert.ok(!instance.state.collapsing); + done(); + }, 100); + }; + instance.setProps({expanded:true}); + assert.ok(instance.state.collapsing); + }); + + it('Should clear height after transition complete', function (done) { + var node = instance.getCollapsableDOMNode(); + + instance._addEndEventListener = function(node, complete){ + setTimeout(function(){ + complete(); + assert.equal(node.style['height'], ''); + done(); + }, 100); + }; + + assert.equal(node.style['height'], ''); + instance.setProps({expanded:true}); + assert.equal(node.style['height'], '15px'); + }); + }); + + describe('from expanded to collapsed', function(){ + beforeEach(function(){ + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + }); + + it('Should have collapsing class', function () { + instance.setProps({expanded:false}); + var node = instance.getCollapsableDOMNode(); + assert.equal(node.className, 'collapsing'); + }); + + it('Should set initial height', function () { + var node = instance.getCollapsableDOMNode(); + + instance._afterWillUpdate = function(){ + assert.equal(node.style['height'], '15px'); + }; + + assert.equal(node.style['height'], ''); + instance.setProps({expanded:false}); + }); + + it('Should set transition to height', function () { + var node = instance.getCollapsableDOMNode(); + assert.equal(node.style['height'], ''); + + instance.setProps({expanded:false}); + assert.equal(node.style['height'], '0px'); + }); + + it('Should transition from collapsing to not collapsing', function (done) { + instance._addEndEventListener = function(node, complete){ + setTimeout(function(){ + complete(); + assert.ok(!instance.state.collapsing); + done(); + }, 100); + }; + instance.setProps({expanded:false}); + assert.ok(instance.state.collapsing); + }); + + it('Should have 0px height after transition complete', function (done) { + var node = instance.getCollapsableDOMNode(); + + instance._addEndEventListener = function(node, complete){ + setTimeout(function(){ + complete(); + assert.ok(node.style['height'] === '0px'); + done(); + }, 100); + }; + + assert.equal(node.style['height'], ''); + instance.setProps({expanded:false}); + assert.equal(node.style['height'], '0px'); + }); + }); + + describe('expanded', function(){ + it('Should have collapse and in class', function () { + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'collapse in')); + }); + + it('Should have collapse and in class with defaultExpanded', function () { + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'collapse in')); + }); + }); + + describe('dimension', function(){ + beforeEach(function(){ + instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + }); + + it('Defaults to height', function(){ + assert.equal(instance.dimension(), 'height'); + }); + + it('Uses getCollapsableDimension if exists', function(){ + instance.getCollapsableDimension = function(){ + return 'whatevs'; + }; + assert.equal(instance.dimension(), 'whatevs'); + }); + }); +}); diff --git a/test/PanelSpec.jsx b/test/PanelSpec.jsx index 37a93c38d0..a761242204 100644 --- a/test/PanelSpec.jsx +++ b/test/PanelSpec.jsx @@ -113,8 +113,28 @@ describe('Panel', function () { assert.ok(anchor.className.match(/\bcollapsed\b/)); }); + it('Should be aria-expanded=true', function () { + var instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + var collapse = instance.getDOMNode().querySelector('.panel-collapse'); + var anchor = instance.getDOMNode().querySelector('.panel-title a'); + assert.equal(collapse.getAttribute('aria-expanded'), 'true'); + assert.equal(anchor.getAttribute('aria-expanded'), 'true'); + }); + + it('Should be aria-expanded=false', function () { + var instance = ReactTestUtils.renderIntoDocument( + Panel content + ); + var collapse = instance.getDOMNode().querySelector('.panel-collapse'); + var anchor = instance.getDOMNode().querySelector('.panel-title a'); + assert.equal(collapse.getAttribute('aria-expanded'), 'false'); + assert.equal(anchor.getAttribute('aria-expanded'), 'false'); + }); + it('Should call onSelect handler', function (done) { - function handleSelect (key) { + function handleSelect (e, key) { assert.equal(key, '1'); done(); } @@ -174,4 +194,4 @@ describe('Panel', function () { assert.equal(children[0].nodeName, 'TABLE'); assert.notOk(children[0].className.match(/\bpanel-body\b/)); }); -}); \ No newline at end of file +}); From befed8367a98363cbcb8797103633a47e3668998 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 10 Mar 2015 08:07:35 -0600 Subject: [PATCH 23/44] [fixed] All panel-* classes dynamic based on bsStyle prop This is some initial work to solve a problem identified in #404. While that issue is directly related to Modals the problem is evident throughout the library. This is the first of many steps to remidy the issue. --- src/BootstrapMixin.js | 6 +++++- src/Panel.jsx | 23 +++++++++++++---------- test/BootstrapMixinSpec.jsx | 9 +++++++++ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/BootstrapMixin.js b/src/BootstrapMixin.js index a0cdfed663..132dc6a86d 100644 --- a/src/BootstrapMixin.js +++ b/src/BootstrapMixin.js @@ -29,7 +29,11 @@ var BootstrapMixin = { } return classes; + }, + + prefixClass: function(subClass) { + return constants.CLASSES[this.props.bsClass] + '-' + subClass; } }; -module.exports = BootstrapMixin; \ No newline at end of file +module.exports = BootstrapMixin; diff --git a/src/Panel.jsx b/src/Panel.jsx index 36ed087612..90058cb2d4 100644 --- a/src/Panel.jsx +++ b/src/Panel.jsx @@ -56,10 +56,10 @@ var Panel = React.createClass({ render: function () { var classes = this.getBsClassSet(); - classes['panel'] = true; return ( -
{this.renderHeading()} {this.props.collapsable ? this.renderCollapsableBody() : this.renderBody()} @@ -69,11 +69,13 @@ var Panel = React.createClass({ }, renderCollapsableBody: function () { + var collapseClass = this.prefixClass('collapse'); + return (
{this.renderBody()}
@@ -84,6 +86,7 @@ var Panel = React.createClass({ var allChildren = this.props.children; var bodyElements = []; var panelBodyChildren = []; + var bodyClass = this.prefixClass('body'); function getProps() { return {key: bodyElements.length}; @@ -95,7 +98,7 @@ var Panel = React.createClass({ function addPanelBody (children) { bodyElements.push( -
+
{children}
); @@ -152,17 +155,17 @@ var Panel = React.createClass({ this.renderCollapsableTitle(header) : header; } else if (this.props.collapsable) { header = cloneWithProps(header, { - className: 'panel-title', + className: this.prefixClass('title'), children: this.renderAnchor(header.props.children) }); } else { header = cloneWithProps(header, { - className: 'panel-title' + className: this.prefixClass('title') }); } return ( -
+
{header}
); @@ -182,7 +185,7 @@ var Panel = React.createClass({ renderCollapsableTitle: function (header) { return ( -

+

{this.renderAnchor(header)}

); @@ -194,7 +197,7 @@ var Panel = React.createClass({ } return ( -
+
{this.props.footer}
); diff --git a/test/BootstrapMixinSpec.jsx b/test/BootstrapMixinSpec.jsx index a80545c519..eb3fb69096 100644 --- a/test/BootstrapMixinSpec.jsx +++ b/test/BootstrapMixinSpec.jsx @@ -188,5 +188,14 @@ describe('BootstrapMixin', function () { ); assert.deepEqual(instance.getBsClassSet(), {'btn': true, 'btn-xs': true}); }); + + it('should return "btn-title"', function () { + var instance = ReactTestUtils.renderIntoDocument( + + content + + ); + assert.equal(instance.prefixClass('title'), 'btn-title'); + }); }); }); From 4fae87131c8ac039286adaf0ec8b4ae250f9b15f Mon Sep 17 00:00:00 2001 From: James Akers Date: Fri, 20 Feb 2015 13:56:37 -0500 Subject: [PATCH 24/44] [added] CollapsableNav implements bootstrap markup for navbar-collapse --- docs/examples/CollapsableNav.js | 23 +++++++ docs/src/ComponentsPage.js | 10 +++ docs/src/ReactPlayground.js | 1 + src/CollapsableNav.jsx | 110 ++++++++++++++++++++++++++++++++ test/CollapsableNavSpec.jsx | 89 ++++++++++++++++++++++++++ tools/amd/index.js | 1 + tools/cjs/main.js | 1 + 7 files changed, 235 insertions(+) create mode 100644 docs/examples/CollapsableNav.js create mode 100644 src/CollapsableNav.jsx create mode 100644 test/CollapsableNavSpec.jsx diff --git a/docs/examples/CollapsableNav.js b/docs/examples/CollapsableNav.js new file mode 100644 index 0000000000..7211b1342d --- /dev/null +++ b/docs/examples/CollapsableNav.js @@ -0,0 +1,23 @@ +var navbarInstance = ( + + {/* This is the eventKey referenced */} + + + + + ); + +React.render(navbarInstance, mountNode); diff --git a/docs/src/ComponentsPage.js b/docs/src/ComponentsPage.js index 7a611b42c6..315f8a8019 100644 --- a/docs/src/ComponentsPage.js +++ b/docs/src/ComponentsPage.js @@ -357,6 +357,16 @@ var ComponentsPage = React.createClass({
+ +

Mobile Friendly (Multiple Nav Components)

+

To have a mobile friendly Navbar that handles multiple Nav components use CollapsableNav. The toggleNavKey must still be set, however, the corresponding eventKey must now be on the CollapsableNav component.

+
+

Div collapse

+

The navbar-collapse div gets created as the collapsable element which follows the bootstrap collapsable navbar documentation.

+
<div class="collapse navbar-collapse"></div>
+
+ +
{/* Tabbed Areas */} diff --git a/docs/src/ReactPlayground.js b/docs/src/ReactPlayground.js index 8a23add10d..b91c26ee63 100644 --- a/docs/src/ReactPlayground.js +++ b/docs/src/ReactPlayground.js @@ -9,6 +9,7 @@ var Button = require('../../lib/Button'); var ButtonGroup = require('../../lib/ButtonGroup'); var ButtonToolbar = require('../../lib/ButtonToolbar'); var CollapsableMixin = require('../../lib/CollapsableMixin'); +var CollapsableNav = require('../../lib/CollapsableNav'); var Carousel = require('../../lib/Carousel'); var CarouselItem = require('../../lib/CarouselItem'); var Col = require('../../lib/Col'); diff --git a/src/CollapsableNav.jsx b/src/CollapsableNav.jsx new file mode 100644 index 0000000000..65e37931e7 --- /dev/null +++ b/src/CollapsableNav.jsx @@ -0,0 +1,110 @@ +var React = require('react'); +var joinClasses = require('./utils/joinClasses'); +var BootstrapMixin = require('./BootstrapMixin'); +var CollapsableMixin = require('./CollapsableMixin'); +var classSet = require('./utils/classSet'); +var domUtils = require('./utils/domUtils'); +var cloneWithProps = require('./utils/cloneWithProps'); + +var ValidComponentChildren = require('./utils/ValidComponentChildren'); +var createChainedFunction = require('./utils/createChainedFunction'); + + +var CollapsableNav = React.createClass({ + mixins: [BootstrapMixin, CollapsableMixin], + + propTypes: { + onSelect: React.PropTypes.func, + expanded: React.PropTypes.bool, + eventKey: React.PropTypes.any + }, + + getCollapsableDOMNode: function () { + return this.getDOMNode(); + }, + + getCollapsableDimensionValue: function () { + var height = 0; + var nodes = this.refs; + for (var key in nodes) { + if (nodes.hasOwnProperty(key)) { + + var n = nodes[key].getDOMNode() + , h = n.offsetHeight + , computedStyles = domUtils.getComputedStyles(n); + + height += (h + parseInt(computedStyles.marginTop, 10) + parseInt(computedStyles.marginBottom, 10)); + } + } + return height; + }, + + render: function () { + /* + * this.props.collapsable is set in NavBar when a eventKey is supplied. + */ + var classes = this.props.collapsable ? this.getCollapsableClassSet() : {}; + /* + * prevent duplicating navbar-collapse call if passed as prop. kind of overkill... good cadidate to have check implemented as a util that can + * also be used elsewhere. + */ + if (this.props.className == undefined || this.props.className.split(" ").indexOf('navbar-collapse') == -1) + classes['navbar-collapse'] = this.props.collapsable; + + return ( +
+ {ValidComponentChildren.map(this.props.children, (this.props.collapsable) ? this.renderCollapsableNavChildren : this.renderChildren )} +
+ ); + }, + + getChildActiveProp: function (child) { + if (child.props.active) { + return true; + } + if (this.props.activeKey != null) { + if (child.props.eventKey == this.props.activeKey) { + return true; + } + } + if (this.props.activeHref != null) { + if (child.props.href === this.props.activeHref) { + return true; + } + } + + return child.props.active; + }, + + renderChildren: function (child, index) { + var key = child.key ? child.key : index; + return cloneWithProps( + child, + { + activeKey: this.props.activeKey, + activeHref: this.props.activeHref, + ref: 'nocollapse_' + key, + key: key, + navItem: true + } + ); + }, + + renderCollapsableNavChildren: function (child, index) { + var key = child.key ? child.key : index; + return cloneWithProps( + child, + { + active: this.getChildActiveProp(child), + activeKey: this.props.activeKey, + activeHref: this.props.activeHref, + onSelect: createChainedFunction(child.props.onSelect, this.props.onSelect), + ref: 'collapsable_' + key, + key: key, + navItem: true + } + ); + } +}); + +module.exports = CollapsableNav; diff --git a/test/CollapsableNavSpec.jsx b/test/CollapsableNavSpec.jsx new file mode 100644 index 0000000000..e559ce810d --- /dev/null +++ b/test/CollapsableNavSpec.jsx @@ -0,0 +1,89 @@ +/*global describe, beforeEach, afterEach, it, assert */ + +var React = require('react'); +var ReactTestUtils = require('react/lib/ReactTestUtils'); +var Navbar = require('../lib/Navbar'); +var CollapsableNav = require('../lib/CollapsableNav'); +var Nav = require('../lib/Nav'); +var NavItem = require('../lib/NavItem'); + +describe('CollapsableNav', function () { + it('Should create div and add collapse class', function () { + var instance = ReactTestUtils.renderIntoDocument( + + + + + + ); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'navbar-collapse')); + }); + + it('Should handle multiple Nav elements', function () { + var instance = ReactTestUtils.renderIntoDocument( + + + + + + + ); + assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.collapsable_object.refs.collapsable_0, Nav)); + assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.collapsable_object.refs.collapsable_1, Nav)); + }); + + it('Should just render children and move along if not in ', function () { + var instance = ReactTestUtils.renderIntoDocument( + + + + ); + assert.notOk(instance.getDOMNode().className.match(/\navbar-collapse\b/)); + assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.nocollapse_0, Nav)); + }); + + it('Should retain childrens classes set by className', function () { + var instance = ReactTestUtils.renderIntoDocument( + + + + + + ); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'foo')); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'bar')); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'baz')); + }); + + it('Should should not duplicate classes', function () { + var instance = ReactTestUtils.renderIntoDocument( + + + + + + ); + var classDOM = ReactTestUtils.findRenderedDOMComponentWithTag(instance.refs.collapsable_object, 'DIV').props.className + , class_array = classDOM.split(" ") + , idx = class_array.indexOf('navbar-collapse'); + assert.equal(class_array.indexOf('navbar-collapse',idx+1), -1); + }); +}); diff --git a/tools/amd/index.js b/tools/amd/index.js index bc848179c6..d410f24e0c 100644 --- a/tools/amd/index.js +++ b/tools/amd/index.js @@ -16,6 +16,7 @@ define(function (require) { Carousel: require('./lib/Carousel'), CarouselItem: require('./lib/CarouselItem'), Col: require('./lib/Col'), + CollapsableNav: require('./lib/CollapsableNav'), CollapsableMixin: require('./lib/CollapsableMixin'), DropdownButton: require('./lib/DropdownButton'), DropdownMenu: require('./lib/DropdownMenu'), diff --git a/tools/cjs/main.js b/tools/cjs/main.js index 9ca6c92d8a..754cdd2d6d 100644 --- a/tools/cjs/main.js +++ b/tools/cjs/main.js @@ -11,6 +11,7 @@ module.exports = { Carousel: require('./Carousel'), CarouselItem: require('./CarouselItem'), Col: require('./Col'), + CollapsableNav: require('./CollapsableNav'), CollapsableMixin: require('./CollapsableMixin'), DropdownButton: require('./DropdownButton'), DropdownMenu: require('./DropdownMenu'), From c55e2895b7baf81a3dcfd6c056f3f2c7528e61b7 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 17 Mar 2015 09:03:27 -0600 Subject: [PATCH 25/44] "Release v0.17.0" --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d1f6b6a0..c52ffde62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +v0.17.0 - Tue, 17 Mar 2015 15:03:27 GMT +--------------------------------------- + +- [4fae871](../../commit/4fae871) [added] CollapsableNav implements bootstrap markup for navbar-collapse +- [befed83](../../commit/befed83) [fixed] All panel-* classes dynamic based on bsStyle prop +- [de6f7dd](../../commit/de6f7dd) [fixed] CollapsableMixin fixed size +- [7cc4747](../../commit/7cc4747) [fixed] Added role="button" to NavItem for aria compliance. +- [3b6ba7a](../../commit/3b6ba7a) [fixed] Col Offset/Pull/Push of zero. Fixes #406 +- [66c439f](../../commit/66c439f) [fixed] OverlayTrigger improvement related to #353 . Helps reduce browser reflows for lots of multiple OverlayTriggers being rendered at once. Before: http://i.imgur.com/e4UZ5l6.png , http://i.imgur.com/Tw39F9t.png After: http://i.imgur.com/bU0f7VY.png + + v0.16.1 - Tue, 03 Mar 2015 23:04:19 GMT --------------------------------------- diff --git a/package.json b/package.json index 0261bb1ab4..e7a70d2f50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap", - "version": "0.16.1", + "version": "0.17.0", "description": "Bootstrap 3 components build with React", "repository": { "type": "git", From 70f8596e33240a390ee927d94dbce9aa2ba1bd38 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 17 Mar 2015 09:28:46 -0600 Subject: [PATCH 26/44] [added] Travis CI Optimization More information: http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/ --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 87f8cd91a5..777b8a6b35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false language: node_js node_js: - - "0.10" \ No newline at end of file + - "0.10" From 4c26075e43e1e38d92bec580ec3eea20aa67b5ad Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 17 Mar 2015 09:30:43 -0600 Subject: [PATCH 27/44] [fixed] Build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2434e6bf6f..c89c131d61 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [Bootstrap 3](http://getbootstrap.com) components built with [React](http://facebook.github.io/react/) -[![Build Status](https://travis-ci.org/react-bootstrap/react-bootstrap.svg)](https://travis-ci.org/react-bootstrap/react-bootstrap) [![NPM version](https://badge.fury.io/js/react-bootstrap.svg)](http://badge.fury.io/js/react-bootstrap) [![Bower version](https://badge.fury.io/bo/react-bootstrap.svg)](http://badge.fury.io/bo/react-bootstrap) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/react-bootstrap/react-bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/react-bootstrap/react-bootstrap.svg?branch=master)](https://travis-ci.org/react-bootstrap/react-bootstrap) [![NPM version](https://badge.fury.io/js/react-bootstrap.svg)](http://badge.fury.io/js/react-bootstrap) [![Bower version](https://badge.fury.io/bo/react-bootstrap.svg)](http://badge.fury.io/bo/react-bootstrap) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/react-bootstrap/react-bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Under active development - APIs will change. From 156a1678cbf15c0d28d0e19b5fd840aa29ee0de8 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 17 Mar 2015 15:31:29 -0600 Subject: [PATCH 28/44] Minor tweeks to README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index c89c131d61..a453755c92 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,5 @@ and many [contributors](https://github.com/react-bootstrap/react-bootstrap/graph Yes please! - Run `npm install`, `npm run test-watch` to run tests while you develop (however this hides any build errors, you can see these with `grunt build`) -- Add tests for any new or changed functionality +- Review the [contributing guidelines](https://github.com/react-bootstrap/react-bootstrap/blob/master/CONTRIBUTING.md) - See [issues](https://github.com/stevoland/react-bootstrap/issues) for some ideas -- Follow existing style From 804c24a33f390166a89276f68772fa64b1510ba8 Mon Sep 17 00:00:00 2001 From: jquense Date: Sat, 28 Feb 2015 12:16:27 -0500 Subject: [PATCH 29/44] [added] Support for React 0.13.x re add react to devDeps remove old or depreciated API's Remove getDOMNode classSet, joinClasses, CloneWithProps Fix case on require() typos, and syntax errors update AMD, cjs package def fix tests Removed test dependence on root refs [FIX] Docs, upgrade to react-router --- Gruntfile.js | 3 +- docs/build.js | 16 +++- docs/client.js | 10 ++- docs/server.js | 15 +++- docs/src/NavMain.js | 11 +-- docs/src/ReactPlayground.js | 2 +- docs/src/Root.js | 30 +------- docs/src/Routes.js | 21 +++++ ie8/index.html | 2 +- ie8/package.json | 4 +- package.json | 11 +-- src/Affix.jsx | 4 +- src/AffixMixin.js | 2 +- src/Alert.jsx | 6 +- src/Badge.jsx | 6 +- src/Button.jsx | 8 +- src/ButtonGroup.jsx | 6 +- src/ButtonToolbar.jsx | 6 +- src/Carousel.jsx | 10 +-- src/CarouselItem.jsx | 8 +- src/Col.jsx | 6 +- src/DropdownButton.jsx | 15 ++-- src/DropdownMenu.jsx | 11 ++- src/DropdownStateMixin.js | 2 +- src/FadeMixin.js | 10 ++- src/Glyphicon.jsx | 6 +- src/Grid.jsx | 4 +- src/Input.jsx | 14 ++-- src/Jumbotron.jsx | 4 +- src/Label.jsx | 6 +- src/ListGroup.jsx | 7 +- src/ListGroupItem.jsx | 24 +++--- src/MenuItem.jsx | 6 +- src/Modal.jsx | 15 ++-- src/ModalTrigger.jsx | 6 +- src/Nav.jsx | 17 ++--- src/NavItem.jsx | 6 +- src/Navbar.jsx | 29 ++++--- src/OverlayMixin.js | 20 +---- src/OverlayTrigger.jsx | 8 +- src/PageHeader.jsx | 4 +- src/PageItem.jsx | 6 +- src/Pager.jsx | 9 +-- src/Panel.jsx | 24 +++--- src/PanelGroup.jsx | 10 +-- src/Popover.jsx | 6 +- src/ProgressBar.jsx | 17 ++--- src/Row.jsx | 4 +- src/SplitButton.jsx | 6 +- src/SubNav.jsx | 10 +-- src/TabPane.jsx | 7 +- src/TabbedArea.jsx | 5 +- src/Table.jsx | 5 +- src/Tooltip.jsx | 5 +- src/Well.jsx | 5 +- src/utils/CustomPropTypes.js | 4 +- src/utils/classSet.js | 39 ---------- src/utils/cloneWithProps.js | 143 ----------------------------------- src/utils/joinClasses.js | 40 ---------- test/DropdownButtonSpec.jsx | 2 +- test/DropdownMenuSpec.jsx | 18 +++-- test/FadeMixinSpec.jsx | 1 + test/ListGroupSpec.jsx | 11 ++- test/NavSpec.jsx | 53 ++++++++----- test/NavbarSpec.jsx | 6 +- test/OverlayMixinSpec.jsx | 2 +- test/PagerSpec.jsx | 11 ++- test/SplitButtonSpec.jsx | 2 +- test/TabbedAreaSpec.jsx | 77 ++++++++++++------- tools/amd/bower.json | 3 +- tools/cjs/package.json | 5 +- 71 files changed, 386 insertions(+), 551 deletions(-) create mode 100644 docs/src/Routes.js delete mode 100644 src/utils/classSet.js delete mode 100644 src/utils/cloneWithProps.js delete mode 100644 src/utils/joinClasses.js diff --git a/Gruntfile.js b/Gruntfile.js index 9f8e622e48..bcce4c838f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -136,7 +136,8 @@ module.exports = function (grunt) { almond: "../tools/vendor/almond" }, packages: [ - { name: 'react', location: '../node_modules/react', main: './react' } + { name: 'react', location: '../node_modules/react', main: './react' }, + { name: 'classnames', location: '../node_modules/classnames', main: './index' } ], include: ["almond", "react-bootstrap"], exclude: ["react"], diff --git a/docs/build.js b/docs/build.js index 4252a7c420..dbde01a273 100755 --- a/docs/build.js +++ b/docs/build.js @@ -2,12 +2,22 @@ var fs = require('fs'); var path = require('path'); -var nodejsx = require('node-jsx').install(); + +require('node-jsx').install(); + +var React = require('react'); +var routes = require('./src/Routes'); +var Router = require('react-router'); + var Root = require('./src/Root'); Root.getPages() .forEach(function (fileName) { - var RootHTML = Root.renderToString({initialPath: fileName}); - fs.writeFileSync(path.join(__dirname, fileName), RootHTML); + Router.run(routes, '/' + fileName, function (Handler) { + var RootHTML = React.renderToString(React.createElement(Handler)); + + fs.writeFileSync( + path.join(__dirname, fileName), RootHTML); + }) }); \ No newline at end of file diff --git a/docs/client.js b/docs/client.js index 80ebcd9b4e..733668d326 100644 --- a/docs/client.js +++ b/docs/client.js @@ -1,9 +1,15 @@ 'use strict'; var React = require('react'); -var Root = require('./src/Root'); +var Router = require('react-router'); +var routes = require('./src/Routes'); // For React devtools window.React = React; -React.render(React.createFactory(Root)(window.INITIAL_PROPS), document); +Router.run(routes, Router.RefreshLocation, function (Handler) { + React.render( + React.createElement(Handler, window.INITIAL_PROPS), document); +}) + + diff --git a/docs/server.js b/docs/server.js index 0b55c78101..244e93d0fb 100644 --- a/docs/server.js +++ b/docs/server.js @@ -2,15 +2,19 @@ var express = require('express'); + var development = process.env.NODE_ENV !== 'production'; var app = express(); if (development) { + var React = require('react'); var path = require('path'); var url = require('url'); var browserify = require('connect-browserify'); var nodejsx = require('node-jsx').install(); - var Root = require('./src/Root'); + + var routes = require('./src/Routes'); + var Router = require('react-router'); app = app .get('/assets/bundle.js', browserify('./client', {debug: true, watch: false})) @@ -18,9 +22,14 @@ if (development) { .use('/vendor', express.static(path.join(__dirname, 'vendor'))) .use(function renderApp(req, res) { var fileName = url.parse(req.url).pathname; - var RootHTML = Root.renderToString({initialPath: fileName}); - res.send(RootHTML); + Router.run(routes, req.url, function (Handler) { + var RootHTML = React.renderToString(React.createElement(Handler)); + + res.send(RootHTML); + }) + + }); } else { app = app diff --git a/docs/src/NavMain.js b/docs/src/NavMain.js index 72cb59f400..930064273d 100644 --- a/docs/src/NavMain.js +++ b/docs/src/NavMain.js @@ -1,19 +1,20 @@ 'use strict'; var React = require('react'); -var Router = require('react-router-component'); +var Router = require('react-router'); var Navbar = require('../../lib/Navbar'); var Nav = require('../../lib/Nav'); + var InternalLink = Router.Link; var NAV_LINKS = { 'getting-started': { - link: '/getting-started.html', + link: 'getting-started', title: 'Getting started' }, 'components': { - link: '/components.html', + link: 'components', title: 'Components' } }; @@ -24,7 +25,7 @@ var NavMain = React.createClass({ }, render: function () { - var brand = React Bootstrap; + var brand = React Bootstrap; return ( @@ -40,7 +41,7 @@ var NavMain = React.createClass({ return (
  • - {link.title} + {link.title}
  • ); } diff --git a/docs/src/ReactPlayground.js b/docs/src/ReactPlayground.js index 7ff19f1b02..28ef4fee4e 100644 --- a/docs/src/ReactPlayground.js +++ b/docs/src/ReactPlayground.js @@ -1,5 +1,5 @@ var React = require('react'); -var classSet = require('react/lib/cx'); +var classSet = require('classnames'); var CodeMirror = global.CodeMirror; var JSXTransformer = global.JSXTransformer; var Accordion = require('../../lib/Accordion'); diff --git a/docs/src/Root.js b/docs/src/Root.js index f97bc30768..be1af64df0 100644 --- a/docs/src/Root.js +++ b/docs/src/Root.js @@ -1,30 +1,7 @@ 'use strict'; var React = require('react'); -var Router = require('react-router-component'); - -var HomePage = React.createFactory(require('./HomePage')); -var GettingStartedPage = React.createFactory(require('./GettingStartedPage')); -var ComponentsPage = React.createFactory(require('./ComponentsPage')); -var NotFoundPage = React.createFactory(require('./NotFoundPage')); - -var Locations = Router.Locations; -var Location = Router.Location; -var NotFound = Router.NotFound; - -var PagesHolder = React.createClass({ - render: function () { - return ( - - - - - - - - ); - } -}); +var Router = require('react-router'); var Root = React.createClass({ statics: { @@ -110,9 +87,7 @@ var Root = React.createClass({ - - - + \n\ - \n\ - \ - '} +
    {`
    +  
    +  
    +  
    +                    `}
    @@ -74,21 +70,21 @@ var Page = React.createClass({

    jQuery is currently required only for IE8 support for components which require reading element positions from the DOM: Popover and Tooltip when launched with OverlayTrigger. We would like to remove this dependency in future versions but for now, including the following snippet in your page should have you covered:

    -
    {'\
    -  \n\
    -                    '}
    +
    {`
    +  
    +                    `}
    @@ -100,9 +96,9 @@ var Page = React.createClass({ ); }, - shouldComponentUpdate: function() { + shouldComponentUpdate() { return false; } }); -module.exports = Page; +export default Page; diff --git a/docs/src/HomePage.js b/docs/src/HomePage.js index a1e3e50bfe..1035c99c43 100644 --- a/docs/src/HomePage.js +++ b/docs/src/HomePage.js @@ -1,12 +1,9 @@ -'use strict'; +import React from 'react'; -var React = require('react'); +import NavMain from './NavMain'; +import PageFooter from './PageFooter'; -var NavMain = require('./NavMain'); -var PageHeader = require('./PageHeader'); -var PageFooter = require('./PageFooter'); - -var HomePage = React.createClass({ +const HomePage = React.createClass({ render: function () { return (
    @@ -25,4 +22,4 @@ var HomePage = React.createClass({ } }); -module.exports = HomePage; \ No newline at end of file +export default HomePage; diff --git a/docs/src/NavMain.js b/docs/src/NavMain.js index 930064273d..4ec0218027 100644 --- a/docs/src/NavMain.js +++ b/docs/src/NavMain.js @@ -1,14 +1,9 @@ -'use strict'; +import React from 'react'; +import Router, { Link } from 'react-router'; +import Navbar from '../../src/Navbar'; +import Nav from '../../src/Nav'; -var React = require('react'); -var Router = require('react-router'); -var Navbar = require('../../lib/Navbar'); -var Nav = require('../../lib/Nav'); - - -var InternalLink = Router.Link; - -var NAV_LINKS = { +const NAV_LINKS = { 'getting-started': { link: 'getting-started', title: 'Getting started' @@ -19,32 +14,37 @@ var NAV_LINKS = { } }; -var NavMain = React.createClass({ +const NavMain = React.createClass({ propTypes: { activePage: React.PropTypes.string }, - render: function () { - var brand = React Bootstrap; + render() { + let brand = React Bootstrap; + let links = Object.keys(NAV_LINKS).map(this.renderNavItem).concat([ +
  • + GitHub +
  • + ]); return ( ); }, - renderNavItem: function (linkName) { - var link = NAV_LINKS[linkName]; + renderNavItem(linkName) { + let link = NAV_LINKS[linkName]; return (
  • - {link.title} + {link.title}
  • ); } }); -module.exports = NavMain; +export default NavMain; diff --git a/docs/src/NotFoundPage.js b/docs/src/NotFoundPage.js index d92797fcba..484570fda7 100644 --- a/docs/src/NotFoundPage.js +++ b/docs/src/NotFoundPage.js @@ -1,20 +1,18 @@ -'use strict'; +import React from 'react'; -var React = require('react'); +import NavMain from './NavMain'; +import PageHeader from './PageHeader'; +import PageFooter from './PageFooter'; -var NavMain = require('./NavMain'); -var PageHeader = require('./PageHeader'); -var PageFooter = require('./PageFooter'); - -var NotFoundPage = React.createClass({ - render: function () { +const NotFoundPage = React.createClass({ + render() { return (
    - + + title='404' + subTitle='Hmmm this is awkward.' />
    @@ -22,4 +20,4 @@ var NotFoundPage = React.createClass({ } }); -module.exports = NotFoundPage; \ No newline at end of file +export default NotFoundPage; diff --git a/docs/src/PageFooter.js b/docs/src/PageFooter.js index d0eb4b49c6..1da8666bdd 100644 --- a/docs/src/PageFooter.js +++ b/docs/src/PageFooter.js @@ -1,32 +1,30 @@ -'use strict'; +import React from 'react'; +import packageJSON from '../../package.json'; -var React = require('react'); -var packageJSON = require('../../package.json'); - -var PageHeader = React.createClass({ - render: function () { +const PageHeader = React.createClass({ + render() { return ( -