diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index cb9e0c2..0000000 --- a/.eslintignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -lib/ -amd/ -karma.conf.js diff --git a/.eslintrc b/.eslintrc index c048662..15139dc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,41 +1,7 @@ { - "extends": ["eslint-config-airbnb"], - "env": { - "browser": true, - "node": true - }, - "ecmaFeatures": { - "jsx": true - }, - "parser": "babel-eslint", - "plugins": [ - "react", - "babel" - ], + "extends": "airbnb", "rules": { "comma-dangle": 0, - "comma-spacing": 1, - "key-spacing": 0, - "no-eq-null": 0, - "no-param-reassign": 0, - "no-underscore-dangle": 0, - "no-undef": 2, - "no-unused-vars": [2, { "vars": "all", "args": "none" }], - "no-var": 2, - "babel/object-shorthand": 2, - "quotes": [1, "single", "avoid-escape"], - "react/display-name": 0, - "react/jsx-no-undef": 2, - "react/jsx-quotes": 0, - "react/jsx-uses-react": 2, - "react/no-did-mount-set-state": 2, - "react/no-did-update-set-state": 2, - "react/no-multi-comp": 2, - "react/prop-types": [1, { "ignore": ["children", "className"] }], - "react/react-in-jsx-scope": 2, - "react/self-closing-comp": 1, - "react/wrap-multilines": 2, - "react/jsx-uses-vars": 1, - "strict": 0 + "no-eq-null": 0 } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1dfea92..8cbd1c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Contributing -As part of the react-bootstrap organization all contributing guidelines can be -found at: -https://github.com/react-bootstrap/react-bootstrap/blob/master/CONTRIBUTING.md +As part of the react-bootstrap organization all contributing guidelines can be found at: https://github.com/react-bootstrap/react-bootstrap/blob/master/CONTRIBUTING.md. -Note that automated changelog generation has not been setup on this repo yet. +Use `npm run visual-test` to check the appearance of components in your browser. The page will load at [http://localhost:8080/](http://localhost:8080/). + +Note that automated changelog generation has not been set up on this repo yet. diff --git a/README.md b/README.md index 933a84a..fd7633b 100644 --- a/README.md +++ b/README.md @@ -1,127 +1,33 @@ # react-router-bootstrap +Integration between [React Router](https://github.com/rackt/react-router) and [React-Bootstrap](https://github.com/react-bootstrap/react-bootstrap). [![Build Status](https://travis-ci.org/react-bootstrap/react-router-bootstrap.svg?branch=master)](https://travis-ci.org/react-bootstrap/react-router-bootstrap) +[![npm version](https://badge.fury.io/js/react-router-bootstrap.svg)](http://badge.fury.io/js/react-router-bootstrap) -Intregation between [react-router](https://github.com/rackt/react-router) and [react-bootstrap](https://github.com/react-bootstrap/react-bootstrap) - -This package gives you react-router compatible substitutes for: - -- `NavItem` -> `NavItemLink` -- `Button` -> `ButtonLink` -- `MenuItem` -> `MenuItemLink` -- `ListGroupItem` -> `ListGroupItemLink` -- `PageItem` -> `PageItemLink` -- `Thumbnail` -> `ThumbnailLink` - -Turning this: +## Usage -```jsx -React.createClass({ - mixins: [State, Navigation], +Wrap your React-Bootstrap element in a `LinkContainer` to make it behave like a React Router `Link`: - render: function() { - var href = this.makeHref('destination', {some: 'params'}, {some: 'query param'}); - var isActive = this.isActive('destination', {some: 'params'}, {some: 'query param'}); - return + ``` -Into this - -```jsx -React.createClass({ - render: function() { - return ; - } -}); -``` +To disable the element and the link, set the `disabled` prop on the `LinkContainer`. For the equivalent of `IndexLink`, use `IndexLinkContainer`. ## Installation ``` -npm install --save react-router-bootstrap +npm install react-router-bootstrap ``` -You will also (if you haven't already) want to install `react-router` and `react-bootstrap` +You will also want to have React Router and React-Bootstrap. ``` -npm install --save react-router react-bootstrap -``` - -## Usage - -A simple example - -```jsx -var Router = require('react-router') - , RouteHandler = Router.RouteHandler - , Route = Router.Route; - -var ReactBootstrap = require('react-bootstrap') - , Nav = ReactBootstrap.Nav - , ListGroup = ReactBootstrap.ListGroup; - -var ReactRouterBootstrap = require('react-router-bootstrap') - , NavItemLink = ReactRouterBootstrap.NavItemLink - , ButtonLink = ReactRouterBootstrap.ButtonLink - , ListGroupItemLink = ReactRouterBootstrap.ListGroupItemLink; - -var App = React.createClass({ - render: function() { - return ( -
- NavItemLink
- -
- ButtonLink
- - Linky! - -
- - - Linky! - - - -
- ); - } -}); - -var Destination = React.createClass({ - render: function() { - return
You made it!
; - } -}); - -var routes = ( - - - -); - -Router.run(routes, function (Handler) { - React.render(, document.body); -}); - +npm install react-router react-bootstrap ``` ## Contributing -See [CONTRIBUTING](CONTRIBUTING.md) - -Use `npm run visual-test` command to check components appearance in browser. It will open browser with a blank page. Then after `webpack-server` finishes its bundling, the browser automatically will refresh the page. - -URL for it: http://localhost:8080/public/visual#/ +See [CONTRIBUTING](CONTRIBUTING.md). diff --git a/assets/thumbnail.png b/assets/thumbnail.png deleted file mode 100644 index 6c05a89..0000000 Binary files a/assets/thumbnail.png and /dev/null differ diff --git a/karma.conf.js b/karma.conf.js index ade7535..a2d839a 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -4,26 +4,28 @@ delete webpackConfig.entry; module.exports = function (config) { config.set({ - basePath: '', - frameworks: [ 'mocha' ], + frameworks: [ + 'mocha', + 'sinon-chai' + ], files: [ './tests/index.js' ], - exclude: [], - preprocessors: { - './tests/index.js': [ 'webpack' ] + './tests/index.js': ['webpack', 'sourcemap'] }, - webpack: [ webpackConfig ], + webpack: webpackConfig, - webpackMiddleware: { }, + webpackMiddleware: { + noInfo: true + }, - reporters: [ 'mocha' ], + reporters: ['mocha'], port: 9876, @@ -33,7 +35,7 @@ module.exports = function (config) { autoWatch: true, - browsers: [ 'PhantomJS' ], + browsers: ['PhantomJS'], captureTimeout: 60000, browserDisconnectTimeout: 7000, diff --git a/package.json b/package.json index 2e9f964..343de98 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,16 @@ { "name": "react-router-bootstrap", "version": "0.18.1", - "description": "react-router and react-bootstrap compatible components", + "description": "Integration between React Router and React-Bootstrap", "main": "./lib/index.js", "scripts": { "prepublish": "npm run build", - "build": "babel src --out-dir=lib && webpack && COMPRESS=1 webpack && npm run bower-prepare", + "build": "babel src --out-dir=lib && webpack && webpack -p", "test": "npm run lint && karma start --single-run", "tdd": "karma start", - "visual-test": "open http://localhost:8080/public/visual#/ && webpack-dev-server --config webpack.test.config.babel.js", - "lint": "eslint ./", - "bower-prepare": "babel-node scripts/bower-prepare.js", - "patch": "release patch", - "minor": "release minor", - "major": "release major" + "visual-test": "open http://localhost:8080/ && webpack-dev-server --config webpack.visual.config.babel.js", + "lint": "eslint *.babel.js src tests", + "release": "release" }, "repository": { "type": "git", @@ -34,48 +31,53 @@ }, "homepage": "https://github.com/react-bootstrap/react-router-bootstrap", "peerDependencies": { - "react-bootstrap": ">=0.22.4", - "react-router": ">=0.13.1" + "react": ">=0.13.0 || >=0.14.0-rc1", + "react-router": ">=1.0.0-rc1" }, "devDependencies": { - "babel": "^5.5.6", - "babel-core": "^5.5.6", - "babel-eslint": "^4.0.5", - "babel-loader": "^5.1.4", - "bootstrap": "^3.3.1", - "chai": "^3.0.0", - "colors": "^1.1.2", - "css-loader": "^0.15.3", - "eslint": "^1.0.0", - "eslint-config-airbnb": "0.0.7", - "eslint-plugin-babel": "^1.0.0", - "eslint-plugin-mocha": "^0.4.0", - "eslint-plugin-react": "^3.1.0", - "karma": "^0.13.3", + "babel": "^5.8.23", + "babel-core": "^5.8.24", + "babel-eslint": "^4.1.2", + "babel-loader": "^5.3.2", + "bootstrap": "^3.3.5", + "css-loader": "^0.18.0", + "es5-shim": "^4.1.13", + "eslint": "1.3.x", + "eslint-config-airbnb": "0.0.8", + "eslint-plugin-babel": "^2.1.1", + "eslint-plugin-mocha": "^0.5.1", + "eslint-plugin-react": "^3.3.2", + "file-loader": "^0.8.4", + "history": "^1.9.1", + "html-webpack-plugin": "^1.6.1", + "karma": "^0.13.9", "karma-cli": "^0.1.0", "karma-mocha": "^0.2.0", - "karma-mocha-reporter": "^1.0.2", - "karma-phantomjs-launcher": "^0.2.0", - "karma-webpack": "^1.5.0", + "karma-mocha-reporter": "^1.1.1", + "karma-phantomjs-launcher": "^0.2.1", + "karma-sinon-chai": "^1.1.0", + "karma-sourcemap-loader": "^0.3.5", + "karma-webpack": "^1.7.0", "less": "^2.5.1", "less-loader": "^2.2.0", - "lodash": "^3.10.0", - "mocha": "^2.1.0", - "mt-changelog": "^0.6.1", + "mocha": "^2.3.2", + "mt-changelog": "^0.6.2", "node-libs-browser": "^0.5.2", - "phantomjs": "^1.9.13", - "react": ">0.10.0", - "react-bootstrap": ">=0.22.4", - "react-router": ">=0.13.1", - "release-script": "^0.2.1", - "shelljs": "^0.5.1", + "phantomjs": "^1.9.18", + "react": "^0.14.0-rc1", + "react-bootstrap": "^0.25.100-react-pre.1", + "react-dom": "^0.14.0-rc1", + "react-router": "^1.0.0-rc1", + "release-script": "^0.2.7", "style-loader": "^0.12.3", "url-loader": "^0.5.6", - "webpack": "^1.4.15", - "webpack-dev-server": "^1.7.0", - "yargs": "^3.15.0" + "webpack": "^1.12.1", + "webpack-dev-server": "^1.10.1", + "yargs": "^3.25.0" }, "files": [ + "README", + "CHANGELOG.md", "lib" ], "release-script": { diff --git a/scripts/bower-prepare.js b/scripts/bower-prepare.js deleted file mode 100644 index e98318e..0000000 --- a/scripts/bower-prepare.js +++ /dev/null @@ -1,70 +0,0 @@ -/* globals cat, config, cp, mkdir, rm, test */ -/* eslint curly: 0, no-console: 0 */ -import 'colors'; -import 'shelljs/global'; -import path from 'path'; -import _ from 'lodash'; -import yargs from 'yargs'; - -// do not die on errors -config.fatal = false; - -//------------------------------------------------------------------------------ -// constants -const repoRoot = path.resolve(__dirname, '../'); -const libFolder = path.join(repoRoot, 'lib'); -const bowerRoot = path.join(repoRoot, 'amd'); -const bowerTemplate = path.join(repoRoot, 'bower.template.json'); -const license = path.join(repoRoot, 'LICENSE'); - - -//------------------------------------------------------------------------------ -// command line options -const argv = yargs - .usage('Usage: $0 [--verbose]') - .example('$0', 'Prepare bower package for releasing') - .option('verbose', { - demand: false, - default: false, - describe: 'Increased debug output' - }) - .argv; - -if (argv.dryRun) console.log('DRY RUN'.magenta); - -config.silent = !argv.verbose; - - -//------------------------------------------------------------------------------ -// functions -function bower() { - console.log('Creating: '.cyan + 'bower package'.green); - - rm('-rf', bowerRoot); - mkdir('-p', bowerRoot); - - // generate bower.json from template - const pkg = JSON.parse(cat(path.join(repoRoot, 'package.json'))); - const template = _.template(cat(bowerTemplate)); - const bowerConfigObject = template({ pkg }); - const json = JSON.stringify(JSON.parse(bowerConfigObject), null, 2); // proper formatting hack - json.to(path.join(bowerRoot, 'bower.json')); - - // copy readme and license - const readmeBower = path.join(repoRoot, 'README.bower.md'); - const readme = path.join(repoRoot, 'README.md'); - if (test('-e', readmeBower)) { - cp(readmeBower, path.join(bowerRoot, 'README.md')); - } else { - cp(readme, bowerRoot); - } - if (test('-e', license)) cp(license, bowerRoot); - - // copy distr files - cp('-r', libFolder, bowerRoot); - - console.log('Created: '.cyan + 'bower package'.green); -} - -//------------------------------------------------------------------------------ -bower(); diff --git a/src/ButtonLink.js b/src/ButtonLink.js deleted file mode 100644 index c0dfe26..0000000 --- a/src/ButtonLink.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import Button from 'react-bootstrap/lib/Button'; -import LinkMixin from './LinkMixin'; - -const ButtonLink = React.createClass({ - mixins: [ - LinkMixin - ], - - render() { - return ( - - ); - } -}); - -export default ButtonLink; diff --git a/src/IndexLinkContainer.js b/src/IndexLinkContainer.js new file mode 100644 index 0000000..7b6b999 --- /dev/null +++ b/src/IndexLinkContainer.js @@ -0,0 +1,11 @@ +import React from 'react'; + +import LinkContainer from './LinkContainer'; + +export default class IndexLinkContainer extends React.Component { + render() { + return ( + + ); + } +} diff --git a/src/LinkContainer.js b/src/LinkContainer.js new file mode 100644 index 0000000..9d4df62 --- /dev/null +++ b/src/LinkContainer.js @@ -0,0 +1,53 @@ +// This is largely taken from react-router/lib/Link. + +import React from 'react'; +import {Link} from 'react-router'; + +export default class LinkContainer extends React.Component { + constructor(props, context) { + super(props, context); + + this.onClick = this.onClick.bind(this); + } + + onClick(event) { + if (this.props.disabled) { + event.preventDefault(); + return; + } + + Link.prototype.handleClick.call(this, event); + } + + render() { + const {history} = this.context; + const {onlyActiveOnIndex, to, query, children, ...props} = this.props; + + delete props.state; + delete props.onClick; + props.onClick = this.onClick; + props.href = history.createHref(to, query); + props.active = history.isActive(to, query, onlyActiveOnIndex); + + return React.cloneElement(React.Children.only(children), props); + } +} + +LinkContainer.propTypes = { + onlyActiveOnIndex: React.PropTypes.bool.isRequired, + to: React.PropTypes.string.isRequired, + query: React.PropTypes.object, + state: React.PropTypes.object, + onClick: React.PropTypes.func, + disabled: React.PropTypes.bool.isRequired, + children: React.PropTypes.node.isRequired +}; + +LinkContainer.contextTypes = { + history: React.PropTypes.object.isRequired +}; + +LinkContainer.defaultProps = { + onlyActiveOnIndex: false, + disabled: false +}; diff --git a/src/LinkMixin.js b/src/LinkMixin.js deleted file mode 100644 index e1cccbc..0000000 --- a/src/LinkMixin.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; - -function isLeftClickEvent(event) { - return event.button === 0; -} - -function isModifiedEvent(event) { - return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); -} - -export default { - propTypes: { - active: React.PropTypes.bool, - activeClassName: React.PropTypes.string.isRequired, - disabled: React.PropTypes.bool, - to: React.PropTypes.string.isRequired, - params: React.PropTypes.object, - query: React.PropTypes.object, - onClick: React.PropTypes.func - }, - contextTypes: { - router: React.PropTypes.func.isRequired - }, - - getDefaultProps() { - return { - activeClassName: 'active' - }; - }, - - /** - * Returns props except those used by this Mixin - * Gets "active" from router if needed. - * Gets the value of the "href" attribute to use on the DOM element. - * Sets "onClick" to "handleRouteTo". - */ - getLinkProps() { - const { - to, - params, - query, - ...props - } = this.props; - - if (this.props.active === undefined) { - props.active = this.context.router.isActive(to, params, query); - } - - props.href = this.context.router.makeHref(to, params, query); - - props.onClick = this.handleRouteTo; - - return props; - }, - - handleRouteTo(event) { - let allowTransition = true; - let clickResult; - - if (this.props.disabled) { - event.preventDefault(); - return; - } - - if (this.props.onClick) { - clickResult = this.props.onClick(event); - } - - if (isModifiedEvent(event) || !isLeftClickEvent(event)) { - return; - } - - if (clickResult === false || event.defaultPrevented === true) { - allowTransition = false; - } - - event.preventDefault(); - - if (allowTransition) { - this.context.router.transitionTo(this.props.to, this.props.params, this.props.query); - } - } -}; diff --git a/src/ListGroupItemLink.js b/src/ListGroupItemLink.js deleted file mode 100644 index 5de16ed..0000000 --- a/src/ListGroupItemLink.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import ListGroupItem from 'react-bootstrap/lib/ListGroupItem'; -import LinkMixin from './LinkMixin'; - -const LinkGroupItemLink = React.createClass({ - mixins: [ - LinkMixin - ], - - render() { - return ( - - {this.props.children} - - ); - } -}); - -export default LinkGroupItemLink; diff --git a/src/MenuItemLink.js b/src/MenuItemLink.js deleted file mode 100644 index 2cd4af5..0000000 --- a/src/MenuItemLink.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -import MenuItem from 'react-bootstrap/lib/MenuItem'; -import LinkMixin from './LinkMixin'; - -const MenuItemLink = React.createClass({ - mixins: [ - LinkMixin - ], - - render() { - const props = this.getLinkProps(); - delete props.onSelect; // this is done on the copy of this.props - - return ( - - {this.props.children} - - ); - } -}); - -export default MenuItemLink; diff --git a/src/NavItemLink.js b/src/NavItemLink.js deleted file mode 100644 index 27a32a9..0000000 --- a/src/NavItemLink.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import NavItem from 'react-bootstrap/lib/NavItem'; -import LinkMixin from './LinkMixin'; - -const NavItemLink = React.createClass({ - mixins: [ - LinkMixin - ], - - render() { - return ( - - {this.props.children} - - ); - } -}); - -export default NavItemLink; diff --git a/src/PageItemLink.js b/src/PageItemLink.js deleted file mode 100644 index 56b2ba9..0000000 --- a/src/PageItemLink.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import PageItem from 'react-bootstrap/lib/PageItem'; -import LinkMixin from './LinkMixin'; - -const PageItemLink = React.createClass({ - mixins: [ - LinkMixin - ], - - render() { - return ( - - {this.props.children} - - ); - } -}); - -export default PageItemLink; diff --git a/src/RouterOverlayTrigger.js b/src/RouterOverlayTrigger.js deleted file mode 100644 index 1ba7224..0000000 --- a/src/RouterOverlayTrigger.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; - -export default OverlayTrigger.withContext({ - router: React.PropTypes.func -}); diff --git a/src/ThumbnailLink.js b/src/ThumbnailLink.js deleted file mode 100644 index 979e8f6..0000000 --- a/src/ThumbnailLink.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import Thumbnail from 'react-bootstrap/lib/Thumbnail'; -import LinkMixin from './LinkMixin'; - -const ThumbnailLink = React.createClass({ - mixins: [ - LinkMixin - ], - - render() { - return ( - - {this.props.children} - - ); - } -}); - -export default ThumbnailLink; diff --git a/src/index.js b/src/index.js index 1d8d34f..5fd05e4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,2 @@ -export ButtonLink from './ButtonLink'; -export ListGroupItemLink from './ListGroupItemLink'; -export MenuItemLink from './MenuItemLink'; -export NavItemLink from './NavItemLink'; -export PageItemLink from './PageItemLink'; -export RouterOverlayTrigger from './RouterOverlayTrigger'; -export ThumbnailLink from './ThumbnailLink'; +export IndexLinkContainer from './IndexLinkContainer'; +export LinkContainer from './LinkContainer'; diff --git a/tests/.eslintrc b/tests/.eslintrc index b1618b1..c382f66 100644 --- a/tests/.eslintrc +++ b/tests/.eslintrc @@ -11,13 +11,7 @@ "mocha" ], "rules": { - "func-names": 0, - "no-alert": 0, - "no-script-url": 0, - "no-unused-expressions": 0, - "react/no-multi-comp": 0, - "react/prop-types": 0, - "react/sort-comp": 0, - "mocha/no-exclusive-tests": 2 + "mocha/no-exclusive-tests": 2, + "react/no-multi-comp": 0 } } diff --git a/tests/ButtonLink.spec.js b/tests/ButtonLink.spec.js deleted file mode 100644 index 729778b..0000000 --- a/tests/ButtonLink.spec.js +++ /dev/null @@ -1,166 +0,0 @@ -/* globals describe, it, assert, expect */ - -import React from 'react/addons'; -import ButtonLink from '../src/ButtonLink'; -import Router, { Route, RouteHandler } from 'react-router'; -import { Foo, Bar } from './TestHandlers'; -import TestLocation from 'react-router/lib/locations/TestLocation'; -const { click } = React.addons.TestUtils.Simulate; - -describe('A ButtonLink', function() { - describe('with params and a query', function() { - it('knows how to make its href', function() { - const ButtonLinkHandler = React.createClass({ - render() { - return ButtonLink; - } - }); - - const routes = [ - , - - ]; - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - const a = div.querySelector('a'); - expect(a.getAttribute('href')).to.equal('/foo/baz?qux=quux'); - }); - }); - }); - }); - - describe('when its route is active', function() { - it('has an active class name', function(done) { - const ButtonLinkHandler = React.createClass({ - render() { - return ( -
- ButtonLink - -
- ); - } - }); - - const routes = ( - - - - - ); - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/foo']; - const steps = []; - - function assertActive() { - const a = div.querySelector('a'); - expect(a.className.split(' ').sort().join(' ')).to.equal('active btn btn-primary btn-sm dontKillMe'); - } - - function assertInactive() { - const a = div.querySelector('a'); - expect(a.className.split(' ').sort().join(' ')).to.equal('btn btn-primary btn-sm dontKillMe'); - } - - steps.push(() => { - assertActive(); - testLocation.push('/bar'); - }); - - steps.push(() => { - assertInactive(); - testLocation.push('/foo'); - }); - - steps.push(() => { - assertActive(); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, () => { - steps.shift()(); - }); - }); - }); - }); - - describe('when clicked', function() { - it('calls a user defined click handler', function(done) { - const ButtonLinkHandler = React.createClass({ - handleClick(event) { - assert.ok(true); - done(); - }, - - render() { - return ButtonLink; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('transitions to the correct route', function(done) { - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - const ButtonLinkHandler = React.createClass({ - handleClick() { - // just here to make sure click handlers don't prevent it from happening - }, - - render() { - return ButtonLink; - } - }); - - const routes = [ - , - - ]; - - const steps = []; - - steps.push(function() { - click(div.querySelector('a'), {button: 0}); - }); - - steps.push(function() { - expect(div.innerHTML).to.match(/Foo/); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - steps.shift()(); - }); - }); - }); - }); -}); diff --git a/tests/IndexLinkContainer.spec.js b/tests/IndexLinkContainer.spec.js new file mode 100644 index 0000000..c1c6e84 --- /dev/null +++ b/tests/IndexLinkContainer.spec.js @@ -0,0 +1,56 @@ +import createMemoryHistory from 'history/lib/createMemoryHistory'; +import React from 'react'; +import ReactTestUtils from 'react/lib/ReactTestUtils'; +import * as ReactBootstrap from 'react-bootstrap'; +import ReactDOM from 'react-dom'; +import {IndexRoute, Route, Router} from 'react-router'; + +import IndexLinkContainer from '../src/IndexLinkContainer'; + +describe('IndexLinkContainer', () => { + [ + 'Button', + 'NavItem', + 'ListGroupItem' + ].forEach(name => { + describe(name, () => { + const Component = ReactBootstrap[name]; + + describe('active state', () => { + function renderComponent(location) { + class LinkWrapper extends React.Component { + render() { + return ( + + Root + + ); + } + } + + const router = ReactTestUtils.renderIntoDocument( + + + + + + + ); + + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + return ReactDOM.findDOMNode(component); + } + + it('should be active on the index route', () => { + expect(renderComponent('/').className).to.match(/\bactive\b/); + }); + + it('should not be active on a child route', () => { + expect(renderComponent('/foo').className).to.not.match(/\bactive\b/); + }); + }); + }); + }); +}); diff --git a/tests/LinkContainer.spec.js b/tests/LinkContainer.spec.js new file mode 100644 index 0000000..8db2355 --- /dev/null +++ b/tests/LinkContainer.spec.js @@ -0,0 +1,223 @@ +import createMemoryHistory from 'history/lib/createMemoryHistory'; +import React from 'react'; +import ReactTestUtils from 'react/lib/ReactTestUtils'; +import * as ReactBootstrap from 'react-bootstrap'; +import ReactDOM from 'react-dom'; +import {Route, Router} from 'react-router'; + +import LinkContainer from '../src/LinkContainer'; + +describe('LinkContainer', () => { + ['Button', + 'NavItem', + 'ListGroupItem' + ].forEach(name => { + describe(name, () => { + const Component = ReactBootstrap[name]; + + it('should make the correct href', () => { + class LinkWrapper extends React.Component { + render() { + return ( + + Foo + + ); + } + } + + const router = ReactTestUtils.renderIntoDocument( + + + + ); + + const anchor = ReactTestUtils.findRenderedDOMComponentWithTag( + router, 'A' + ); + expect(anchor.getAttribute('href')).to.equal('/foo?bar=baz'); + }); + + it('should not add extra DOM nodes', () => { + class LinkWrapper extends React.Component { + render() { + return ( + + Foo + + ); + } + } + + const router = ReactTestUtils.renderIntoDocument( + + + + ); + + const container = ReactTestUtils.findRenderedComponentWithType( + router, LinkContainer + ); + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + + expect(ReactDOM.findDOMNode(container)) + .to.equal(ReactDOM.findDOMNode(component)); + }); + + describe('when clicked', () => { + it('should transition to the correct route', () => { + class LinkWrapper extends React.Component { + render() { + return ( + + Target + + ); + } + } + + class Target extends React.Component { + render() { + return
; + } + } + + const router = ReactTestUtils.renderIntoDocument( + + + + + ); + + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(component), + {button: 0} + ); + + const target = ReactTestUtils.findRenderedDOMComponentWithClass( + router, 'target' + ); + expect(target).to.exist; + }); + + it('should call a user defined click handler', () => { + const onClick = sinon.spy(); + + class LinkWrapper extends React.Component { + render() { + return ( + + Foo + + ); + } + } + + const router = ReactTestUtils.renderIntoDocument( + + + + ); + + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(component)); + + expect(onClick).to.have.been.called; + }); + }); + + describe('active state', () => { + function renderComponent(location) { + class LinkWrapper extends React.Component { + render() { + return ( + + Foo + + ); + } + } + + const router = ReactTestUtils.renderIntoDocument( + + + + + + + ); + + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + return ReactDOM.findDOMNode(component); + } + + it('should be active when on the target route', () => { + expect(renderComponent('/foo').className).to.match(/\bactive\b/); + }); + + it('should not be active when on a different route', () => { + expect(renderComponent('/bar').className).to.not.match(/\bactive\b/); + }); + }); + + describe('disabled state', () => { + let router; + + beforeEach(() => { + class LinkWrapper extends React.Component { + render() { + return ( + + Target + + ); + } + } + + class Target extends React.Component { + render() { + return
; + } + } + + router = ReactTestUtils.renderIntoDocument( + + + + + ); + }); + + it('should not transition on click', () => { + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(component), + {button: 0} + ); + + const target = ReactTestUtils.scryRenderedDOMComponentsWithClass( + router, 'target' + ); + expect(target).to.be.empty; + }); + + it('should render with disabled class', () => { + const component = ReactTestUtils.findRenderedComponentWithType( + router, Component + ); + expect(ReactDOM.findDOMNode(component).className) + .to.match(/\bdisabled\b/); + }); + }); + }); + }); +}); diff --git a/tests/ListGroupItemLink.spec.js b/tests/ListGroupItemLink.spec.js deleted file mode 100644 index dfea564..0000000 --- a/tests/ListGroupItemLink.spec.js +++ /dev/null @@ -1,164 +0,0 @@ -/* globals describe, it, assert, expect */ - -import React from 'react/addons'; -import ListGroupItemLink from '../src/ListGroupItemLink'; -import Router, { Route, RouteHandler } from 'react-router'; -import { Foo, Bar } from './TestHandlers'; -import TestLocation from 'react-router/lib/locations/TestLocation'; -const { click } = React.addons.TestUtils.Simulate; - -describe('A ListGroupItemLink', function() { - describe('with params and a query', function() { - it('knows how to make its href', function() { - const ListGroupItemLinkHandler = React.createClass({ - render() { - return ListGroupItemLink; - } - }); - - const routes = [ - , - - ]; - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - const a = div.querySelector('a'); - expect(a.getAttribute('href')).to.equal('/foo/baz?qux=quux'); - }); - }); - }); - }); - - describe('when its route is active', function() { - it('has an active class name', function(done) { - const ListGroupItemLinkHandler = React.createClass({ - render() { - return ( -
- ListGroupItemLink - -
- ); - } - }); - - const routes = ( - - - - - ); - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/foo']; - const steps = []; - - function assertActive() { - const a = div.querySelector('a'); - expect(a.className.split(' ').sort().join(' ')).to.equal('active dontKillMe list-group-item'); - } - - function assertInactive() { - const a = div.querySelector('a'); - expect(a.className).to.equal('dontKillMe list-group-item'); - } - - steps.push(() => { - assertActive(); - testLocation.push('/bar'); - }); - - steps.push(() => { - assertInactive(); - testLocation.push('/foo'); - }); - - steps.push(() => { - assertActive(); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, () => { - steps.shift()(); - }); - }); - }); - }); - - describe('when clicked', function() { - it('calls a user defined click handler', function(done) { - const ListGroupItemLinkHandler = React.createClass({ - handleClick(event) { - assert.ok(true); - done(); - }, - - render() { - return ListGroupItemLink; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('transitions to the correct route', function(done) { - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - const ListGroupItemLinkHandler = React.createClass({ - handleClick() { - // just here to make sure click handlers don't prevent it from happening - }, - - render() { - return ListGroupItemLink; - } - }); - - const routes = [ - , - - ]; - - const steps = []; - - steps.push(function() { - click(div.querySelector('a'), {button: 0}); - }); - - steps.push(function() { - expect(div.innerHTML).to.match(/Foo/); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - steps.shift()(); - }); - }); - }); - }); -}); diff --git a/tests/MenuItemLink.spec.js b/tests/MenuItemLink.spec.js deleted file mode 100644 index ebae3a2..0000000 --- a/tests/MenuItemLink.spec.js +++ /dev/null @@ -1,168 +0,0 @@ -/* globals describe, it, assert, expect */ - -import React from 'react/addons'; -import MenuItemLink from '../src/MenuItemLink'; -import Router, { Route, RouteHandler } from 'react-router'; -import { Foo, Bar } from './TestHandlers'; -import TestLocation from 'react-router/lib/locations/TestLocation'; -const { click } = React.addons.TestUtils.Simulate; - -describe('A MenuItemLink', function() { - describe('with params and a query', function() { - it('knows how to make its href', function() { - const MenuItemLinkHandler = React.createClass({ - render() { - return MenuItemLink; - } - }); - - const routes = [ - , - - ]; - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - const a = div.querySelector('a'); - expect(a.getAttribute('href')).to.equal('/foo/baz?qux=quux'); - }); - }); - }); - }); - - describe('when its route is active', function() { - it('has an active class name', function(done) { - const MenuItemLinkHandler = React.createClass({ - render() { - return ( -
- MenuItemLink - -
- ); - } - }); - - const routes = ( - - - - - ); - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/foo']; - const steps = []; - - function assertActive() { - const li = div.querySelector('li'); - expect(li.className.split(' ').sort().join(' ')).to.equal('active dontKillMe'); - const a = div.querySelector('a'); - expect(a.className.split(' ').sort().join(' ')).to.equal(''); - } - - function assertInactive() { - const li = div.querySelector('li'); - expect(li.className.split(' ').sort().join(' ')).to.equal('dontKillMe'); - const a = div.querySelector('a'); - expect(a.className.split(' ').sort().join(' ')).to.equal(''); - } - - steps.push(() => { - assertActive(); - testLocation.push('/bar'); - }); - - steps.push(() => { - assertInactive(); - testLocation.push('/foo'); - }); - - steps.push(() => { - assertActive(); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, () => { - steps.shift()(); - }); - }); - }); - }); - - describe('when clicked', function() { - it('calls a user defined click handler', function(done) { - const MenuItemLinkHandler = React.createClass({ - handleClick(event) { - assert.ok(true); - done(); - }, - - render() { - return MenuItemLink; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('transitions to the correct route', function(done) { - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - const MenuItemLinkHandler = React.createClass({ - handleClick() { - // just here to make sure click handlers don't prevent it from happening - }, - - render() { - return MenuItemLink; - } - }); - - const routes = [ - , - - ]; - - const steps = []; - - steps.push(function() { - click(div.querySelector('a'), {button: 0}); - }); - - steps.push(function() { - expect(div.innerHTML).to.match(/Foo/); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - steps.shift()(); - }); - }); - }); - }); -}); diff --git a/tests/NavItemLink.spec.js b/tests/NavItemLink.spec.js deleted file mode 100644 index b72f754..0000000 --- a/tests/NavItemLink.spec.js +++ /dev/null @@ -1,190 +0,0 @@ -/* globals describe, it, assert, expect */ - -import React from 'react/addons'; -import NavItemLink from '../src/NavItemLink'; -import Router, { Route, RouteHandler } from 'react-router'; -import { Foo, Bar } from './TestHandlers'; -import TestLocation from 'react-router/lib/locations/TestLocation'; -const { click } = React.addons.TestUtils.Simulate; - -describe('A NavItemLink', function() { - describe('with params and a query', function() { - it('knows how to make its href', function() { - const NavItemLinkHandler = React.createClass({ - render() { - return NavItemLink; - } - }); - - const routes = [ - , - - ]; - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - const a = div.querySelector('a'); - expect(a.getAttribute('href')).to.equal('/foo/baz?qux=quux'); - }); - }); - }); - }); - - describe('when its route is active', function() { - it('has an active class name', function(done) { - const NavItemLinkHandler = React.createClass({ - render() { - return ( -
- NavItemLink - -
- ); - } - }); - - const routes = ( - - - - - ); - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/foo']; - const steps = []; - - function assertActive() { - const a = div.querySelector('li'); - expect(a.className.split(' ').sort().join(' ')).to.equal('active dontKillMe'); - } - - function assertInactive() { - const a = div.querySelector('li'); - expect(a.className).to.equal('dontKillMe'); - } - - steps.push(() => { - assertActive(); - testLocation.push('/bar'); - }); - - steps.push(() => { - assertInactive(); - testLocation.push('/foo'); - }); - - steps.push(() => { - assertActive(); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, () => { - steps.shift()(); - }); - }); - }); - }); - - describe('when clicked', function() { - it('calls a user defined click handler', function(done) { - const NavItemLinkHandler = React.createClass({ - handleClick(event) { - assert.ok(true); - done(); - }, - - render() { - return NavItemLink; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('when disabled does not proceed', function() { - const NavItemLinkHandler = React.createClass({ - handleClick(event) { - throw new Error('click should not be called'); - }, - - render() { - return NavItemLink; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('transitions to the correct route', function(done) { - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - const NavItemLinkHandler = React.createClass({ - handleClick() { - // just here to make sure click handlers don't prevent it from happening - }, - - render() { - return NavItemLink; - } - }); - - const routes = [ - , - - ]; - - const steps = []; - - steps.push(function() { - click(div.querySelector('a'), {button: 0}); - }); - - steps.push(function() { - expect(div.innerHTML).to.match(/Foo/); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - steps.shift()(); - }); - }); - }); - }); -}); diff --git a/tests/PageItemLink.spec.js b/tests/PageItemLink.spec.js deleted file mode 100644 index 534853c..0000000 --- a/tests/PageItemLink.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -/* globals describe, it, assert, expect */ - -import React from 'react/addons'; -import PageItemLink from '../src/PageItemLink'; -import Router, { Route } from 'react-router'; -import { Foo } from './TestHandlers'; -import TestLocation from 'react-router/lib/locations/TestLocation'; -const { click } = React.addons.TestUtils.Simulate; - -describe('A PageItemLink', function() { - describe('with params and a query', function() { - it('knows how to make its href', function() { - const PageItemLinkHandler = React.createClass({ - render() { - return PageItemLink; - } - }); - - const routes = [ - , - - ]; - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - const a = div.querySelector('a'); - expect(a.getAttribute('href')).to.equal('/foo/baz?qux=quux'); - }); - }); - }); - }); - - describe('when clicked', function() { - it('calls a user defined click handler', function(done) { - const PageItemLinkHandler = React.createClass({ - handleClick(event) { - assert.ok(true); - done(); - }, - - render() { - return PageItemLink; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('transitions to the correct route', function(done) { - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - const PageItemLinkHandler = React.createClass({ - handleClick() { - // just here to make sure click handlers don't prevent it from happening - }, - - render() { - return PageItemLink; - } - }); - - const routes = [ - , - - ]; - - const steps = []; - - steps.push(function() { - click(div.querySelector('a'), {button: 0}); - }); - - steps.push(function() { - expect(div.innerHTML).to.match(/Foo/); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - steps.shift()(); - }); - }); - }); - }); -}); diff --git a/tests/RouterOverlayTrigger.spec.js b/tests/RouterOverlayTrigger.spec.js deleted file mode 100644 index 8cbf51b..0000000 --- a/tests/RouterOverlayTrigger.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -/* globals describe, it, expect */ - -import React from 'react'; - -import RouterOverlayTrigger from '../src/RouterOverlayTrigger'; - -describe('A RouterOverlayTrigger', function() { - it('has the right contextTypes', function() { - expect(RouterOverlayTrigger.contextTypes).to.eql({ - router: React.PropTypes.func - }); - }); -}); diff --git a/tests/TestHandlers.js b/tests/TestHandlers.js deleted file mode 100644 index f01e033..0000000 --- a/tests/TestHandlers.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { RouteHandler } from 'react-router'; - -exports.Nested = React.createClass({ - render() { - return ( -
-

Nested

- -
- ); - } -}); - -exports.Foo = React.createClass({ - render() { - return
Foo
; - } -}); - -exports.Bar = React.createClass({ - render() { - return
Bar
; - } -}); - -exports.Baz = React.createClass({ - render() { - return
Baz
; - } -}); diff --git a/tests/ThumbnailLink.spec.js b/tests/ThumbnailLink.spec.js deleted file mode 100644 index a66a5d6..0000000 --- a/tests/ThumbnailLink.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -/* globals describe, it, assert, expect */ - -import React from 'react/addons'; -import ThumbnailLink from '../src/ThumbnailLink'; -import Router, { Route } from 'react-router'; -import { Foo } from './TestHandlers'; -import TestLocation from 'react-router/lib/locations/TestLocation'; -const { click } = React.addons.TestUtils.Simulate; - -describe('A ThumbnailLink', function() { - describe('with params and a query', function() { - it('knows how to make its href', function() { - const ThumbnailLinkHandler = React.createClass({ - render() { - return ; - } - }); - - const routes = [ - , - - ]; - - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - const a = div.querySelector('a'); - expect(a.getAttribute('href')).to.equal('/foo/baz?qux=quux'); - }); - }); - }); - }); - - describe('when clicked', function() { - it('calls a user defined click handler', function(done) { - const ThumbnailLinkHandler = React.createClass({ - handleClick(event) { - assert.ok(true); - done(); - }, - - render() { - return ; - } - }); - - const routes = [ - , - - ]; - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - click(div.querySelector('a')); - }); - }); - }); - - it('transitions to the correct route', function(done) { - const div = document.createElement('div'); - const testLocation = new TestLocation(); - testLocation.history = ['/link']; - - const ThumbnailLinkHandler = React.createClass({ - handleClick() { - // just here to make sure click handlers don't prevent it from happening - }, - - render() { - return ; - } - }); - - const routes = [ - , - - ]; - - const steps = []; - - steps.push(function() { - click(div.querySelector('a'), {button: 0}); - }); - - steps.push(function() { - expect(div.innerHTML).to.match(/Foo/); - done(); - }); - - Router.run(routes, testLocation, function(Handler) { - React.render(, div, function() { - steps.shift()(); - }); - }); - }); - }); -}); diff --git a/tests/bower-imports-module.spec.js b/tests/bower-imports-module.spec.js deleted file mode 100644 index d61a9bd..0000000 --- a/tests/bower-imports-module.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -import loader from '../webpack/bower-imports-loader'; - -describe('bower-imports-loader', function() { - it('replaces react-bootstrap/lib/* imports to work with bower dependency', function() { - const input = `import React from 'react'; - -import Button from 'react-bootstrap/lib/Button'; -import LinkMixin from './LinkMixin'; - -const ButtonLink = React.createClass({`; - - const expected = `import React from 'react'; - -import {Button} from 'react-bootstrap'; -import LinkMixin from './LinkMixin'; - -const ButtonLink = React.createClass({`; - - loader(input).should.equal(expected); - }); -}); diff --git a/tests/index.js b/tests/index.js index 1c5bc19..3e704d9 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,19 +1,4 @@ -import './phantom-shims'; -import 'mocha'; +import 'es5-shim'; -const chai = require('chai'); -chai.should(); - -global.expect = chai.expect; -global.assert = chai.assert; - -global.TestUtils = require('react/addons').addons.TestUtils; - -import './ButtonLink.spec.js'; -import './ListGroupItemLink.spec.js'; -import './MenuItemLink.spec.js'; -import './NavItemLink.spec.js'; -import './PageItemLink.spec.js'; -import './RouterOverlayTrigger.spec.js'; -import './ThumbnailLink.spec.js'; -import './bower-imports-module.spec.js'; +const testsContext = require.context('.', true, /\.spec\.js$/); +testsContext.keys().forEach(testsContext); diff --git a/tests/phantom-shims.js b/tests/phantom-shims.js deleted file mode 100644 index 3d0e2aa..0000000 --- a/tests/phantom-shims.js +++ /dev/null @@ -1,31 +0,0 @@ -const Ap = Array.prototype; -const slice = Ap.slice; -const Fp = Function.prototype; - -if (!Fp.bind) { - // PhantomJS doesn't support Function.prototype.bind natively, so - // polyfill it whenever this module is required. - Fp.bind = function(context) { - const func = this; - const args = slice.call(arguments, 1); - - function bound() { - const invokedAsConstructor = func.prototype && (this instanceof func); - return func.apply( - // Ignore the context parameter when invoking the bound function - // as a constructor. Note that this includes not only constructor - // invocations using the new keyword but also calls to base class - // constructors such as BaseClass.call(this, ...) or super(...). - !invokedAsConstructor && context || this, - args.concat(slice.call(arguments)) - ); - } - - // The bound function must share the .prototype of the unbound - // function so that any object created by one constructor will count - // as an instance of both constructors. - bound.prototype = func.prototype; - - return bound; - }; -} diff --git a/tests/visual.js b/tests/visual.js deleted file mode 100644 index 12c0354..0000000 --- a/tests/visual.js +++ /dev/null @@ -1,2 +0,0 @@ -import 'bootstrap/less/bootstrap.less'; -import './visual/app'; diff --git a/tests/visual/ButtonVisual.js b/tests/visual/ButtonVisual.js index a67dde6..715ed92 100644 --- a/tests/visual/ButtonVisual.js +++ b/tests/visual/ButtonVisual.js @@ -1,36 +1,45 @@ import React from 'react'; -import {Link} from 'react-router'; import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar'; import Button from 'react-bootstrap/lib/Button'; -import ButtonLink from '../../src/ButtonLink'; +import {Link} from 'react-router'; + +import LinkContainer from '../../src/LinkContainer'; + +export default () => ( +
+ Back to Index +

Button

-const ButtonVisual = React.createClass({ - render() { - return ( -
- <-- Back to Index -

ButtonLink

-

Baseline (Raw React-Bootstrap)

- - - - - - - - -

ButtonLink

- - Default - Success - Info - Warning - Danger - Link - -
- ); - } -}); +

Baseline

+ + + + + + + + -export default ButtonVisual; +

LinkContainer

+ + + + + + + + + + + + + + + + + + + + +
+); diff --git a/tests/visual/ListGroupItemVisual.js b/tests/visual/ListGroupItemVisual.js index 9701304..8ebc95c 100644 --- a/tests/visual/ListGroupItemVisual.js +++ b/tests/visual/ListGroupItemVisual.js @@ -1,41 +1,77 @@ import React from 'react'; -import {Link} from 'react-router'; import ListGroup from 'react-bootstrap/lib/ListGroup'; import ListGroupItem from 'react-bootstrap/lib/ListGroupItem'; -import ListGroupItemLink from '../../src/ListGroupItemLink'; +import {Link} from 'react-router'; + +import LinkContainer from '../../src/LinkContainer'; + +export default () => ( +
+ Back to Index +

ListGroupItem

-const NavItemVisual = React.createClass({ - handleSelect(selectedKey) { - window.alert('selected ' + selectedKey); - }, - render() { - return ( -
- <-- Back to Index -

ListGroupItemLink

-

Baseline (Raw React-Bootstrap)

- - ListGroupItem 1 content - ListGroupItem 2 content - ListGroupItem 3 content disabled - ListGroupItem 4 content success - ListGroupItem 5 content info - ListGroupItem 6 content warning - ListGroupItem 7 content danger - -

ListGroupItemLink

- - ListGroupItemLink 1 content - ListGroupItemLink 2 content - ListGroupItemLink 3 content disabled - ListGroupItemLink 4 content success - ListGroupItemLink 5 content info - ListGroupItemLink 6 content warning - ListGroupItemLink 7 content danger - -
- ); - } -}); +

Baseline

+ + + ListGroupItem 1 content + + + ListGroupItem 2 content + + + ListGroupItem 3 content disabled + + + ListGroupItem 4 content success + + + ListGroupItem 5 content info + + + ListGroupItem 6 content warning + + + ListGroupItem 7 content danger + + -export default NavItemVisual; +

LinkContainer

+ + + + ListGroupItem 1 content + + + + + ListGroupItem 2 content + + + + + ListGroupItem 3 content disabled + + + + + ListGroupItem 4 content success + + + + + ListGroupItem 5 content info + + + + + ListGroupItem 6 content warning + + + + + ListGroupItem 7 content danger + + + +
+); diff --git a/tests/visual/MenuItemVisual.js b/tests/visual/MenuItemVisual.js deleted file mode 100644 index 517d460..0000000 --- a/tests/visual/MenuItemVisual.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import {Link} from 'react-router'; -import ButtonToolbar from 'react-bootstrap/lib/ButtonToolbar'; -import SplitButton from 'react-bootstrap/lib/SplitButton'; -import MenuItem from 'react-bootstrap/lib/MenuItem'; -import MenuItemLink from '../../src/MenuItemLink'; - -const MenuItemVisual = React.createClass({ - handleSelect(selectedKey) { - window.alert('selected ' + selectedKey); - }, - render() { - return ( -
- <-- Back to Index -

MenuItemLink

-

Baseline (Raw React-Bootstrap)

- - - Action - Another action - Something else here - - Separated link - - -

MenuItemLink

- - - Action - Another action - Something else here - - Separated link - - -
- ); - } -}); - -export default MenuItemVisual; diff --git a/tests/visual/NavItemVisual.js b/tests/visual/NavItemVisual.js index 01c7ce8..aabf278 100644 --- a/tests/visual/NavItemVisual.js +++ b/tests/visual/NavItemVisual.js @@ -1,33 +1,33 @@ import React from 'react'; -import {Link} from 'react-router'; import Nav from 'react-bootstrap/lib/Nav'; import NavItem from 'react-bootstrap/lib/NavItem'; -import NavItemLink from '../../src/NavItemLink'; +import {Link} from 'react-router'; + +import LinkContainer from '../../src/LinkContainer'; + +export default () => ( +
+ Back to Index +

NavItem

-const NavItemVisual = React.createClass({ - handleSelect(selectedKey) { - window.alert('selected ' + selectedKey); - }, - render() { - return ( -
- <-- Back to Index -

NavItemLink

-

Baseline (Raw React-Bootstrap)

- -

NavItemLink

- -
- ); - } -}); +

Baseline

+ -export default NavItemVisual; +

LinkContainer

+ +
+); diff --git a/tests/visual/PageItemVisual.js b/tests/visual/PageItemVisual.js deleted file mode 100644 index 5fa116f..0000000 --- a/tests/visual/PageItemVisual.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import {Link} from 'react-router'; -import Pager from 'react-bootstrap/lib/Pager'; -import PageItem from 'react-bootstrap/lib/PageItem'; -import PageItemLink from '../../src/PageItemLink'; - -const PageItemVisual = React.createClass({ - render() { - return ( -
- <-- Back to Index -

PageItemLink

-

Baseline (Raw React-Bootstrap)

- - ← Previous Page - Next Page → - -

PageItemLink

- - ← Previous Page - Next Page → - -
- ); - } -}); - -export default PageItemVisual; diff --git a/tests/visual/ThumbnailVisual.js b/tests/visual/ThumbnailVisual.js deleted file mode 100644 index 16c75d6..0000000 --- a/tests/visual/ThumbnailVisual.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import {Link} from 'react-router'; -import Thumbnail from 'react-bootstrap/lib/Thumbnail'; -import ThumbnailLink from '../../src/ThumbnailLink'; - -const ThumbnailVisual = React.createClass({ - render() { - return ( -
- <-- Back to Index -

Thumbnailink

-

Baseline (Raw React-Bootstrap)

- -

ThumbnailLink

- -
- ); - } -}); - -export default ThumbnailVisual; diff --git a/tests/visual/app.js b/tests/visual/app.js deleted file mode 100644 index f3035a2..0000000 --- a/tests/visual/app.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import Router, { Route, RouteHandler } from 'react-router'; - -const App = React.createClass({ - render() { - return ( -
-

React-Router-Bootstrap Module Visual Test

- -
- ); - } -}); - -const routes = ( - - - - - - - - - -); - -Router.run(routes, function(Handler) { - React.render(, document.body); -}); diff --git a/tests/visual/home.js b/tests/visual/home.js index a83c80b..834776e 100644 --- a/tests/visual/home.js +++ b/tests/visual/home.js @@ -1,22 +1,13 @@ import React from 'react'; import {Link} from 'react-router'; -const Home = React.createClass({ - render() { - return ( -
-

Index

-
    -
  • ButtonLink
  • -
  • NavItemLink
  • -
  • MenuItemLink
  • -
  • ListGroupItemLink
  • -
  • PageItemLink
  • -
  • ThumbnailLink
  • -
-
- ); - } -}); - -export default Home; +export default () => ( +
+

Index

+
    +
  • Button
  • +
  • NavItem
  • +
  • ListGroupItem
  • +
+
+); diff --git a/tests/visual/index.js b/tests/visual/index.js new file mode 100644 index 0000000..72a67a1 --- /dev/null +++ b/tests/visual/index.js @@ -0,0 +1,35 @@ +import React from 'react'; +import Grid from 'react-bootstrap/lib/Grid'; +import ReactDOM from 'react-dom'; +import {IndexRoute, Route, Router} from 'react-router'; + +import ButtonVisual from './ButtonVisual'; +import Home from './Home'; +import ListGroupItemVisual from './ListGroupItemVisual'; +import NavItemVisual from './NavItemVisual'; + +import 'bootstrap/less/bootstrap.less'; + +const App = ({children}) => ( + +

React-Router-Bootstrap Module Visual Test

+ {children} +
+); + +const mountNode = document.createElement('div'); +document.body.appendChild(mountNode); + +ReactDOM.render( + + + replaceWith(null, '/home')} /> + + + + + + + , + mountNode +); diff --git a/webpack.config.babel.js b/webpack.config.babel.js index 22cc107..a1fd0d6 100644 --- a/webpack.config.babel.js +++ b/webpack.config.babel.js @@ -1,44 +1,24 @@ -import path from 'path'; import webpack from 'webpack'; +import yargs from 'yargs'; -const plugins = []; - -if (process.env.COMPRESS) { - plugins.push( - new webpack.optimize.UglifyJsPlugin({ - compressor: { - warnings: false - } - }) - ); -} +const {optimizeMinimize} = yargs.alias('p', 'optimize-minimize').argv; +const nodeEnv = optimizeMinimize ? 'production' : 'development'; export default { entry: { 'ReactRouterBootstrap': './src/index.js' }, - output: { path: './lib', - filename: process.env.COMPRESS ? '[name].min.js' : '[name].js', + filename: optimizeMinimize ? '[name].min.js' : '[name].js', library: 'ReactRouterBootstrap', libraryTarget: 'umd' }, - module: { loaders: [ - { - test: /\.js/, - loaders: [ - 'babel', - path.join(__dirname, 'webpack/bower-imports-loader.js') - ], - exclude: /node_modules/ } + {test: /\.js$/, loader: 'babel', exclude: /node_modules/} ] }, - - devtool: process.env.COMPRESS && 'source-map', - externals: [ { 'react': { @@ -55,20 +35,12 @@ export default { commonjs: 'react-router', amd: 'react-router' } - }, - { - 'react-bootstrap': { - root: 'ReactBootstrap', - commonjs2: 'react-bootstrap', - commonjs: 'react-bootstrap', - amd: 'react-bootstrap' - } } ], - - node: { - buffer: false - }, - - plugins + plugins: [ + new webpack.DefinePlugin({ + 'process.env': {'NODE_ENV': JSON.stringify(nodeEnv)} + }) + ], + devtool: optimizeMinimize ? 'source-map' : null }; diff --git a/webpack.test.config.babel.js b/webpack.test.config.babel.js index 3c1a7a8..2e18c18 100644 --- a/webpack.test.config.babel.js +++ b/webpack.test.config.babel.js @@ -1,24 +1,11 @@ -import path from 'path'; - -module.exports = { - entry: { - visual: ['./tests/visual.js'] - }, - - devtool: 'inline-source-map', - +export default { output: { - path: path.join(__dirname, 'tests-served/'), - publicPath: '/public/', - filename: '[name].js' + pathinfo: true }, - module: { loaders: [ - {test: /\.js/, loader: 'babel', exclude: /node_modules/}, - {test: /\.less$/, loader: 'style!css!less'}, - {test:/\.woff|\.woff2$/, loader: 'url?prefix=font/&limit=5000'}, - {test:/\.eot$|\.ttf$|\.svg$/, loader: 'file?prefix=font/'} + {test: /\.js/, loader: 'babel', exclude: /node_modules/} ] - } + }, + devtool: 'inline-source-map' }; diff --git a/webpack.visual.config.babel.js b/webpack.visual.config.babel.js new file mode 100644 index 0000000..4da1a57 --- /dev/null +++ b/webpack.visual.config.babel.js @@ -0,0 +1,17 @@ +import HtmlWebpackPlugin from 'html-webpack-plugin'; + +export default { + entry: './tests/visual', + module: { + loaders: [ + {test: /\.js/, loader: 'babel', exclude: /node_modules/}, + {test: /\.less$/, loader: 'style!css!less'}, + {test: /\.woff|\.woff2$/, loader: 'url?prefix=font/&limit=5000'}, + {test: /\.eot$|\.ttf$|\.svg$/, loader: 'file?prefix=font/'} + ] + }, + plugins: [ + new HtmlWebpackPlugin() + ], + devtool: 'eval-source-map' +}; diff --git a/webpack/bower-imports-loader.js b/webpack/bower-imports-loader.js deleted file mode 100644 index 32e4d90..0000000 --- a/webpack/bower-imports-loader.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function bowerImportsLoader(source) { - const rgx = /^import (.*) from 'react-bootstrap\/lib\/.*';/; - - return source.split('\n') - .map(line => line.replace(rgx, "import {$1} from 'react-bootstrap';")) - .join('\n'); -}