From ce5b436c6254983a34d4eb1e993b542063f0234f Mon Sep 17 00:00:00 2001 From: Alex Abenoja Date: Tue, 26 May 2015 11:09:40 -0600 Subject: [PATCH 001/128] [removed] Input type=submit deprecation warning. Closes #733 --- src/Input.js | 6 +----- test/InputSpec.js | 24 ------------------------ 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/src/Input.js b/src/Input.js index fa516a4ee4..f385c5607d 100644 --- a/src/Input.js +++ b/src/Input.js @@ -1,15 +1,11 @@ import React from 'react'; import InputBase from './InputBase'; -import ButtonInput from './ButtonInput'; import FormControls from './FormControls'; import deprecationWarning from './utils/deprecationWarning'; class Input extends InputBase { render() { - if (ButtonInput.types.indexOf(this.props.type) > -1) { - deprecationWarning(`Input type=${this.props.type}`, 'ButtonInput'); - return ; - } else if (this.props.type === 'static') { + if (this.props.type === 'static') { deprecationWarning('Input type=static', 'StaticText'); return ; } diff --git a/test/InputSpec.js b/test/InputSpec.js index 3a73cba5cf..54bb2b3c51 100644 --- a/test/InputSpec.js +++ b/test/InputSpec.js @@ -41,30 +41,6 @@ describe('Input', function () { assert.equal(instance.getValue(), 'v'); }); - it('throws a deprecation warning on type=button', function () { - ReactTestUtils.renderIntoDocument( - - ); - - shouldWarn('deprecated'); - }); - - it('throws a deprecation warning on type=reset', function () { - ReactTestUtils.renderIntoDocument( - - ); - - shouldWarn('deprecated'); - }); - - it('throws a deprecation warning on type=submit', function () { - ReactTestUtils.renderIntoDocument( - - ); - - shouldWarn('deprecated'); - }); - it('throws a warning when type=static', function () { ReactTestUtils.renderIntoDocument( From 9c09e2a5068e437c5767c4c5299bba16d9bb1aa8 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 29 May 2015 09:07:02 -0600 Subject: [PATCH 002/128] [fixed] Keyboard accessibility for anchors serving as buttons Bootstrap uses a lot of styling that is specifically targeting anchor tags that may also serve in the capacity as a button. Unfortunately since Bootstrap does not style said buttons we have to use an anchor tag instead. But in order to maintain keyboard functionality for accessibility concerns even those anchor tags must provide an href. The solution is to add an internal `SafeAnchor` component which ensures that something exists for the href attribute, and calls `event.preventDefault()` when the anchor is clicked. It will then continue to invoke any additional `onClick` handler provided. --- src/ListGroupItem.js | 6 +-- src/MenuItem.js | 6 +-- src/NavItem.js | 14 ++----- src/PageItem.js | 14 ++----- src/SafeAnchor.js | 38 +++++++++++++++++ src/SubNav.js | 8 ++-- src/Thumbnail.js | 5 ++- src/index.js | 2 + test/NavSpec.js | 4 +- test/PageItemSpec.js | 2 +- test/SafeAnchorSpec.js | 93 ++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 157 insertions(+), 35 deletions(-) create mode 100644 src/SafeAnchor.js create mode 100644 test/SafeAnchorSpec.js diff --git a/src/ListGroupItem.js b/src/ListGroupItem.js index dae133d54f..ac79d8bd37 100644 --- a/src/ListGroupItem.js +++ b/src/ListGroupItem.js @@ -1,7 +1,7 @@ import React, { cloneElement } from 'react'; import BootstrapMixin from './BootstrapMixin'; import classNames from 'classnames'; - +import SafeAnchor from './SafeAnchor'; const ListGroupItem = React.createClass({ mixins: [BootstrapMixin], @@ -51,12 +51,12 @@ const ListGroupItem = React.createClass({ renderAnchor(classes) { return ( - {this.props.header ? this.renderStructuredContent() : this.props.children} - + ); }, diff --git a/src/MenuItem.js b/src/MenuItem.js index d5bdce7a89..bf63602cfd 100644 --- a/src/MenuItem.js +++ b/src/MenuItem.js @@ -1,5 +1,6 @@ import React from 'react'; import classNames from 'classnames'; +import SafeAnchor from './SafeAnchor'; const MenuItem = React.createClass({ propTypes: { @@ -15,7 +16,6 @@ const MenuItem = React.createClass({ getDefaultProps() { return { - href: '#', active: false }; }, @@ -29,9 +29,9 @@ const MenuItem = React.createClass({ renderAnchor() { return ( - + {this.props.children} - + ); }, diff --git a/src/NavItem.js b/src/NavItem.js index 9bc768df48..8613635567 100644 --- a/src/NavItem.js +++ b/src/NavItem.js @@ -1,6 +1,7 @@ import React from 'react'; import classNames from 'classnames'; import BootstrapMixin from './BootstrapMixin'; +import SafeAnchor from './SafeAnchor'; const NavItem = React.createClass({ mixins: [BootstrapMixin], @@ -15,12 +16,6 @@ const NavItem = React.createClass({ target: React.PropTypes.string }, - getDefaultProps() { - return { - href: '#' - }; - }, - render() { let { disabled, @@ -38,8 +33,7 @@ const NavItem = React.createClass({ href, title, target, - onClick: this.handleClick, - ref: 'anchor' + onClick: this.handleClick }; if (href === '#') { @@ -48,9 +42,9 @@ const NavItem = React.createClass({ return (
  • - + { children } - +
  • ); }, diff --git a/src/PageItem.js b/src/PageItem.js index 5a7c22d7f9..44ef5e419c 100644 --- a/src/PageItem.js +++ b/src/PageItem.js @@ -1,5 +1,6 @@ import React from 'react'; import classNames from 'classnames'; +import SafeAnchor from './SafeAnchor'; const PageItem = React.createClass({ @@ -14,12 +15,6 @@ const PageItem = React.createClass({ eventKey: React.PropTypes.any }, - getDefaultProps() { - return { - href: '#' - }; - }, - render() { let classes = { 'disabled': this.props.disabled, @@ -31,14 +26,13 @@ const PageItem = React.createClass({
  • - + onClick={this.handleSelect}> {this.props.children} - +
  • ); }, diff --git a/src/SafeAnchor.js b/src/SafeAnchor.js new file mode 100644 index 0000000000..8316bbe634 --- /dev/null +++ b/src/SafeAnchor.js @@ -0,0 +1,38 @@ +import React from 'react'; + +/** + * Note: This is intended as a stop-gap for accessibility concerns that the + * Bootstrap CSS does not address as they have styled anchors and not buttons + * in many cases. + */ +export default class SafeAnchor extends React.Component { + constructor(props) { + super(props); + + this.handleClick = this.handleClick.bind(this); + } + + handleClick(event) { + if (this.props.href === undefined) { + event.preventDefault(); + } + + if (this.props.onClick) { + this.props.onClick(event); + } + } + + render() { + return ( + + ); + } +} + +SafeAnchor.propTypes = { + href: React.PropTypes.string, + onClick: React.PropTypes.func +}; diff --git a/src/SubNav.js b/src/SubNav.js index e9487c1ada..814604cc76 100644 --- a/src/SubNav.js +++ b/src/SubNav.js @@ -4,6 +4,7 @@ import classNames from 'classnames'; import ValidComponentChildren from './utils/ValidComponentChildren'; import createChainedFunction from './utils/createChainedFunction'; import BootstrapMixin from './BootstrapMixin'; +import SafeAnchor from './SafeAnchor'; const SubNav = React.createClass({ mixins: [BootstrapMixin], @@ -99,14 +100,13 @@ const SubNav = React.createClass({ return (
  • - + onClick={this.handleClick}> {this.props.text} - +
      {ValidComponentChildren.map(this.props.children, this.renderNavItem)}
    diff --git a/src/Thumbnail.js b/src/Thumbnail.js index ea05cf85d2..8085e63fea 100644 --- a/src/Thumbnail.js +++ b/src/Thumbnail.js @@ -1,6 +1,7 @@ import React from 'react'; import classSet from 'classnames'; import BootstrapMixin from './BootstrapMixin'; +import SafeAnchor from './SafeAnchor'; const Thumbnail = React.createClass({ mixins: [BootstrapMixin], @@ -16,9 +17,9 @@ const Thumbnail = React.createClass({ if(this.props.href) { return ( - + {this.props.alt} - + ); } else { diff --git a/src/index.js b/src/index.js index 0f05aed32f..ed0d61b513 100644 --- a/src/index.js +++ b/src/index.js @@ -42,6 +42,7 @@ import Pager from './Pager'; import Popover from './Popover'; import ProgressBar from './ProgressBar'; import Row from './Row'; +import SafeAnchor from './SafeAnchor'; import SplitButton from './SplitButton'; import SubNav from './SubNav'; import TabbedArea from './TabbedArea'; @@ -97,6 +98,7 @@ export default { Popover, ProgressBar, Row, + SafeAnchor, SplitButton, SubNav, TabbedArea, diff --git a/test/NavSpec.js b/test/NavSpec.js index 81a8d09fe4..1061edb738 100644 --- a/test/NavSpec.js +++ b/test/NavSpec.js @@ -83,9 +83,9 @@ describe('Nav', function () { ); - let items = ReactTestUtils.scryRenderedComponentsWithType(instance, NavItem); + let items = ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, 'A'); - ReactTestUtils.Simulate.click(items[1].refs.anchor); + ReactTestUtils.Simulate.click(items[1]); }); it('Should set the correct item active by href', function () { diff --git a/test/PageItemSpec.js b/test/PageItemSpec.js index 99e3d0b10d..7f079f4be2 100644 --- a/test/PageItemSpec.js +++ b/test/PageItemSpec.js @@ -36,7 +36,7 @@ describe('PageItem', function () { it('Should call "onSelect" when item is clicked', function (done) { function handleSelect(key, href) { assert.equal(key, 1); - assert.equal(href, '#'); + assert.equal(href, undefined); done(); } let instance = ReactTestUtils.renderIntoDocument( diff --git a/test/SafeAnchorSpec.js b/test/SafeAnchorSpec.js new file mode 100644 index 0000000000..75b9d0a8de --- /dev/null +++ b/test/SafeAnchorSpec.js @@ -0,0 +1,93 @@ +import React from 'react'; +import ReactTestUtils from 'react/lib/ReactTestUtils'; +import SafeAnchor from '../src/SafeAnchor'; + +describe('SafeAnchor', function() { + it('renders an anchor tag', function() { + const instance = ReactTestUtils.renderIntoDocument(); + const node = React.findDOMNode(instance); + + node.tagName.should.equal('A'); + }); + + it('forwards arbitrary props to the anchor', function() { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + anchor.props.herpa.should.equal('derpa'); + }); + + it('forwards provided href', function() { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + anchor.props.href.should.equal('http://google.com'); + }); + + it('ensures that an href is provided', function() { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + anchor.props.href.should.equal(''); + }); + + it('forwards onClick handler', function(done) { + const handleClick = (event) => { + done(); + }; + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + ReactTestUtils.Simulate.click(anchor); + }); + + it('prevents default when no href is provided', function(done) { + const handleClick = (event) => { + event.defaultPrevented.should.be.true; + done(); + }; + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + ReactTestUtils.Simulate.click(anchor); + }); + + it('does not prevent default when href is provided', function(done) { + const handleClick = (event) => { + expect(event.defaultPrevented).to.not.be.ok; + done(); + }; + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + ReactTestUtils.Simulate.click(anchor); + }); + + it('forwards provided role', function () { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + anchor.props.role.should.equal('test'); + }); + + it('forwards provided role with href', function () { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + anchor.props.role.should.equal('test'); + }); + + it('set role=button with no provided href', function () { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + anchor.props.role.should.equal('button'); + }); + + it('sets no role with provided href', function () { + const instance = ReactTestUtils.renderIntoDocument(); + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); + + expect(anchor.props.role).to.be.undefined; + }); +}); From b547109a71f5f5115534650e58721e70517d664d Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 2 Jun 2015 11:01:37 -0600 Subject: [PATCH 003/128] Fix alpha changelog generation to be added --- tools/release-scripts/changelog.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/release-scripts/changelog.js b/tools/release-scripts/changelog.js index a97b72b786..c31f53841a 100644 --- a/tools/release-scripts/changelog.js +++ b/tools/release-scripts/changelog.js @@ -29,11 +29,9 @@ export default (version) => { .then(() => exec(`node_modules/.bin/changelog --title v${version} --out ${output}${additionalArgs}`)) .then(() => safeExec(`git add ${changelog}`)) .then(() => { - if (!removedAlphaChangelog) { - return null; + if (removedAlphaChangelog || isPrerelease) { + return safeExec(`git add -A ${alphaChangelog}`); } - - return safeExec(`git add -A ${alphaChangelog}`); }) .then(() => console.log('Generated Changelog'.cyan)); }; From 3146dee139a0b9b5b332b4549d72e1c77fcd39e2 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 2 Jun 2015 11:08:52 -0600 Subject: [PATCH 004/128] Release v0.24.0-alpha.0 --- CHANGELOG-alpha.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG-alpha.md diff --git a/CHANGELOG-alpha.md b/CHANGELOG-alpha.md new file mode 100644 index 0000000000..9c16c92cb7 --- /dev/null +++ b/CHANGELOG-alpha.md @@ -0,0 +1,5 @@ +v0.24.0-alpha.0 - Tue, 02 Jun 2015 17:08:11 GMT +----------------------------------------------- + +- [9c09e2a](../../commit/9c09e2a) [fixed] Keyboard accessibility for anchors serving as buttons +- [ce5b436](../../commit/ce5b436) [removed] Input type=submit deprecation warning. diff --git a/package.json b/package.json index e24d1a0841..7f8e9ddc53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap", - "version": "0.23.1", + "version": "0.24.0-alpha.0", "description": "Bootstrap 3 components build with React", "repository": { "type": "git", From beaa1fa614d96507c576b5f3823160b408b17a49 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 8 Jun 2015 18:57:17 -0600 Subject: [PATCH 005/128] [changed] `PaginationButton` to use `SafeAnchor` Using the `SafeAnchor` for better accessibility. Per #786 commitcomment-11527424 Resolves #788 --- src/PaginationButton.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/PaginationButton.js b/src/PaginationButton.js index 0238b5b9b7..e49e1344ca 100644 --- a/src/PaginationButton.js +++ b/src/PaginationButton.js @@ -2,6 +2,7 @@ import React from 'react'; import classNames from 'classnames'; import BootstrapMixin from './BootstrapMixin'; import createSelectedEvent from './utils/createSelectedEvent'; +import SafeAnchor from './SafeAnchor'; const PaginationButton = React.createClass({ mixins: [BootstrapMixin], @@ -25,9 +26,6 @@ const PaginationButton = React.createClass({ }, handleClick(event) { - // This would go away once SafeAnchor is available - event.preventDefault(); - if (this.props.onSelect) { let selectedEvent = createSelectedEvent(this.props.eventKey); this.props.onSelect(event, selectedEvent); @@ -35,14 +33,22 @@ const PaginationButton = React.createClass({ }, render() { - let classes = this.getBsClassSet(); + let classes = { + active: this.props.active, + disabled: this.props.disabled, + ...this.getBsClassSet() + }; - classes.active = this.props.active; - classes.disabled = this.props.disabled; + let { + className, + ...anchorProps // eslint-disable-line object-shorthand + } = this.props; return ( -
  • - {this.props.children} +
  • +
  • ); } From 704a168eb334c293cfef0d566d51999f6ed46557 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 8 Jun 2015 19:07:11 -0600 Subject: [PATCH 006/128] Release v0.24.0-alpha.1 --- CHANGELOG-alpha.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-alpha.md b/CHANGELOG-alpha.md index 9c16c92cb7..ded6d073d6 100644 --- a/CHANGELOG-alpha.md +++ b/CHANGELOG-alpha.md @@ -1,3 +1,14 @@ +v0.24.0-alpha.1 - Tue, 09 Jun 2015 01:06:34 GMT +----------------------------------------------- + +- [7211dcb](../../commit/7211dcb) [added] Add prevIcon and nextIcon props as node proptypes to Carousel +- [5734ec3](../../commit/5734ec3) [added] Pagination component +- [2f8c454](../../commit/2f8c454) [changed] Assert ProgressBar children can be ProgressBar only. +- [2c46820](../../commit/2c46820) [added] `createSelectedEvent` for consistent onSelect handling +- [c2ff9ad](../../commit/c2ff9ad) [added] property disabled on MenuItem + + + v0.24.0-alpha.0 - Tue, 02 Jun 2015 17:08:11 GMT ----------------------------------------------- diff --git a/package.json b/package.json index 7f8e9ddc53..9c4b05b14e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap", - "version": "0.24.0-alpha.0", + "version": "0.24.0-alpha.1", "description": "Bootstrap 3 components build with React", "repository": { "type": "git", From 598b9d85820edc8eafac2f95a0d1e8bae0623485 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 9 Jun 2015 18:16:20 -0600 Subject: [PATCH 007/128] [fixed] SafeAnchor event ordering If we prevent default before applying the `onClick` function provided in props then we prevent elements from using the `event.preventDefault()` mechanics for anchors as buttons. For example in the Dropdown re-work this prevented me from having the Dropdown work when in a Nav since the toggle is an anchor. Yet that functionality should allow uses to prevent the Dropdown if they want in their own `onClick` handler. This will enable such a use case. --- src/SafeAnchor.js | 7 ++----- test/SafeAnchorSpec.js | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/SafeAnchor.js b/src/SafeAnchor.js index 8316bbe634..ae45a9c261 100644 --- a/src/SafeAnchor.js +++ b/src/SafeAnchor.js @@ -1,4 +1,5 @@ import React from 'react'; +import createChainedFunction from './utils/createChainedFunction'; /** * Note: This is intended as a stop-gap for accessibility concerns that the @@ -16,17 +17,13 @@ export default class SafeAnchor extends React.Component { if (this.props.href === undefined) { event.preventDefault(); } - - if (this.props.onClick) { - this.props.onClick(event); - } } render() { return ( ); } diff --git a/test/SafeAnchorSpec.js b/test/SafeAnchorSpec.js index 75b9d0a8de..2271c2fbbe 100644 --- a/test/SafeAnchorSpec.js +++ b/test/SafeAnchorSpec.js @@ -43,8 +43,12 @@ describe('SafeAnchor', function() { it('prevents default when no href is provided', function(done) { const handleClick = (event) => { - event.defaultPrevented.should.be.true; - done(); + expect(event.isDefaultPrevented()).to.not.be.ok; + + setTimeout(() => { + event.isDefaultPrevented().should.be.true; + done(); + }, 100); }; const instance = ReactTestUtils.renderIntoDocument(); const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); @@ -54,8 +58,12 @@ describe('SafeAnchor', function() { it('does not prevent default when href is provided', function(done) { const handleClick = (event) => { - expect(event.defaultPrevented).to.not.be.ok; - done(); + expect(event.isDefaultPrevented()).to.not.be.ok; + + setTimeout(() => { + expect(event.isDefaultPrevented()).to.not.be.ok; + done(); + }); }; const instance = ReactTestUtils.renderIntoDocument(); const anchor = ReactTestUtils.findRenderedDOMComponentWithTag(instance, 'A'); From 6e985b0537550d7123ef58bdff125da626acda2c Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Wed, 10 Jun 2015 19:28:05 -0600 Subject: [PATCH 008/128] [removed] Individual files in bower release Resolves #693 --- tools/amd/bower.json | 3 +-- tools/amd/build.js | 16 ++-------------- tools/buildBabel.js | 8 +------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/tools/amd/bower.json b/tools/amd/bower.json index 0e3451d5c4..5c56aa2f4d 100644 --- a/tools/amd/bower.json +++ b/tools/amd/bower.json @@ -16,7 +16,6 @@ "**/.*" ], "dependencies": { - "react": ">= 0.13.0", - "classnames": "^2.0.0" + "react": ">= 0.13.0" } } diff --git a/tools/amd/build.js b/tools/amd/build.js index ae47031c22..70f87c72e0 100644 --- a/tools/amd/build.js +++ b/tools/amd/build.js @@ -3,9 +3,7 @@ import path from 'path'; import fsp from 'fs-promise'; import { copy } from '../fs-utils'; import { exec } from '../exec'; -import generateFactories from '../generateFactories'; -import { repoRoot, srcRoot, bowerRoot } from '../constants'; -import { buildFolder } from '../buildBabel'; +import { repoRoot, bowerRoot } from '../constants'; const packagePath = path.join(repoRoot, 'package.json'); const bowerTemplate = path.join(__dirname, 'bower.json'); @@ -14,14 +12,6 @@ const bowerJson = path.join(bowerRoot, 'bower.json'); const readme = path.join(__dirname, 'README.md'); const license = path.join(repoRoot, 'LICENSE'); -const babelOptions = { - __reactBootstrapDeprecationWarning: true, - modules: 'amd' -}; - -const libDestination = path.join(bowerRoot, 'lib'); -const factoriesDestination = path.join(libDestination, 'factories'); - function bowerConfig() { return Promise.all([ fsp.readFile(packagePath) @@ -37,11 +27,9 @@ export default function BuildBower() { console.log('Building: '.cyan + 'bower module'.green); return exec(`rimraf ${bowerRoot}`) - .then(() => fsp.mkdirs(factoriesDestination)) + .then(() => fsp.mkdirs(bowerRoot)) .then(() => Promise.all([ bowerConfig(), - generateFactories(factoriesDestination, babelOptions), - buildFolder(srcRoot, libDestination, babelOptions), copy(readme, bowerRoot), copy(license, bowerRoot) ])) diff --git a/tools/buildBabel.js b/tools/buildBabel.js index 7811d21bb4..e2ef5c66e2 100644 --- a/tools/buildBabel.js +++ b/tools/buildBabel.js @@ -12,13 +12,7 @@ export function buildContent(content, filename, destination, babelOptions={}) { } export function buildFile(filename, destination, babelOptions={}) { - let content = fs.readFileSync(filename, {encoding: 'utf8'}); - if (babelOptions.__reactBootstrapDeprecationWarning) { - content = `console.warn('This file is deprecated, and will be removed in v0.24.0. Use react-bootstrap.js or react-bootstrap.min.js instead.'); -console.warn('You can read more about it at https://github.com/react-bootstrap/react-bootstrap/issues/693'); -${content}`; - } - + const content = fs.readFileSync(filename, {encoding: 'utf8'}); if(babelUtil.canCompile(filename)) { // Get file basename without the extension (in case not .js) let outputName = path.basename(filename, path.extname(filename)); From 36358d6176ae9be1d4266fce20ae308602939736 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 12 Jun 2015 15:52:46 -0600 Subject: [PATCH 009/128] Release v0.24.0-alpha.2 --- CHANGELOG-alpha.md | 14 ++++++++++++++ package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-alpha.md b/CHANGELOG-alpha.md index ded6d073d6..62db670361 100644 --- a/CHANGELOG-alpha.md +++ b/CHANGELOG-alpha.md @@ -1,3 +1,17 @@ +v0.24.0-alpha.2 - Fri, 12 Jun 2015 21:52:11 GMT +----------------------------------------------- + +- [9ca26e9](../../commit/9ca26e9) [added] contains "polyfill" to domUtils +- [6e985b0](../../commit/6e985b0) [removed] Individual files in bower release +- [3a254a1](../../commit/3a254a1) [added] Deprecation warning for individual file use in the Bower release +- [73c7705](../../commit/73c7705) [changed] Update chai. Dev dependency. +- [3ca90c7](../../commit/3ca90c7) [changed] Update karma-sinon-chai. Dev dependency. +- [cc4e820](../../commit/cc4e820) [changed] Update fs-extra. Dev dependency. +- [598b9d8](../../commit/598b9d8) [fixed] SafeAnchor event ordering +- [beaa1fa](../../commit/beaa1fa) [changed] `PaginationButton` to use `SafeAnchor` + + + v0.24.0-alpha.1 - Tue, 09 Jun 2015 01:06:34 GMT ----------------------------------------------- diff --git a/package.json b/package.json index d61be1e689..b075ef7e69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap", - "version": "0.24.0-alpha.1", + "version": "0.24.0-alpha.2", "description": "Bootstrap 3 components build with React", "repository": { "type": "git", From fafe46f0037814773b5bce49f1b7b2bdf3fb3f2b Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Sat, 13 Jun 2015 12:18:33 -0400 Subject: [PATCH 010/128] [changed] Use named exports in index files Fixes #350 --- .babelrc | 4 +- docs/src/GettingStartedPage.js | 7 +- docs/src/ReactPlayground.js | 10 +- src/FormControls/index.js | 6 +- src/Input.js | 2 +- src/index.js | 170 +++++++++++---------------------- src/utils/index.js | 18 +--- test/FormControlsSpec.js | 2 +- tools/public-components.js | 2 +- 9 files changed, 77 insertions(+), 144 deletions(-) diff --git a/.babelrc b/.babelrc index 8f85e598f6..3162e1a075 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,3 @@ { - "optional": [ - "es7.objectRestSpread" - ] + "stage": 1 } diff --git a/docs/src/GettingStartedPage.js b/docs/src/GettingStartedPage.js index f19af4f741..883dff20e9 100644 --- a/docs/src/GettingStartedPage.js +++ b/docs/src/GettingStartedPage.js @@ -43,7 +43,12 @@ $ npm install react-bootstrap` codeText={ `var Alert = require('react-bootstrap/lib/Alert'); // or -var Alert = require('react-bootstrap').Alert;` +var Alert = require('react-bootstrap').Alert; + +// with ES6 modules +import Alert from 'react-bootstrap/lib/Alert'; +// or +import {Alert} from 'react-bootstrap';` } /> diff --git a/docs/src/ReactPlayground.js b/docs/src/ReactPlayground.js index 0b33a304a3..743bb65979 100644 --- a/docs/src/ReactPlayground.js +++ b/docs/src/ReactPlayground.js @@ -3,10 +3,10 @@ import * as modClassNames from 'classnames'; import * as modAccordion from '../../src/Accordion'; import * as modAlert from '../../src/Alert'; import * as modBadge from '../../src/Badge'; -import * as modmodButton from '../../src/Button'; +import * as modButton from '../../src/Button'; import * as modButtonGroup from '../../src/ButtonGroup'; import * as modButtonInput from '../../src/ButtonInput'; -import * as modmodButtonToolbar from '../../src/ButtonToolbar'; +import * as modButtonToolbar from '../../src/ButtonToolbar'; import * as modCollapsibleNav from '../../src/CollapsibleNav'; import * as modCollapsibleMixin from '../../src/CollapsibleMixin'; import * as modCarousel from '../../src/Carousel'; @@ -56,17 +56,17 @@ const React = modReact.default; const Accordion = modAccordion.default; const Alert = modAlert.default; const Badge = modBadge.default; -const Button = modmodButton.default; +const Button = modButton.default; const ButtonGroup = modButtonGroup.default; const ButtonInput = modButtonInput.default; -const ButtonToolbar = modmodButtonToolbar.default; +const ButtonToolbar = modButtonToolbar.default; const CollapsibleNav = modCollapsibleNav.default; const CollapsibleMixin = modCollapsibleMixin.default; const Carousel = modCarousel.default; const CarouselItem = modCarouselItem.default; const Col = modCol.default; const DropdownButton = modDropdownButton.default; -const FormControls = modFormControls.default; +const FormControls = modFormControls; const Glyphicon = modGlyphicon.default; const Grid = modGrid.default; const Input = modInput.default; diff --git a/src/FormControls/index.js b/src/FormControls/index.js index 5a7c16286e..ef0f49c381 100644 --- a/src/FormControls/index.js +++ b/src/FormControls/index.js @@ -1,5 +1 @@ -import Static from './Static'; - -export default { - Static -}; +export Static from './Static'; diff --git a/src/Input.js b/src/Input.js index f385c5607d..d08eac7b35 100644 --- a/src/Input.js +++ b/src/Input.js @@ -1,6 +1,6 @@ import React from 'react'; import InputBase from './InputBase'; -import FormControls from './FormControls'; +import * as FormControls from './FormControls'; import deprecationWarning from './utils/deprecationWarning'; class Input extends InputBase { diff --git a/src/index.js b/src/index.js index 814c690572..5167ae7474 100644 --- a/src/index.js +++ b/src/index.js @@ -1,115 +1,57 @@ -import Accordion from './Accordion'; -import Affix from './Affix'; -import AffixMixin from './AffixMixin'; -import Alert from './Alert'; -import BootstrapMixin from './BootstrapMixin'; -import Badge from './Badge'; -import Button from './Button'; -import ButtonGroup from './ButtonGroup'; -import ButtonInput from './ButtonInput'; -import ButtonToolbar from './ButtonToolbar'; -import CollapsibleNav from './CollapsibleNav'; -import Carousel from './Carousel'; -import CarouselItem from './CarouselItem'; -import Col from './Col'; -import CollapsibleMixin from './CollapsibleMixin'; -import DropdownButton from './DropdownButton'; -import DropdownMenu from './DropdownMenu'; -import DropdownStateMixin from './DropdownStateMixin'; -import FadeMixin from './FadeMixin'; -import FormControls from './FormControls'; -import Glyphicon from './Glyphicon'; -import Grid from './Grid'; -import Input from './Input'; -import Interpolate from './Interpolate'; -import Jumbotron from './Jumbotron'; -import Label from './Label'; -import ListGroup from './ListGroup'; -import ListGroupItem from './ListGroupItem'; -import MenuItem from './MenuItem'; -import Modal from './Modal'; -import Nav from './Nav'; -import Navbar from './Navbar'; -import NavItem from './NavItem'; -import ModalTrigger from './ModalTrigger'; -import OverlayTrigger from './OverlayTrigger'; -import OverlayMixin from './OverlayMixin'; -import PageHeader from './PageHeader'; -import Pagination from './Pagination'; -import Panel from './Panel'; -import PanelGroup from './PanelGroup'; -import PageItem from './PageItem'; -import Pager from './Pager'; -import Popover from './Popover'; -import ProgressBar from './ProgressBar'; -import Row from './Row'; -import SafeAnchor from './SafeAnchor'; -import SplitButton from './SplitButton'; -import SubNav from './SubNav'; -import TabbedArea from './TabbedArea'; -import Table from './Table'; -import TabPane from './TabPane'; -import Thumbnail from './Thumbnail'; -import Tooltip from './Tooltip'; -import utils from './utils'; -import Well from './Well'; -import styleMaps from './styleMaps'; +export Accordion from './Accordion'; +export Affix from './Affix'; +export AffixMixin from './AffixMixin'; +export Alert from './Alert'; +export Badge from './Badge'; +export BootstrapMixin from './BootstrapMixin'; +export Button from './Button'; +export ButtonGroup from './ButtonGroup'; +export ButtonInput from './ButtonInput'; +export ButtonToolbar from './ButtonToolbar'; +export Carousel from './Carousel'; +export CarouselItem from './CarouselItem'; +export Col from './Col'; +export CollapsibleMixin from './CollapsibleMixin'; +export CollapsibleNav from './CollapsibleNav'; +export DropdownButton from './DropdownButton'; +export DropdownMenu from './DropdownMenu'; +export DropdownStateMixin from './DropdownStateMixin'; +export FadeMixin from './FadeMixin'; +export Glyphicon from './Glyphicon'; +export Grid from './Grid'; +export Input from './Input'; +export Interpolate from './Interpolate'; +export Jumbotron from './Jumbotron'; +export Label from './Label'; +export ListGroup from './ListGroup'; +export ListGroupItem from './ListGroupItem'; +export MenuItem from './MenuItem'; +export Modal from './Modal'; +export ModalTrigger from './ModalTrigger'; +export Nav from './Nav'; +export Navbar from './Navbar'; +export NavItem from './NavItem'; +export OverlayMixin from './OverlayMixin'; +export OverlayTrigger from './OverlayTrigger'; +export PageHeader from './PageHeader'; +export PageItem from './PageItem'; +export Pager from './Pager'; +export Pagination from './Pagination'; +export Panel from './Panel'; +export PanelGroup from './PanelGroup'; +export Popover from './Popover'; +export ProgressBar from './ProgressBar'; +export Row from './Row'; +export SafeAnchor from './SafeAnchor'; +export SplitButton from './SplitButton'; +export styleMaps from './styleMaps'; +export SubNav from './SubNav'; +export TabbedArea from './TabbedArea'; +export Table from './Table'; +export TabPane from './TabPane'; +export Thumbnail from './Thumbnail'; +export Tooltip from './Tooltip'; +export Well from './Well'; -export default { - Accordion, - Affix, - AffixMixin, - Alert, - BootstrapMixin, - Badge, - Button, - ButtonGroup, - ButtonInput, - ButtonToolbar, - CollapsibleNav, - Carousel, - CarouselItem, - Col, - CollapsibleMixin, - DropdownButton, - DropdownMenu, - DropdownStateMixin, - FadeMixin, - FormControls, - Glyphicon, - Grid, - Input, - Interpolate, - Jumbotron, - Label, - ListGroup, - ListGroupItem, - MenuItem, - Modal, - Nav, - Navbar, - NavItem, - ModalTrigger, - OverlayTrigger, - OverlayMixin, - PageHeader, - Panel, - PanelGroup, - PageItem, - Pager, - Pagination, - Popover, - ProgressBar, - Row, - SafeAnchor, - SplitButton, - SubNav, - TabbedArea, - Table, - TabPane, - Thumbnail, - Tooltip, - utils, - Well, - styleMaps -}; +export * as FormControls from './FormControls'; +export * as utils from './utils'; diff --git a/src/utils/index.js b/src/utils/index.js index aad9066a10..f024432a7b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,13 +1,5 @@ -import childrenValueInputValidation from './childrenValueInputValidation'; -import createChainedFunction from './createChainedFunction'; -import CustomPropTypes from './CustomPropTypes'; -import domUtils from './domUtils'; -import ValidComponentChildren from './ValidComponentChildren'; - -export default { - childrenValueInputValidation, - createChainedFunction, - CustomPropTypes, - domUtils, - ValidComponentChildren -}; +export childrenValueInputValidation from './childrenValueInputValidation'; +export createChainedFunction from './createChainedFunction'; +export CustomPropTypes from './CustomPropTypes'; +export domUtils from './domUtils'; +export ValidComponentChildren from './ValidComponentChildren'; diff --git a/test/FormControlsSpec.js b/test/FormControlsSpec.js index cc56ecd618..31c9941d30 100644 --- a/test/FormControlsSpec.js +++ b/test/FormControlsSpec.js @@ -1,6 +1,6 @@ import React from 'react'; import ReactTestUtils from 'react/lib/ReactTestUtils'; -import FormControls from '../src/FormControls'; +import * as FormControls from '../src/FormControls'; describe('Form Controls', function () { describe('Static', function () { diff --git a/tools/public-components.js b/tools/public-components.js index a030df7253..67fc67f0c7 100644 --- a/tools/public-components.js +++ b/tools/public-components.js @@ -1,5 +1,5 @@ import React from 'react'; -import index from '../src/index'; +import * as index from '../src/index'; let components = []; Object.keys(index).forEach(function (item) { From fbbb3440c0ef409613ace104791b914ca6ca61ec Mon Sep 17 00:00:00 2001 From: AlexKVal Date: Sat, 13 Jun 2015 23:21:33 +0300 Subject: [PATCH 011/128] [fixed] bower template. Removed "classnames" from dependencies because webpack bundles it. --- tools/amd/bower.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/amd/bower.json b/tools/amd/bower.json index 5c56aa2f4d..5d22b3626e 100644 --- a/tools/amd/bower.json +++ b/tools/amd/bower.json @@ -8,14 +8,12 @@ "react-bootstrap.js" ], "keywords": [ - "react", - "react-component", - "bootstrap" + <%= _.map(pkg.keywords, function(keyword) { return '"' + keyword + '"' }).join(',\n ')%> ], "ignore": [ "**/.*" ], "dependencies": { - "react": ">= 0.13.0" + "react": "<%= pkg.peerDependencies.react %>" } } From b67081b81d7c74edce6d341f8f54a880380d65b8 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Sun, 14 Jun 2015 15:21:13 -0400 Subject: [PATCH 012/128] [fixed] rootClose behavior on replaced elements Fixes #802 --- src/OverlayMixin.js | 8 ++++-- src/RootCloseWrapper.js | 31 ++++++++++++++++---- test/OverlayTriggerSpec.js | 59 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/OverlayMixin.js b/src/OverlayMixin.js index 76524ed61c..f8874a8828 100644 --- a/src/OverlayMixin.js +++ b/src/OverlayMixin.js @@ -37,7 +37,7 @@ export default { let overlay = this.renderOverlay(); - // Save reference to help testing + // Save reference for future access. if (overlay !== null) { this._overlayInstance = React.render(overlay, this._overlayTarget); } else { @@ -57,7 +57,11 @@ export default { } if (this._overlayInstance) { - return React.findDOMNode(this._overlayInstance); + if (this._overlayInstance.getWrappedDOMNode) { + return this._overlayInstance.getWrappedDOMNode(); + } else { + return React.findDOMNode(this._overlayInstance); + } } return null; diff --git a/src/RootCloseWrapper.js b/src/RootCloseWrapper.js index 146c688ab9..35fecab769 100644 --- a/src/RootCloseWrapper.js +++ b/src/RootCloseWrapper.js @@ -4,6 +4,15 @@ import EventListener from './utils/EventListener'; // TODO: Merge this logic with dropdown logic once #526 is done. +// TODO: Consider using an ES6 symbol here, once we use babel-runtime. +const CLICK_WAS_INSIDE = '__click_was_inside'; + +function suppressRootClose(event) { + // Tag the native event to prevent the root close logic on document click. + // This seems safer than using event.nativeEvent.stopImmediatePropagation(), + // which is only supported in IE >= 9. + event.nativeEvent[CLICK_WAS_INSIDE] = true; +} export default class RootCloseWrapper extends React.Component { constructor(props) { @@ -23,10 +32,8 @@ export default class RootCloseWrapper extends React.Component { } handleDocumentClick(e) { - // If the click originated from within this component, don't do anything. - // e.srcElement is required for IE8 as e.target is undefined - let target = e.target || e.srcElement; - if (domUtils.contains(React.findDOMNode(this), target)) { + // This is now the native event. + if (e[CLICK_WAS_INSIDE]) { return; } @@ -54,7 +61,21 @@ export default class RootCloseWrapper extends React.Component { } render() { - return React.Children.only(this.props.children); + // Wrap the child in a new element, so the child won't have to handle + // potentially combining multiple onClick listeners. + return ( +
    + {React.Children.only(this.props.children)} +
    + ); + } + + getWrappedDOMNode() { + // We can't use a ref to identify the wrapped child, since we might be + // stealing the ref from the owner, but we know exactly the DOM structure + // that will be rendered, so we can just do this to get the child's DOM + // node for doing size calculations in OverlayMixin. + return React.findDOMNode(this).children[0]; } componentWillUnmount() { diff --git a/test/OverlayTriggerSpec.js b/test/OverlayTriggerSpec.js index 756cb4a46d..75ae2fcf40 100644 --- a/test/OverlayTriggerSpec.js +++ b/test/OverlayTriggerSpec.js @@ -236,13 +236,66 @@ describe('OverlayTrigger', function() { }); it('Should have correct isOverlayShown state', function () { - const event = document.createEvent('HTMLEvents'); - event.initEvent('click', true, true); - document.documentElement.dispatchEvent(event); + document.documentElement.click(); + // Need to click this way for it to propagate to document element. instance.state.isOverlayShown.should.equal(testCase.shownAfterClick); }); }); }); + + describe('replaced overlay', function () { + let instance; + + beforeEach(function () { + class ReplacedOverlay extends React.Component { + constructor(props) { + super(props); + + this.handleClick = this.handleClick.bind(this); + this.state = {replaced: false}; + } + + handleClick() { + this.setState({replaced: true}); + } + + render() { + if (this.state.replaced) { + return ( +
    replaced
    + ); + } else { + return ( +
    + ); + } + } + } + + instance = ReactTestUtils.renderIntoDocument( + } + trigger='click' rootClose={true} + > + + + ); + const overlayTrigger = React.findDOMNode(instance); + ReactTestUtils.Simulate.click(overlayTrigger); + }); + + it('Should still be shown', function () { + // Need to click this way for it to propagate to document element. + const replaceOverlay = document.getElementById('replace-overlay'); + replaceOverlay.click(); + + instance.state.isOverlayShown.should.be.true; + }); + }); }); }); From c987a8c10e572d248b1615fed88bd7cf536418c1 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Mon, 15 Jun 2015 12:50:23 -0400 Subject: [PATCH 013/128] Use babel-runtime transformer for helpers Fixes #579 --- .babelrc | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.babelrc b/.babelrc index 3162e1a075..2fa4b4fe3c 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,4 @@ { - "stage": 1 + "stage": 1, + "optional": ["runtime"] } diff --git a/package.json b/package.json index b075ef7e69..86d5c5664e 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "yargs": "^3.5.4" }, "dependencies": { + "babel-runtime": "^5.1.10", "classnames": "^2.0.0" } -} \ No newline at end of file +} From 01c547f1df131ac885546c861ba5f9c49ce4ddd8 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Mon, 15 Jun 2015 14:25:05 -0400 Subject: [PATCH 014/128] [fixed] Do not use Babel cache for release build Fixes #840 --- docs/build.js | 7 +++++-- tools/build-cli.js | 9 ++++++++- tools/build.js | 13 +++++++------ tools/dist/build.js | 8 +++++--- tools/release-scripts/release.js | 6 ++++-- webpack/base.config.js | 33 +++++++++++++++++++------------- 6 files changed, 49 insertions(+), 27 deletions(-) diff --git a/docs/build.js b/docs/build.js index 6f9b8a666c..f167e47641 100644 --- a/docs/build.js +++ b/docs/build.js @@ -32,16 +32,19 @@ function generateHTML(fileName) { }); } -export default function BuildDocs({ dev }) { +export default function BuildDocs({useCache, dev}) { console.log('Building: '.cyan + 'docs'.green + (dev ? ' [DEV]'.grey : '')); + const useCacheOption = `--use-cache=${Boolean(useCache)}`; + const devOption = dev ? '' : '-p'; + return exec(`rimraf ${docsBuilt}`) .then(() => fsp.mkdir(docsBuilt)) .then(() => { let pagesGenerators = Root.getPages().map(generateHTML); return Promise.all(pagesGenerators.concat([ - exec(`webpack --config webpack.docs.js ${dev ? '' : '-p '}--bail`), + exec(`webpack --config webpack.docs.js ${useCacheOption} --bail ${devOption}`), copy(license, docsBuilt), copy(readmeSrc, readmeDest) ])); diff --git a/tools/build-cli.js b/tools/build-cli.js index 2ff0148967..204ed5cea5 100644 --- a/tools/build-cli.js +++ b/tools/build-cli.js @@ -8,10 +8,17 @@ import { setExecOptions } from './exec'; import yargs from 'yargs'; const argv = yargs + .help('h') .option('docs-only', { demand: false, default: false }) + .option('use-cache', { + type: 'boolean', + demand: false, + default: true, + describe: 'Use Babel cache when running webpack' + }) .option('verbose', { demand: false, default: false, @@ -26,7 +33,7 @@ const argv = yargs setExecOptions(argv); -let buildProcess = argv.docsOnly ? docs(argv) : build(); +let buildProcess = argv.docsOnly ? docs(argv) : build(argv); buildProcess .catch(err => { diff --git a/tools/build.js b/tools/build.js index d0e79ae53e..ee3a1bdbce 100644 --- a/tools/build.js +++ b/tools/build.js @@ -6,21 +6,22 @@ import { copy } from './fs-utils'; import { distRoot, bowerRoot } from './constants'; import { exec } from './exec'; -function forkAndBuildDocs(verbose) { +function forkAndBuildDocs({verbose, useCache}) { console.log('Building: '.cyan + 'docs'.green); - let options = verbose ? ' -- --verbose' : ''; + const verboseOption = verbose ? '--verbose' : ''; + const useCacheOption = `--use-cache=${Boolean(useCache)}`; - return exec(`npm run docs-build${options}`) + return exec(`npm run docs-build -- ${verboseOption} ${useCacheOption}`) .then(() => console.log('Built: '.cyan + 'docs'.green)); } -export default function Build(verbose) { +export default function Build({verbose, useCache} = {}) { return Promise.all([ lib(), bower(), - dist(), - forkAndBuildDocs(verbose) + dist({useCache}), + forkAndBuildDocs({verbose, useCache}) ]) .then(() => copy(distRoot, bowerRoot)); } diff --git a/tools/dist/build.js b/tools/dist/build.js index 27047db903..c47704fd27 100644 --- a/tools/dist/build.js +++ b/tools/dist/build.js @@ -1,13 +1,15 @@ import { exec } from '../exec'; import { distRoot } from '../constants'; -export default function BuildDistributable() { +export default function BuildDistributable({useCache}) { console.log('Building: '.cyan + 'distributable'.green); + const useCacheOption = `--use-cache=${Boolean(useCache)}`; + return exec(`rimraf ${distRoot}`) .then(() => Promise.all([ - exec('webpack --bail'), - exec('webpack --bail -p') + exec(`webpack ${useCacheOption} --bail`), + exec(`webpack ${useCacheOption} --bail -p`) ])) .then(() => console.log('Built: '.cyan + 'distributable'.green)); } diff --git a/tools/release-scripts/release.js b/tools/release-scripts/release.js index 927b11a0cb..26b81e351e 100644 --- a/tools/release-scripts/release.js +++ b/tools/release-scripts/release.js @@ -15,6 +15,7 @@ import { bowerRepo, bowerRoot, tmpBowerRepo, docsRoot, docsRepo, tmpDocsRepo } f const yargsConf = yargs .usage('Usage: $0 [--preid ]') + .help('h') .example('$0 minor --preid beta', 'Release with minor version bump with pre-release tag') .example('$0 major', 'Release with major version bump') .example('$0 major --dry-run', 'Release dry run with patch version bump') @@ -32,7 +33,7 @@ const yargsConf = yargs alias: 'n', demand: false, default: false, - describe: 'Execute command in dry run mode. Will not commit, tag, push, or publish anything. Userful for testing.' + describe: 'Execute command in dry run mode. Will not commit, tag, push, or publish anything. Useful for testing.' }) .option('verbose', { demand: false, @@ -66,7 +67,8 @@ preConditions() .then(v => { version = v; }) .then(() => addChangelog(version)) .then(() => { - return build(argv.verbose) + // useCache implicitly defaults to false here. + return build(argv) .catch(err => { console.log('Build failed, reverting version bump'.red); diff --git a/webpack/base.config.js b/webpack/base.config.js index bbcefeaa53..902ff6174f 100644 --- a/webpack/base.config.js +++ b/webpack/base.config.js @@ -3,28 +3,35 @@ import path from 'path'; import webpack from 'webpack'; import yargs from 'yargs'; -const babelCache = path.resolve(path.join(__dirname, '../.babel-cache')); - -if (!fs.existsSync(babelCache)) { - try { - fs.mkdirSync(babelCache); - } catch (err) { - if (err.code !== 'EEXIST') { - console.error(err.stack); - } - } -} - export const options = yargs .alias('p', 'optimize-minimize') .alias('d', 'debug') + .option('use-cache', { + type: 'boolean', + default: true + }) .option('port', { default: '8080', type: 'string' }) .argv; -export const jsLoader = `babel?cacheDirectory=${babelCache}`; +export let jsLoader = 'babel'; +if (options.useCache) { + const babelCache = path.resolve(path.join(__dirname, '../.babel-cache')); + + if (!fs.existsSync(babelCache)) { + try { + fs.mkdirSync(babelCache); + } catch (err) { + if (err.code !== 'EEXIST') { + console.error(err.stack); + } + } + } + + jsLoader += `?cacheDirectory=${babelCache}`; +} const baseConfig = { entry: undefined, From d12d59e7a4f8d696e2e85ca115f3559d8153e8e2 Mon Sep 17 00:00:00 2001 From: jquense Date: Mon, 15 Jun 2015 18:32:19 -0400 Subject: [PATCH 015/128] [changed] Enabled "loose" Babel transpilation --- .babelrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.babelrc b/.babelrc index 2fa4b4fe3c..4d5093a16f 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,5 @@ { "stage": 1, - "optional": ["runtime"] + "optional": ["runtime"], + "loose": ["all"] } From b798b7f708ebd21b039d85930923677e88a0386a Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 15 Jun 2015 18:44:25 -0600 Subject: [PATCH 016/128] Release v0.24.0-alpha.3 --- CHANGELOG-alpha.md | 16 ++++++++++++++++ package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-alpha.md b/CHANGELOG-alpha.md index 62db670361..ae8e1b9b81 100644 --- a/CHANGELOG-alpha.md +++ b/CHANGELOG-alpha.md @@ -1,3 +1,19 @@ +v0.24.0-alpha.3 - Tue, 16 Jun 2015 00:43:49 GMT +----------------------------------------------- + +- [d12d59e](../../commit/d12d59e) [changed] Enabled "loose" Babel transpilation +- [0ce46b9](../../commit/0ce46b9) [changed] only autofocus modals when enforceFocus is true (the default) +- [01c547f](../../commit/01c547f) [fixed] Do not use Babel cache for release build +- [b67081b](../../commit/b67081b) [fixed] rootClose behavior on replaced elements +- [c5855d2](../../commit/c5855d2) [changed] createChainedFunction to chain many functions, and to throw if non-functions are provided. +- [d18dadb](../../commit/d18dadb) [fixed] container content no longer shifts when overflowing +- [66f0f92](../../commit/66f0f92) [added] enforceFocus prop to Modal +- [3869ca2](../../commit/3869ca2) [fixed] Modal doesn't "jump" when container is overflowing +- [fbbb344](../../commit/fbbb344) [fixed] bower template. +- [fafe46f](../../commit/fafe46f) [changed] Use named exports in index files + + + v0.24.0-alpha.2 - Fri, 12 Jun 2015 21:52:11 GMT ----------------------------------------------- diff --git a/package.json b/package.json index 348c77c3cd..7124319646 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap", - "version": "0.24.0-alpha.2", + "version": "0.24.0-alpha.3", "description": "Bootstrap 3 components build with React", "repository": { "type": "git", From 2a0b073f5a824c76f4eb2f72209d2197fe3597e8 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 22 Jun 2015 19:49:18 -0600 Subject: [PATCH 017/128] Release v0.24.0-alpha.4 --- CHANGELOG-alpha.md | 16 ++++++++++++++++ package.json | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-alpha.md b/CHANGELOG-alpha.md index ae8e1b9b81..2df5e1fdf0 100644 --- a/CHANGELOG-alpha.md +++ b/CHANGELOG-alpha.md @@ -1,3 +1,19 @@ +v0.24.0-alpha.4 - Tue, 23 Jun 2015 01:49:18 GMT +----------------------------------------------- + +- [23f9d21](../../commit/23f9d21) [changed] Add missed prop types validations. +- [320b7ab](../../commit/320b7ab) [changed] Update fs-extra. Dev dependency. +- [2ffcf5d](../../commit/2ffcf5d) [fixed] Popovers flicker when moving mouse amongst children of the trigger +- [ccc50e0](../../commit/ccc50e0) [fixed] Accessibility: Panel header uses aria-controls +- [1e552cc](../../commit/1e552cc) [added] Accessibility: use appropriate ARIA's when an id is given to the tabbed area +- [8752754](../../commit/8752754) [added] Add linkId prop to NavItem +- [722969d](../../commit/722969d) [added] Accessibility, add tab roles when type "tabs" +- [4adaa70](../../commit/4adaa70) [added] Accessibility: role 'alert' and aria-label to Alert component +- [2594dce](../../commit/2594dce) [fixed] Modal Null Exception when react-bootstrap is loaded before the Body tag +- [e77bf88](../../commit/e77bf88) [changed] Update eslint. Dev dependency. + + + v0.24.0-alpha.3 - Tue, 16 Jun 2015 00:43:49 GMT ----------------------------------------------- diff --git a/package.json b/package.json index 28ef63b29d..18b95eaa9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap", - "version": "0.24.0-alpha.3", + "version": "0.24.0-alpha.4", "description": "Bootstrap 3 components build with React", "repository": { "type": "git", @@ -110,4 +110,4 @@ "babel-runtime": "^5.1.10", "classnames": "^2.0.0" } -} \ No newline at end of file +} From e89b9bca8150b0d376ea0de15f0267e851433be3 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Thu, 25 Jun 2015 14:05:41 -0400 Subject: [PATCH 018/128] [removed] Don't need to disable Babel cache --- docs/build.js | 5 ++--- tools/build-cli.js | 6 ------ tools/build.js | 11 +++++------ tools/dist/build.js | 8 +++----- webpack/base.config.js | 21 +-------------------- 5 files changed, 11 insertions(+), 40 deletions(-) diff --git a/docs/build.js b/docs/build.js index 0bf9095c42..1ad8005952 100644 --- a/docs/build.js +++ b/docs/build.js @@ -34,10 +34,9 @@ function generateHTML(fileName) { }); } -export default function BuildDocs({useCache, dev}) { +export default function BuildDocs({dev}) { console.log('Building: '.cyan + 'docs'.green + (dev ? ' [DEV]'.grey : '')); - const useCacheOption = `--use-cache=${Boolean(useCache)}`; const devOption = dev ? '' : '-p'; return exec(`rimraf ${docsBuilt}`) @@ -46,7 +45,7 @@ export default function BuildDocs({useCache, dev}) { let pagesGenerators = Root.getPages().map(generateHTML); return Promise.all(pagesGenerators.concat([ - exec(`webpack --config webpack.docs.js ${useCacheOption} --bail ${devOption}`), + exec(`webpack --config webpack.docs.js --bail ${devOption}`), copy(license, docsBuilt), copy(readmeSrc, readmeDest) ])); diff --git a/tools/build-cli.js b/tools/build-cli.js index c9f7c239dd..d3b69fed77 100644 --- a/tools/build-cli.js +++ b/tools/build-cli.js @@ -14,12 +14,6 @@ const argv = yargs demand: false, default: false }) - .option('use-cache', { - type: 'boolean', - demand: false, - default: true, - describe: 'Use Babel cache when running webpack' - }) .option('lib-only', { demand: false, default: false, diff --git a/tools/build.js b/tools/build.js index ee3a1bdbce..401b123a75 100644 --- a/tools/build.js +++ b/tools/build.js @@ -6,22 +6,21 @@ import { copy } from './fs-utils'; import { distRoot, bowerRoot } from './constants'; import { exec } from './exec'; -function forkAndBuildDocs({verbose, useCache}) { +function forkAndBuildDocs({verbose}) { console.log('Building: '.cyan + 'docs'.green); const verboseOption = verbose ? '--verbose' : ''; - const useCacheOption = `--use-cache=${Boolean(useCache)}`; - return exec(`npm run docs-build -- ${verboseOption} ${useCacheOption}`) + return exec(`npm run docs-build -- ${verboseOption}`) .then(() => console.log('Built: '.cyan + 'docs'.green)); } -export default function Build({verbose, useCache} = {}) { +export default function Build(options) { return Promise.all([ lib(), bower(), - dist({useCache}), - forkAndBuildDocs({verbose, useCache}) + dist(), + forkAndBuildDocs(options) ]) .then(() => copy(distRoot, bowerRoot)); } diff --git a/tools/dist/build.js b/tools/dist/build.js index c47704fd27..624327e7a8 100644 --- a/tools/dist/build.js +++ b/tools/dist/build.js @@ -1,15 +1,13 @@ import { exec } from '../exec'; import { distRoot } from '../constants'; -export default function BuildDistributable({useCache}) { +export default function BuildDistributable() { console.log('Building: '.cyan + 'distributable'.green); - const useCacheOption = `--use-cache=${Boolean(useCache)}`; - return exec(`rimraf ${distRoot}`) .then(() => Promise.all([ - exec(`webpack ${useCacheOption} --bail`), - exec(`webpack ${useCacheOption} --bail -p`) + exec(`webpack --bail`), + exec(`webpack --bail -p`) ])) .then(() => console.log('Built: '.cyan + 'distributable'.green)); } diff --git a/webpack/base.config.js b/webpack/base.config.js index 902ff6174f..4a6010214e 100644 --- a/webpack/base.config.js +++ b/webpack/base.config.js @@ -6,32 +6,13 @@ import yargs from 'yargs'; export const options = yargs .alias('p', 'optimize-minimize') .alias('d', 'debug') - .option('use-cache', { - type: 'boolean', - default: true - }) .option('port', { default: '8080', type: 'string' }) .argv; -export let jsLoader = 'babel'; -if (options.useCache) { - const babelCache = path.resolve(path.join(__dirname, '../.babel-cache')); - - if (!fs.existsSync(babelCache)) { - try { - fs.mkdirSync(babelCache); - } catch (err) { - if (err.code !== 'EEXIST') { - console.error(err.stack); - } - } - } - - jsLoader += `?cacheDirectory=${babelCache}`; -} +export const jsLoader = 'babel?cacheDirectory'; const baseConfig = { entry: undefined, From b1b6a4cdc312d919fa8c1fd274a2f748397ecff4 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Thu, 25 Jun 2015 14:21:42 -0400 Subject: [PATCH 019/128] [changed] Add two-release deprecation policy Fixes #863 --- CONTRIBUTING.md | 10 ++++++++++ MAINTAINING.md | 14 +++++++------- README.md | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e063b3342a..0f939d1f8d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,6 +82,16 @@ Also Bootstrap mentions http://getbootstrap.com/getting-started/#examples as examples of things you can do, but they are not part of the core library, therefore this project is the wrong place to implement them. +## Breaking changes + +Breaking changes should be accompanied with deprecations of removed +functionality. Prior to the 1.0.0 release, we aim to follow React's example of +taking two Minor releases to break old functionality. As such, changes that +intend to remove or change public APIs should be be submitted against the +`vX-rc` branch, and should be accompanied with deprecation warnings on the old +APIs. The deprecated APIs themselves should not be removed until the Minor +release after that. + ## Collaborators diff --git a/MAINTAINING.md b/MAINTAINING.md index e6c5f29b14..402e8379ae 100644 --- a/MAINTAINING.md +++ b/MAINTAINING.md @@ -95,13 +95,13 @@ then be re-applied and released with the proper version bump. ### Release Candidates In an effort to reduce the frequency with which we introduce breaking changes we -should do our best to first push deprecation warnings in a Minor or Patch -release. Also, Pull Requests with breaking changes should be submitted against -the `vX-rc` branch, where X is the next Major version. Which we will in turn -release as an `alpha` release of the next Major version. When we are ready to -release the next Major version bump we will merge the `vX-rc` branch into the -`master` branch and cut a `beta` release. Once bugs have been addressed with -the `beta` release then we will release the Major version bump. +should do our best to first push deprecation warnings in a Minor release. Also, +Pull Requests with breaking changes should be submitted against the `vX-rc` +branch, where X is the next Major version. Which we will in turn release as an +`alpha` release of the next Major version. When we are ready to release the next +Major version bump we will merge the `vX-rc` branch into the `master` branch and +cut a `beta` release. Once bugs have been addressed with the `beta` release +then we will release the Major version bump. ### Live releasing the documentation diff --git a/README.md b/README.md index f05ea61500..3b8833288e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [![devDependency Status][dev-deps-badge]][dev-deps] [![peerDependency Status][peer-deps-badge]][peer-deps] -__Under active development - APIs will change.__ Check out the [1.0.0 Roadmap](https://github.com/react-bootstrap/react-bootstrap/wiki#100-roadmap) and [Contributing Guidelines][contributing] to see where you can help out. Prior to the 1.0.0 release, breaking changes should result in a Minor version bump. +__Under active development - APIs will change.__ Check out the [1.0.0 Roadmap](https://github.com/react-bootstrap/react-bootstrap/wiki#100-roadmap) and [Contributing Guidelines][contributing] to see where you can help out. Prior to the 1.0.0 release, deprecations or breaking changes should result in a Minor version bump. ## Docs From 0ce9db5812fb2b3cf712905563e7c7452cb1b875 Mon Sep 17 00:00:00 2001 From: AlexKVal Date: Fri, 26 Jun 2015 10:46:08 +0300 Subject: [PATCH 020/128] Fix eslint warnings. --- webpack/base.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/webpack/base.config.js b/webpack/base.config.js index 4a6010214e..102dde14a7 100644 --- a/webpack/base.config.js +++ b/webpack/base.config.js @@ -1,5 +1,3 @@ -import fs from 'fs'; -import path from 'path'; import webpack from 'webpack'; import yargs from 'yargs'; From 75c7270a85af46ed1fed8466e15f0127b9b8a2c7 Mon Sep 17 00:00:00 2001 From: jquense Date: Sat, 4 Jul 2015 10:54:40 -0400 Subject: [PATCH 021/128] Merge tag 'v0.23.7' into v0.24-rc Conflicts: package.json index.js --- .eslintrc | 2 + CHANGELOG.md | 29 +++ CONTRIBUTING.md | 48 +++- README.md | 4 + appveyor.yml | 2 +- docs/assets/style.css | 10 + docs/build.js | 11 +- docs/examples/.eslintrc | 1 + docs/examples/ModalContained.js | 48 ++-- docs/examples/ModalCustomSizing.js | 15 +- docs/examples/ModalDefaultSizing.js | 65 +++-- docs/examples/ModalOverlayMixin.js | 43 ---- docs/examples/ModalStatic.js | 19 +- docs/examples/ModalTrigger.js | 97 ++++--- docs/examples/Overlay.js | 43 ++++ docs/examples/OverlayCustom.js | 45 ++++ docs/examples/PopoverContained.js | 44 +++- docs/examples/TooltipBasic.js | 17 +- docs/examples/TooltipInCopy.js | 12 +- docs/examples/TooltipPositioned.js | 16 +- docs/generate-metadata.js | 104 ++++++++ docs/server.js | 29 ++- docs/src/ComponentsPage.js | 264 +++++++++++++++++-- docs/src/NavMain.js | 4 + docs/src/PropTable.js | 172 +++++++++++++ docs/src/ReactPlayground.js | 13 + docs/src/Root.js | 13 +- docs/src/Routes.js | 2 + docs/src/Samples.js | 7 +- docs/src/SupportPage.js | 48 ++++ karma.conf.js | 1 - package.json | 16 +- src/BootstrapMixin.js | 12 + src/Button.js | 3 +- src/ButtonGroup.js | 4 + src/Col.js | 3 +- src/Grid.js | 3 +- src/Jumbotron.js | 15 +- src/Modal.js | 218 +++++++++++++--- src/ModalBody.js | 26 ++ src/ModalFooter.js | 27 ++ src/ModalHeader.js | 56 +++++ src/ModalTitle.js | 26 ++ src/ModalTrigger.js | 46 +++- src/Navbar.js | 3 +- src/Overlay.js | 63 +++++ src/OverlayMixin.js | 58 +++-- src/OverlayTrigger.js | 335 +++++++++++-------------- src/Popover.js | 36 ++- src/Portal.js | 34 +++ src/Position.js | 102 ++++++++ src/Row.js | 3 +- src/Tooltip.js | 34 ++- src/index.js | 12 + src/utils/CustomPropTypes.js | 65 ++++- src/utils/domUtils.js | 19 +- src/utils/overlayPositionUtils.js | 113 +++++++++ test/JumbotronSpec.js | 11 + test/ModalSpec.js | 95 +++++-- test/ModalTriggerSpec.js | 19 ++ test/NavbarSpec.js | 2 +- test/OverlayMixinSpec.js | 6 + test/OverlayTriggerSpec.js | 101 +------- test/PortalSpec.js | 78 ++++++ test/index.js | 4 + test/utils/CustomPropTypesSpec.js | 76 ++++-- test/utils/overlayPositionUtilsSpec.js | 92 +++++++ tools/promisify.js | 17 ++ 68 files changed, 2456 insertions(+), 605 deletions(-) delete mode 100644 docs/examples/ModalOverlayMixin.js create mode 100644 docs/examples/Overlay.js create mode 100644 docs/examples/OverlayCustom.js create mode 100644 docs/generate-metadata.js create mode 100644 docs/src/PropTable.js create mode 100644 docs/src/SupportPage.js create mode 100644 src/ModalBody.js create mode 100644 src/ModalFooter.js create mode 100644 src/ModalHeader.js create mode 100644 src/ModalTitle.js create mode 100644 src/Overlay.js create mode 100644 src/Portal.js create mode 100644 src/Position.js create mode 100644 src/utils/overlayPositionUtils.js create mode 100644 test/PortalSpec.js create mode 100644 test/utils/overlayPositionUtilsSpec.js create mode 100644 tools/promisify.js diff --git a/.eslintrc b/.eslintrc index 06e1f98f2a..d52de5d000 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,10 +12,12 @@ "babel" ], "rules": { + "constructor-super": 2, "comma-spacing": 2, "comma-style": [2, "last"], "one-var": [2, { "initialized": "never" }], "key-spacing": 0, + "no-this-before-super": 2, "no-underscore-dangle": 0, "no-unused-vars": [2, { "vars": "all", "args": "none" }], "no-var": 2, diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e91ee2f30..59cae1dbfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +v0.23.7 - Wed, 01 Jul 2015 15:18:30 GMT +--------------------------------------- + +- [35ea201](../../commit/35ea201) [fixed] Accidental breaking change in Modal trigger + + + +v0.23.6 - Wed, 01 Jul 2015 00:48:02 GMT +--------------------------------------- + +- [1b1af04](../../commit/1b1af04) [changed] deprecate ModalTrigger +- [83b4cbc](../../commit/83b4cbc) [changed] Modal doesn't require ModalTrigger +- [d70f617](../../commit/d70f617) [changed] tooltips and popovers required id's for a11y +- [389cf3f](../../commit/389cf3f) [changed] Deprecate OverlayTrigger positioning api and "manual" trigger +- [5eb8666](../../commit/5eb8666) [added] Overlay component +- [1638f69](../../commit/1638f69) [added] Position component for custom Overlays +- [f799110](../../commit/f799110) [added] Portal component; replaces OverlayMixin +- [97ef415](../../commit/97ef415) [fixed] Modal won't steal focus from children +- [a8b177a](../../commit/a8b177a) [fixed] Stack overflow with nested Modals +- [3caa866](../../commit/3caa866) [changed] Update babel-loader +- [6ffa325](../../commit/6ffa325) [fixed] 'componentClass' property type is 'elementType' now +- [0e5980f](../../commit/0e5980f) [added] 'elementType' custom prop type validator +- [8f582d2](../../commit/8f582d2) [changed] Update karma-chrome-launcher. Dev dependency +- [d4089d0](../../commit/d4089d0) [changed] Update eslint-plugin-mocha. Dev dependency +- [fd547f4](../../commit/fd547f4) [changed] Update karma-mocha. Dev dependency. +- [c5797e8](../../commit/c5797e8) [added] componentClass prop to Jumbotron + + + v0.23.5 - Tue, 23 Jun 2015 01:31:35 GMT --------------------------------------- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f939d1f8d..dad46214b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,9 +17,10 @@ and submitting pull requests, but please respect the following restrictions: - Please do not use the issue tracker for personal support requests. Stack Overflow ([react-bootstrap](http://stackoverflow.com/questions/tagged/react-bootstrap) - tag), [Slack](http://www.reactiflux.com/) or - [gitter](https://gitter.im/react-bootstrap/react-bootstrap) are better places - to get help. + tag), [Slack](http://www.reactiflux.com/), + [gitter](https://gitter.im/react-bootstrap/react-bootstrap), or + [Thinkful](http://start.thinkful.com/react/?utm_source=github&utm_medium=badge&utm_campaign=react-bootstrap) + are better places to get help. - Please do not open issues or pull requests regarding the code in React or Bootstrap (open them in their respective repositories). @@ -68,11 +69,52 @@ doesn't make sense to do this, then it doesn't make sense to use `[changed]` or `[removed]` :). For further reading on writing a well formed commit message, check out these [5 useful tips for a better commit message][commit-message] +### Using `[changed]` with development dependencies updates + +Use `[changed]` if dev-dependency has impact on the resulting code or API. +`babel` is a good example of such dev-dependency. +`chai`, `colors`, `express` or `eslint` are good examples when there is +no need to add `[changed]`. + +## Visual Changes + +When making a visual change, if at all feasible please provide screenshots +and/or screencasts of the proposed change. This will help us to understand the +desired change easier. + ## Docs Please update the docs with any API changes, the code and docs should always be in sync. +Component prop documentation is generated automatically from the React components +and their leading comments. Please make sure to provide comments for any `propTypes` you add +or change in a Component. + +```js +propTypes: { + /** + * Sets the visibility of the Component + */ + show: React.PropTypes.bool, + + /** + * A callback fired when the visibility changes + * @type {func} + * @required + */ + onHide: myCustomPropType +} +``` + +There are a few caveats to this format that differ from conventional JSDoc comments. + +- Only specific doclets (the @ things) should be used, and only when the data cannot be parsed from the component itself + - `@type`: Override the "type", use the same names as the default React PropTypes: string, func, bool, number, object. You can express enum and oneOfType types, Like `{("optionA"|"optionB")}`. + - `@required`: to mark a prop as required (use the normal React isRequired if possible) + - `@private`: Will hide the prop in the documentation +- All description text should be above the doclets. + ## Implement additional components and features This project is seeking parity with the core Bootstrap library. diff --git a/README.md b/README.md index 3b8833288e..3364b9ece7 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![HuBoard][huboard-badge]][huboard] [![Gitter][gitter-badge]][gitter] +[![Thinkful][thinkful-badge]][thinkful] [![NPM version][npm-badge]][npm] [![Bower version][bower-badge]][bower] @@ -75,5 +76,8 @@ Yes please! See the [contributing guidelines][contributing] for details. [huboard-badge]: https://img.shields.io/badge/Hu-Board-7965cc.svg [huboard]: https://huboard.com/react-bootstrap/react-bootstrap +[thinkful-badge]: https://tf-assets-staging.s3.amazonaws.com/badges/thinkful_repo_badge.svg +[thinkful]: http://start.thinkful.com/react/?utm_source=github&utm_medium=badge&utm_campaign=react-bootstrap + [appveyor-badge]: https://ci.appveyor.com/api/projects/status/ylitpyo6n5yq1s6i/branch/master?svg=true [appveyor]: https://ci.appveyor.com/project/react-bootstrap/react-bootstrap/branch/master diff --git a/appveyor.yml b/appveyor.yml index cac1fa5920..430ba4a1af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ version: '{build}' install: # Get the latest stable version of io.js - ps: Install-Product node '' - - npm -g install npm@latest + - npm -g install npm@2.11.x - set PATH=%APPDATA%\npm;%PATH% - node --version - npm --version diff --git a/docs/assets/style.css b/docs/assets/style.css index 645481e0bf..d03afccb44 100644 --- a/docs/assets/style.css +++ b/docs/assets/style.css @@ -162,3 +162,13 @@ body { position: absolute; } +.prop-table { + background-color: white; +} + +.bs-example.tooltip-static .tooltip { + position: relative; + display: inline-block; + margin: 5px 10px; + +} diff --git a/docs/build.js b/docs/build.js index 1ad8005952..98a01c94e1 100644 --- a/docs/build.js +++ b/docs/build.js @@ -6,6 +6,7 @@ import Root from './src/Root'; import fsp from 'fs-promise'; import { copy } from '../tools/fs-utils'; import { exec } from '../tools/exec'; +import metadata from './generate-metadata'; const repoRoot = path.resolve(__dirname, '../'); const docsBuilt = path.join(repoRoot, 'docs-built'); @@ -21,12 +22,12 @@ const readmeDest = path.join(docsBuilt, 'README.md'); * @return {Promise} promise * @internal */ -function generateHTML(fileName) { +function generateHTML(fileName, propData) { return new Promise((resolve, reject) => { const urlSlug = fileName === 'index.html' ? '/' : `/${fileName}`; Router.run(routes, urlSlug, Handler => { - let html = React.renderToString(React.createElement(Handler)); + let html = React.renderToString(React.createElement(Handler, { propData })); html = '' + html; let write = fsp.writeFile(path.join(docsBuilt, fileName), html); resolve(write); @@ -41,8 +42,10 @@ export default function BuildDocs({dev}) { return exec(`rimraf ${docsBuilt}`) .then(() => fsp.mkdir(docsBuilt)) - .then(() => { - let pagesGenerators = Root.getPages().map(generateHTML); + .then(metadata) + .then(propData => { + + let pagesGenerators = Root.getPages().map( page => generateHTML(page, propData)); return Promise.all(pagesGenerators.concat([ exec(`webpack --config webpack.docs.js --bail ${devOption}`), diff --git a/docs/examples/.eslintrc b/docs/examples/.eslintrc index 6f2fa1bdf5..9dbc49c623 100644 --- a/docs/examples/.eslintrc +++ b/docs/examples/.eslintrc @@ -36,6 +36,7 @@ "ModalTrigger", "OverlayTrigger", "OverlayMixin", + "Overlay", "PageHeader", "PageItem", "Pager", diff --git a/docs/examples/ModalContained.js b/docs/examples/ModalContained.js index 626f17db42..aeab199ef7 100644 --- a/docs/examples/ModalContained.js +++ b/docs/examples/ModalContained.js @@ -9,28 +9,40 @@ * } */ -const ContainedModal = React.createClass({ - render() { - return ( - -
    - Elit est explicabo ipsum eaque dolorem blanditiis doloribus sed id ipsam, beatae, rem fuga id earum? Inventore et facilis obcaecati. -
    -
    - -
    -
    - ); - } -}); - const Trigger = React.createClass({ + getInitialState(){ + return { show: false }; + }, + render() { + let close = e => this.setState({ show: false}); + return (
    - } container={this}> - - + + + + + Contained Modal + + + Elit est explicabo ipsum eaque dolorem blanditiis doloribus sed id ipsam, beatae, rem fuga id earum? Inventore et facilis obcaecati. + + + + +
    ); } diff --git a/docs/examples/ModalCustomSizing.js b/docs/examples/ModalCustomSizing.js index 5ddeab19de..4122e352d4 100644 --- a/docs/examples/ModalCustomSizing.js +++ b/docs/examples/ModalCustomSizing.js @@ -1,8 +1,11 @@ const MyModal = React.createClass({ render() { return ( - -
    + + + Modal heading + +

    Wrapped Text

    Ipsum molestiae natus adipisci modi eligendi? Debitis amet quae unde commodi aspernatur enim, consectetur. Cumque deleniti temporibus ipsam atque a dolores quisquam quisquam adipisci possimus laboriosam. Quibusdam facilis doloribus debitis! Sit quasi quod accusamus eos quod. Ab quos consequuntur eaque quo rem! Mollitia reiciendis porro quo magni incidunt dolore amet atque facilis ipsum deleniti rem! Dolores debitis voluptatibus ipsum dicta. Dolor quod amet ab sint esse distinctio tenetur. Veritatis laudantium quibusdam quidem corporis architecto veritatis. Ex facilis minima beatae sunt perspiciatis placeat. Quasi corporis @@ -19,10 +22,10 @@ const MyModal = React.createClass({ magni delectus maxime. Sit odit provident vel magnam quod. Possimus eligendi non corrupti tenetur culpa accusantium quod quis. Voluptatum quaerat animi dolore maiores molestias voluptate? Necessitatibus illo omnis laborum hic enim minima! Similique. Dolor voluptatum reprehenderit nihil adipisci aperiam voluptatem soluta magnam accusamus iste incidunt tempore consequatur illo illo odit. Asperiores nesciunt iusto nemo animi ratione. Sunt odit similique doloribus temporibus reiciendis! Ullam. Dolor dolores veniam animi sequi dolores molestias voluptatem iure velit. Elit dolore quaerat incidunt enim aut distinctio. Ratione molestiae laboriosam similique laboriosam eum et nemo expedita. Consequuntur perspiciatis cumque dolorem.

    -
    -
    - -
    + + + +
    ); } diff --git a/docs/examples/ModalDefaultSizing.js b/docs/examples/ModalDefaultSizing.js index aabd5dfc1c..19c9ffe0d3 100644 --- a/docs/examples/ModalDefaultSizing.js +++ b/docs/examples/ModalDefaultSizing.js @@ -1,8 +1,11 @@ const MySmallModal = React.createClass({ render() { return ( - -
    + + + Modal heading + +

    Wrapped Text

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    @@ -13,10 +16,10 @@ const MySmallModal = React.createClass({

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    -
    -
    - -
    + + + +
    ); } @@ -25,8 +28,11 @@ const MySmallModal = React.createClass({ const MyLargeModal = React.createClass({ render() { return ( - -
    + + + Modal heading + +

    Wrapped Text

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    @@ -37,24 +43,37 @@ const MyLargeModal = React.createClass({

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    -
    -
    - -
    + + + +
    ); } }); -const overlayTriggerInstance = ( - - }> - - - }> - - - -); +const App = React.createClass({ + getInitialState(){ + return { smShow: false, lgShow: false }; + }, + render(){ + let smClose = e => this.setState({ smShow: false }); + let lgClose = e => this.setState({ lgShow: false }); -React.render(overlayTriggerInstance, mountNode); + return ( + + + + + + + + ); + } +}); + +React.render(, mountNode); diff --git a/docs/examples/ModalOverlayMixin.js b/docs/examples/ModalOverlayMixin.js deleted file mode 100644 index 3a47c75b33..0000000000 --- a/docs/examples/ModalOverlayMixin.js +++ /dev/null @@ -1,43 +0,0 @@ -// Our custom component is managing whether the Modal is visible -const CustomModalTrigger = React.createClass({ - mixins: [OverlayMixin], - - getInitialState() { - return { - isModalOpen: false - }; - }, - - handleToggle() { - this.setState({ - isModalOpen: !this.state.isModalOpen - }); - }, - - render() { - return ( - - ); - }, - - // This is called by the `OverlayMixin` when this component - // is mounted or updated and the return value is appended to the body. - renderOverlay() { - if (!this.state.isModalOpen) { - return ; - } - - return ( - -
    - This modal is controlled by our custom trigger component. -
    -
    - -
    -
    - ); - } -}); - -React.render(, mountNode); diff --git a/docs/examples/ModalStatic.js b/docs/examples/ModalStatic.js index f01a4d058e..138c55f050 100644 --- a/docs/examples/ModalStatic.js +++ b/docs/examples/ModalStatic.js @@ -1,18 +1,25 @@ const modalInstance = (
    - -
    + onHide={function(){}}> + + + Modal title + + + One fine body... -
    -
    + + + -
    +
    ); diff --git a/docs/examples/ModalTrigger.js b/docs/examples/ModalTrigger.js index 36b859d0df..4a25c06f86 100644 --- a/docs/examples/ModalTrigger.js +++ b/docs/examples/ModalTrigger.js @@ -1,42 +1,67 @@ -const MyModal = React.createClass({ +const Example = React.createClass({ + + getInitialState(){ + return { showModal: false }; + }, + + close(){ + this.setState({ showModal: false }); + }, + + open(){ + this.setState({ showModal: true }); + }, + render() { + let popover = very popover. such engagement; + let tooltip = wow.; + return ( - -
    -

    Text in a modal

    -

    Duis mollis, est non commodo luctus, nisi erat porttitor ligula.

    - -

    Popover in a modal

    -

    TODO

    - -

    Tooltips in a modal

    -

    TODO

    - -
    - -

    Overflowing text to show scroll behavior

    -

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    -

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    -

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    -

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    -

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    -

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    -

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    -

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    -

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    -
    -
    - -
    -
    +
    +

    Click to get the full Modal experience!

    + + + + + + Modal heading + + +

    Text in a modal

    +

    Duis mollis, est non commodo luctus, nisi erat porttitor ligula.

    + +

    Popover in a modal

    +

    there is a popover here

    + +

    Tooltips in a modal

    +

    there is a tooltip here

    + +
    + +

    Overflowing text to show scroll behavior

    +

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    +

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    +

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    +

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    +

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    +

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    +

    Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.

    +

    Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.

    +

    Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus auctor fringilla.

    +
    + + + +
    +
    ); } }); -const overlayTriggerInstance = ( - }> - - -); - -React.render(overlayTriggerInstance, mountNode); +React.render(, mountNode); diff --git a/docs/examples/Overlay.js b/docs/examples/Overlay.js new file mode 100644 index 0000000000..4f5f1b9fde --- /dev/null +++ b/docs/examples/Overlay.js @@ -0,0 +1,43 @@ + +const Example = React.createClass({ + getInitialState(){ + return { show: true }; + }, + + toggle(){ + this.setState({ show: !this.state.show }); + }, + + render(){ + const tooltip = Tooltip overload!; + + const sharedProps = { + show: this.state.show, + container: this, + target: props => React.findDOMNode(this.refs.target) + }; + + return ( +
    + + + + { tooltip } + + + { tooltip } + + + { tooltip } + + + { tooltip } + +
    + ); + } +}); + +React.render(, mountNode); diff --git a/docs/examples/OverlayCustom.js b/docs/examples/OverlayCustom.js new file mode 100644 index 0000000000..20230b56c6 --- /dev/null +++ b/docs/examples/OverlayCustom.js @@ -0,0 +1,45 @@ + +const Example = React.createClass({ + getInitialState(){ + return { show: true }; + }, + + toggle(){ + this.setState({ show: !this.state.show }); + }, + + render(){ + const style = { + position: 'absolute', + backgroundColor: '#EEE', + boxShadow: '0 5px 10px rgba(0, 0, 0, 0.2)', + border: '1px solid #CCC', + borderRadius: 3, + marginLeft: -5, + marginTop: 5, + padding: 10 + }; + + return ( +
    + + + this.setState({ show: false })} + placement="right" + container={this} + target={ props => React.findDOMNode(this.refs.target)} + > +
    + Holy guacamole! Check this info. +
    +
    +
    + ); + } +}); + +React.render(, mountNode); diff --git a/docs/examples/PopoverContained.js b/docs/examples/PopoverContained.js index afd3019e3e..3e0c036cee 100644 --- a/docs/examples/PopoverContained.js +++ b/docs/examples/PopoverContained.js @@ -1,13 +1,33 @@ -const positionerInstance = ( - - Holy guacamole! Check this info.} - > - - - -); +class Example extends React.Component { + constructor(props, context){ + super(props, context); + this.state = { show: false }; + } + render(){ -React.render(positionerInstance, mountNode); + return ( + + + + React.findDOMNode(this.state.target)} + placement='bottom' + container={mountNode} + containerPadding={20} + > + + Holy guacamole! Check this info. + + + + ); + } +} + +React.render(, mountNode); diff --git a/docs/examples/TooltipBasic.js b/docs/examples/TooltipBasic.js index f2df4f4a66..ff21989d5e 100644 --- a/docs/examples/TooltipBasic.js +++ b/docs/examples/TooltipBasic.js @@ -1,7 +1,18 @@ const tooltipInstance = ( -
    - - Holy guacamole! Check this info. +
    + + Tooltip right + + + Tooltip top + + + + Tooltip left + + + + Tooltip bottom
    ); diff --git a/docs/examples/TooltipInCopy.js b/docs/examples/TooltipInCopy.js index 8842122aee..40c8d09ab0 100644 --- a/docs/examples/TooltipInCopy.js +++ b/docs/examples/TooltipInCopy.js @@ -1,9 +1,9 @@ const LinkWithTooltip = React.createClass({ render() { - let tooltip = {this.props.tooltip}; + let tooltip = {this.props.tooltip}; return ( - + {this.props.children} ); @@ -12,7 +12,13 @@ const LinkWithTooltip = React.createClass({ const copyInstance = (

    - Tight pants next level keffiyeh you probably haven't heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit american apparel Another tooltip} href='#'>have a terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney's cleanse vegan chambray. A really ironic artisan whatever keytar, scenester farm-to-table banksy Austin twitter handle freegan cred raw denim single-origin coffee viral. + Tight pants next level keffiyeh you probably haven't + heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's + fixie sustainable quinoa 8-bit american apparel Another tooltip} href='#'>have a + terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four + loko mcsweeney's cleanse vegan chambray. A really ironic artisan whatever keytar, + scenester farm-to-table banksy Austin twitter handle freegan + cred raw denim single-origin coffee viral.

    ); diff --git a/docs/examples/TooltipPositioned.js b/docs/examples/TooltipPositioned.js index cedb961fa9..5f94bca114 100644 --- a/docs/examples/TooltipPositioned.js +++ b/docs/examples/TooltipPositioned.js @@ -1,15 +1,23 @@ + +const tooltip = ( + Holy guacamole! Check this info. +); + const positionerInstance = ( - Holy guacamole! Check this info.
    }> + - Holy guacamole! Check this info.}> + + - Holy guacamole! Check this info.}> + + - Holy guacamole! Check this info.}> + + diff --git a/docs/generate-metadata.js b/docs/generate-metadata.js new file mode 100644 index 0000000000..bf30c07d0e --- /dev/null +++ b/docs/generate-metadata.js @@ -0,0 +1,104 @@ +import metadata from 'react-component-metadata'; +import glob from 'glob'; +import fsp from 'fs-promise'; +import promisify from '../tools/promisify'; +import marked from 'marked'; + +marked.setOptions({ + xhtml: true +}); + +let globp = promisify(glob); + +// removes doclet syntax from comments +let cleanDoclets = desc => { + let idx = desc.indexOf('@'); + return (idx === -1 ? desc : desc.substr(0, idx )).trim(); +}; + +let cleanDocletValue = str => str.trim().replace(/^\{/, '').replace(/\}$/, ''); + + +let isLiteral = str => (/^('|")/).test(str.trim()); + +/** + * parse out description doclets to an object and remove the comment + * + * @param {ComponentMetadata|PropMetadata} obj + */ +function parseDoclets(obj){ + obj.doclets = metadata.parseDoclets(obj.desc || '') || {}; + obj.desc = cleanDoclets(obj.desc || ''); + obj.descHtml = marked(obj.desc || ''); +} + +/** + * Reads the JSDoc "doclets" and applies certain ones to the prop type data + * This allows us to "fix" parsing errors, or unparsable data with JSDoc style comments + * + * @param {Object} props Object Hash of the prop metadata + * @param {String} propName + */ +function applyPropDoclets(props, propName){ + let prop = props[propName]; + let doclets = prop.doclets; + let value; + + // the @type doclet to provide a prop type + // Also allows enums (oneOf) if string literals are provided + // ex: @type {("optionA"|"optionB")} + if (doclets.type) { + value = cleanDocletValue(doclets.type); + prop.type.name = value; + + if ( value[0] === '(' ) { + value = value.substring(1, value.length - 1).split('|'); + + prop.type.value = value; + prop.type.name = value.every(isLiteral) ? 'enum' : 'union'; + } + } + + // Use @required to mark a prop as required + // useful for custom propTypes where there isn't a `.isRequired` addon + if ( doclets.required) { + prop.required = true; + } +} + + +export default function generate(destination, options = { mixins: true }){ + + return globp(__dirname + '/../src/**/*.js') //eslint-disable-line no-path-concat + .then( files => { + + let results = files.map( + filename => fsp.readFile(filename).then(content => metadata(content, options)) ); + + return Promise.all(results) + .then( data => { + let result = {}; + + data.forEach(components => { + Object.keys(components).forEach(key => { + const component = components[key]; + + parseDoclets(component); + + Object.keys(component.props).forEach( propName => { + const prop = component.props[propName]; + + parseDoclets(prop); + applyPropDoclets(component.props, propName); + }); + }); + + //combine all the component metadata into one large object + result = { ...result, ...components }; + }); + + return result; + }) + .catch( e => setTimeout(()=> { throw e; })); + }); +} diff --git a/docs/server.js b/docs/server.js index 5d0157dfca..66b49144c8 100644 --- a/docs/server.js +++ b/docs/server.js @@ -5,6 +5,8 @@ import path from 'path'; import Router from 'react-router'; import routes from './src/Routes'; import httpProxy from 'http-proxy'; + +import metadata from './generate-metadata'; import ip from 'ip'; const development = process.env.NODE_ENV !== 'production'; @@ -21,20 +23,27 @@ if (development) { proxy.web(req, res, { target }); }); - app.use(function renderApp(req, res) { - res.header('Access-Control-Allow-Origin', target); - res.header('Access-Control-Allow-Headers', 'X-Requested-With'); - - Router.run(routes, req.url, Handler => { - let html = React.renderToString(); - res.send('' + html); - }); - }); - proxy.on('error', function(e) { console.log('Could not connect to webpack proxy'.red); console.log(e.toString().red); }); + + console.log('Prop data generation started:'.green); + + metadata().then( props => { + console.log('Prop data generation finished:'.green); + + app.use(function renderApp(req, res) { + res.header('Access-Control-Allow-Origin', target); + res.header('Access-Control-Allow-Headers', 'X-Requested-With'); + + Router.run(routes, req.url, Handler => { + let html = React.renderToString(); + res.send('' + html); + }); + }); + }); + } else { app.use(express.static(path.join(__dirname, '../docs-built'))); } diff --git a/docs/src/ComponentsPage.js b/docs/src/ComponentsPage.js index 93aa06266e..c3166eca4d 100644 --- a/docs/src/ComponentsPage.js +++ b/docs/src/ComponentsPage.js @@ -1,4 +1,4 @@ -/* eslint no-path-concat: 0, react/no-did-mount-set-state: 0 */ +/* eslint react/no-did-mount-set-state: 0 */ import React from 'react'; @@ -9,6 +9,7 @@ import NavItem from '../../src/NavItem'; import NavMain from './NavMain'; import PageHeader from './PageHeader'; +import PropTable from './PropTable'; import PageFooter from './PageFooter'; import ReactPlayground from './ReactPlayground'; import Samples from './Samples'; @@ -67,6 +68,7 @@ const ComponentsPage = React.createClass({ flush against each other. To preserve the spacing between multiple inline buttons, wrap your button group in {''}.

    +

    Sizes

    Fancy larger or smaller buttons? Add bsSize="large", bsSize="small", or bsSize="xsmall" for additional sizes.

    @@ -99,6 +101,10 @@ const ComponentsPage = React.createClass({ feedback as to the loading state, this can easily be done by updating your {'