diff --git a/README.md b/README.md
index 7784775..7ddbe72 100644
--- a/README.md
+++ b/README.md
@@ -82,27 +82,27 @@ ReactDOM.render(, content);
### Prop Values
#### data
-`React.PropTypes.oneOfType([React.PropTypes.object,React.PropTypes.array]).isRequired`
+`PropTypes.oneOfType([PropTypes.object,PropTypes.array]).isRequired`
Data that drives the tree view. State-driven effects can be built by manipulating the attributes in this object. Also supports an array for multiple nodes at the root level. An example can be found in `example/data.js`
#### onToggle
-`React.PropTypes.func`
+`PropTypes.func`
Callback function when a node is toggled / clicked. Passes 2 attributes: the data node and it's toggled boolean state.
#### style
-`React.PropTypes.object`
+`PropTypes.object`
Sets the treeview styling. Defaults to `src/themes/default`.
#### animations
-`React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.bool])`
+`PropTypes.oneOfType([PropTypes.object, PropTypes.bool])`
Sets the treeview animations. Set to `false` if you want to turn off animations. See [velocity-react](https://github.com/twitter-fabric/velocity-react) for more details. Defaults to `src/themes/animations`.
#### decorators
-`React.PropTypes.object`
+`PropTypes.object`
Decorates the treeview. Here you can use your own Container, Header, Toggle and Loading components. Defaults to `src/decorators`. See example below:
diff --git a/example/app.js b/example/app.js
index baa9f13..9787955 100644
--- a/example/app.js
+++ b/example/app.js
@@ -1,8 +1,9 @@
'use strict';
import React from 'react';
+import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
-import { StyleRoot } from 'radium';
+import {StyleRoot} from 'radium';
import {Treebeard, decorators} from '../src/index';
import data from './data';
@@ -12,87 +13,96 @@ import * as filters from './filter';
const HELP_MSG = 'Select A Node To See Its Data Structure Here...';
// Example: Customising The Header Decorator To Include Icons
-decorators.Header = (props) => {
- const style = props.style;
- const iconType = props.node.children ? 'folder' : 'file-text';
+decorators.Header = ({style, node}) => {
+ const iconType = node.children ? 'folder' : 'file-text';
const iconClass = `fa fa-${iconType}`;
- const iconStyle = { marginRight: '5px' };
+ const iconStyle = {marginRight: '5px'};
+
return (
- {props.node.name}
+
+ {node.name}
);
};
class NodeViewer extends React.Component {
- constructor(props){
- super(props);
- }
- render(){
+ render() {
const style = styles.viewer;
let json = JSON.stringify(this.props.node, null, 4);
- if(!json){ json = HELP_MSG; }
- return (
-
- {json}
-
- );
+
+ if (!json) {
+ json = HELP_MSG;
+ }
+
+ return {json}
;
}
}
-
NodeViewer.propTypes = {
- node: React.PropTypes.object
+ node: PropTypes.object
};
class DemoTree extends React.Component {
- constructor(props){
- super(props);
+ constructor() {
+ super();
+
this.state = {data};
this.onToggle = this.onToggle.bind(this);
}
- onToggle(node, toggled){
- if(this.state.cursor){this.state.cursor.active = false;}
+
+ onToggle(node, toggled) {
+ const {cursor} = this.state;
+
+ if (cursor) {
+ cursor.active = false;
+ }
+
node.active = true;
- if(node.children){ node.toggled = toggled; }
- this.setState({ cursor: node });
+ if (node.children) {
+ node.toggled = toggled;
+ }
+
+ this.setState({cursor: node});
}
- onFilterMouseUp(e){
+
+ onFilterMouseUp(e) {
const filter = e.target.value.trim();
- if(!filter){ return this.setState({data}); }
+ if (!filter) {
+ return this.setState({data});
+ }
var filtered = filters.filterTree(data, filter);
filtered = filters.expandFilteredNodes(filtered, filter);
this.setState({data: filtered});
}
- render(){
+
+ render() {
+ const {data: stateData, cursor} = this.state;
+
return (
-
+
-
+
-
);
}
}
diff --git a/package.json b/package.json
index d06c0f6..08d6caa 100644
--- a/package.json
+++ b/package.json
@@ -7,13 +7,13 @@
"prepublish": "npm run lib",
"lib": "npm run babel",
"babel": "rimraf lib && babel src/ -d lib/",
- "test": "./node_modules/.bin/karma start karma.conf.js",
- "test-travis": "./node_modules/karma/bin/karma start --browsers Firefox --single-run",
+ "test": "karma start karma.conf.js",
+ "test-travis": "karma start --browsers Firefox --single-run",
"example": "webpack-dev-server --content-base ./example/ --config ./example/webpack.config.js"
},
"peerDependencies": {
- "react": "^0.14 || ^15.0",
- "react-dom": "^0.14 || ^15.0"
+ "react": "^15.5.4",
+ "react-dom": "^15.5.4"
},
"repository": {
"type": "git",
@@ -60,9 +60,8 @@
"karma-webpack": "^1.7.0",
"mocha": "^2.3.3",
"node-libs-browser": "^0.5.3",
- "react": "^0.14.7",
- "react-addons-test-utils": "^0.14.7",
- "react-dom": "^0.14.7",
+ "react": "^15.5.4",
+ "react-dom": "^15.5.4",
"react-hot-loader": "^1.3.0",
"rimraf": "^2.4.4",
"sinon": "uberVU/Sinon.JS.git",
@@ -73,8 +72,9 @@
"dependencies": {
"babel-runtime": "^5.8.29",
"deep-equal": "^1.0.1",
- "radium": "^0.18.0",
+ "prop-types": "^15.5.8",
+ "radium": "^0.19.0",
"shallowequal": "^0.2.2",
- "velocity-react": "^1.1.2"
+ "velocity-react": "^1.3.1"
}
}
diff --git a/src/components/decorators.js b/src/components/decorators.js
index 0ebe13f..ed4a250 100644
--- a/src/components/decorators.js
+++ b/src/components/decorators.js
@@ -1,108 +1,100 @@
'use strict';
import React from 'react';
+import PropTypes from 'prop-types';
import Radium from 'radium';
import {VelocityComponent} from 'velocity-react';
-const Loading = (props) => {
- return (
-
- loading...
-
- );
+const Loading = ({style}) => {
+ return loading...
;
};
-
Loading.propTypes = {
- style: React.PropTypes.object
+ style: PropTypes.object
};
-const Toggle = (props) => {
- const style = props.style;
- const height = style.height;
- const width = style.width;
- let midHeight = height * 0.5;
- let points = `0,0 0,${height} ${width},${midHeight}`;
+const Toggle = ({style}) => {
+ const {height, width} = style;
+ const midHeight = height * 0.5;
+ const points = `0,0 0,${height} ${width},${midHeight}`;
+
return (
);
};
-
Toggle.propTypes = {
- style: React.PropTypes.object
+ style: PropTypes.object
};
-const Header = (props) => {
- const style = props.style;
+const Header = ({node, style}) => {
return (
- {props.node.name}
+ {node.name}
);
};
-
Header.propTypes = {
- style: React.PropTypes.object,
- node: React.PropTypes.object.isRequired
+ style: PropTypes.object,
+ node: PropTypes.object.isRequired
};
@Radium
class Container extends React.Component {
- constructor(props){
- super(props);
- }
- render(){
+ render() {
const {style, decorators, terminal, onClick, node} = this.props;
+
return (
-
- { !terminal ? this.renderToggle() : null }
-
+
this.clickableRef = ref}
+ style={style.container}>
+ {!terminal ? this.renderToggle() : null}
+
+
);
}
- renderToggle(){
- const animations = this.props.animations;
- if(!animations){ return this.renderToggleDecorator(); }
+
+ renderToggle() {
+ const {animations} = this.props;
+
+ if (!animations) {
+ return this.renderToggleDecorator();
+ }
+
return (
-
+ this.velocityRef = ref}>
{this.renderToggleDecorator()}
);
}
- renderToggleDecorator(){
+
+ renderToggleDecorator() {
const {style, decorators} = this.props;
- return ();
+
+ return ;
}
}
-
Container.propTypes = {
- style: React.PropTypes.object.isRequired,
- decorators: React.PropTypes.object.isRequired,
- terminal: React.PropTypes.bool.isRequired,
- onClick: React.PropTypes.func.isRequired,
- animations: React.PropTypes.oneOfType([
- React.PropTypes.object,
- React.PropTypes.bool
+ style: PropTypes.object.isRequired,
+ decorators: PropTypes.object.isRequired,
+ terminal: PropTypes.bool.isRequired,
+ onClick: PropTypes.func.isRequired,
+ animations: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.bool
]).isRequired,
- node: React.PropTypes.object.isRequired
+ node: PropTypes.object.isRequired
};
export default {
diff --git a/src/components/header.js b/src/components/header.js
index 8b4acb3..c5edd09 100644
--- a/src/components/header.js
+++ b/src/components/header.js
@@ -1,52 +1,57 @@
'use strict';
import React from 'react';
+import PropTypes from 'prop-types';
import shallowEqual from 'shallowequal';
import deepEqual from 'deep-equal';
class NodeHeader extends React.Component {
- constructor(props){
- super(props);
- }
- shouldComponentUpdate(nextProps){
+ shouldComponentUpdate(nextProps) {
const props = this.props;
const nextPropKeys = Object.keys(nextProps);
- for(let i = 0; i < nextPropKeys.length; i++){
+
+ for (let i = 0; i < nextPropKeys.length; i++) {
const key = nextPropKeys[i];
- if(key === 'animations'){ continue; }
+ if (key === 'animations') {
+ continue;
+ }
+
const isEqual = shallowEqual(props[key], nextProps[key]);
- if(!isEqual){ return true; }
+ if (!isEqual) {
+ return true;
+ }
}
- return !deepEqual(props.animations, nextProps.animations, { strict: true });
+
+ return !deepEqual(props.animations, nextProps.animations, {strict: true});
}
- render(){
- const {style, decorators} = this.props;
- const terminal = !this.props.node.children;
- const active = this.props.node.active;
+
+ render() {
+ const {animations, decorators, node, onClick, style} = this.props;
+ const {active, children} = node;
+ const terminal = !children;
const container = [style.link, active ? style.activeLink : null];
- const headerStyles = Object.assign({ container }, this.props.style);
+ const headerStyles = Object.assign({container}, style);
+
return (
-
+
);
}
}
NodeHeader.propTypes = {
- style: React.PropTypes.object.isRequired,
- decorators: React.PropTypes.object.isRequired,
- animations: React.PropTypes.oneOfType([
- React.PropTypes.object,
- React.PropTypes.bool
+ style: PropTypes.object.isRequired,
+ decorators: PropTypes.object.isRequired,
+ animations: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.bool
]).isRequired,
- node: React.PropTypes.object.isRequired,
- onClick: React.PropTypes.func
+ node: PropTypes.object.isRequired,
+ onClick: PropTypes.func
};
export default NodeHeader;
diff --git a/src/components/node.js b/src/components/node.js
index 16393aa..eb1c5ca 100644
--- a/src/components/node.js
+++ b/src/components/node.js
@@ -1,110 +1,150 @@
'use strict';
import React from 'react';
+import PropTypes from 'prop-types';
import {VelocityTransitionGroup} from 'velocity-react';
import NodeHeader from './header';
class TreeNode extends React.Component {
- constructor(props){
- super(props);
+ constructor() {
+ super();
+
this.onClick = this.onClick.bind(this);
}
- onClick(){
- let toggled = !this.props.node.toggled;
- let onToggle = this.props.onToggle;
- if(onToggle){ onToggle(this.props.node, toggled); }
+
+ onClick() {
+ const {node, onToggle} = this.props;
+ const {toggled} = node;
+
+ if (onToggle) {
+ onToggle(node, !toggled);
+ }
}
- animations(){
- const props = this.props;
- if(props.animations === false){ return false; }
- let anim = Object.assign({}, props.animations, props.node.animations);
+
+ animations() {
+ const {animations, node} = this.props;
+
+ if (animations === false) {
+ return false;
+ }
+
+ const anim = Object.assign({}, animations, node.animations);
return {
toggle: anim.toggle(this.props),
drawer: anim.drawer(this.props)
};
}
- decorators(){
+
+ decorators() {
// Merge Any Node Based Decorators Into The Pack
- const props = this.props;
- let nodeDecorators = props.node.decorators || {};
- return Object.assign({}, props.decorators, nodeDecorators);
+ const {decorators, node} = this.props;
+ let nodeDecorators = node.decorators || {};
+
+ return Object.assign({}, decorators, nodeDecorators);
}
- render(){
+
+ render() {
+ const {style} = this.props;
const decorators = this.decorators();
const animations = this.animations();
+
return (
-
+ this.topLevelRef = ref}
+ style={style.base}>
{this.renderHeader(decorators, animations)}
+
{this.renderDrawer(decorators, animations)}
);
}
- renderDrawer(decorators, animations){
- const toggled = this.props.node.toggled;
- if(!animations && !toggled){ return null; }
- if(!animations && toggled){
+
+ renderDrawer(decorators, animations) {
+ const {node: {toggled}} = this.props;
+
+ if (!animations && !toggled) {
+ return null;
+ } else if (!animations && toggled) {
return this.renderChildren(decorators, animations);
}
+
+ const {animation, duration, ...restAnimationInfo} = animations.drawer;
return (
-
+ this.velocityRef = ref}>
{toggled ? this.renderChildren(decorators, animations) : null}
);
}
- renderHeader(decorators, animations){
+
+ renderHeader(decorators, animations) {
+ const {node, style} = this.props;
+
return (
-
+
);
}
- renderChildren(decorators){
- if(this.props.node.loading){ return this.renderLoading(decorators); }
- let children = this.props.node.children;
- if (!Array.isArray(children)) { children = children ? [children] : []; }
+
+ renderChildren(decorators) {
+ const {animations, decorators: propDecorators, node, style} = this.props;
+
+ if (node.loading) {
+ return this.renderLoading(decorators);
+ }
+
+ let children = node.children;
+ if (!Array.isArray(children)) {
+ children = children ? [children] : [];
+ }
+
return (
-
- {children.map((child, index) =>
-
+ this.subtreeRef = ref}>
+ {children.map((child, index) =>
)}
);
}
- renderLoading(decorators){
+
+ renderLoading(decorators) {
+ const {style} = this.props;
+
return (
-
+
);
}
- _eventBubbles(){
- return { onToggle: this.props.onToggle };
+
+ _eventBubbles() {
+ const {onToggle} = this.props;
+
+ return {
+ onToggle
+ };
}
}
TreeNode.propTypes = {
- style: React.PropTypes.object.isRequired,
- node: React.PropTypes.object.isRequired,
- decorators: React.PropTypes.object.isRequired,
- animations: React.PropTypes.oneOfType([
- React.PropTypes.object,
- React.PropTypes.bool
+ style: PropTypes.object.isRequired,
+ node: PropTypes.object.isRequired,
+ decorators: PropTypes.object.isRequired,
+ animations: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.bool
]).isRequired,
- onToggle: React.PropTypes.func
+ onToggle: PropTypes.func
};
export default TreeNode;
diff --git a/src/components/treebeard.js b/src/components/treebeard.js
index 7573f55..2e15254 100644
--- a/src/components/treebeard.js
+++ b/src/components/treebeard.js
@@ -1,6 +1,7 @@
'use strict';
import React from 'react';
+import PropTypes from 'prop-types';
import TreeNode from './node';
import defaultDecorators from './decorators';
@@ -8,24 +9,24 @@ import defaultTheme from '../themes/default';
import defaultAnimations from '../themes/animations';
class TreeBeard extends React.Component {
- constructor(props){
- super(props);
- }
- render(){
- let data = this.props.data;
+ render() {
+ const {animations, decorators, data: propsData, onToggle, style} = this.props;
+ let data = propsData;
+
// Support Multiple Root Nodes. Its not formally a tree, but its a use-case.
- if(!Array.isArray(data)){ data = [data]; }
+ if (!Array.isArray(data)) {
+ data = [data];
+ }
return (
-
+ this.treeBaseRef = ref}>
{data.map((node, index) =>
-
+
)}
);
@@ -33,17 +34,17 @@ class TreeBeard extends React.Component {
}
TreeBeard.propTypes = {
- style: React.PropTypes.object,
- data: React.PropTypes.oneOfType([
- React.PropTypes.object,
- React.PropTypes.array
+ style: PropTypes.object,
+ data: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.array
]).isRequired,
- animations: React.PropTypes.oneOfType([
- React.PropTypes.object,
- React.PropTypes.bool
+ animations: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.bool
]),
- onToggle: React.PropTypes.func,
- decorators: React.PropTypes.object
+ onToggle: PropTypes.func,
+ decorators: PropTypes.object
};
TreeBeard.defaultProps = {
diff --git a/src/index.js b/src/index.js
index 734094f..f04b432 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,13 @@
'use strict';
-module.exports = {
- Treebeard: require('./components/treebeard'),
- decorators: require('./components/decorators'),
- animations: require('./themes/animations'),
- theme: require('./themes/default')
+
+import Treebeard from './components/treebeard';
+import decorators from './components/decorators';
+import animations from './themes/animations';
+import theme from './themes/default';
+
+export default {
+ Treebeard,
+ decorators,
+ animations,
+ theme
};
diff --git a/src/themes/animations.js b/src/themes/animations.js
index e4ce091..642c33c 100644
--- a/src/themes/animations.js
+++ b/src/themes/animations.js
@@ -1,22 +1,18 @@
'use strict';
export default {
- toggle: (props) => {
- return {
- animation: { rotateZ: props.node.toggled ? 90 : 0 },
+ toggle: ({node: {toggled}}) => ({
+ animation: {rotateZ: toggled ? 90 : 0},
+ duration: 300
+ }),
+ drawer: (/* props */) => ({
+ enter: {
+ animation: 'slideDown',
duration: 300
- };
- },
- drawer: (/* props */) => {
- return {
- enter: {
- animation: 'slideDown',
- duration: 300
- },
- leave: {
- animation: 'slideUp',
- duration: 300
- }
- };
- }
+ },
+ leave: {
+ animation: 'slideUp',
+ duration: 300
+ }
+ })
};
diff --git a/test/src/components/decorator-tests.js b/test/src/components/decorator-tests.js
index 248c4a9..7321ce1 100644
--- a/test/src/components/decorator-tests.js
+++ b/test/src/components/decorator-tests.js
@@ -2,20 +2,23 @@
'use strict';
-const sinon = require('sinon');
-const React = require('react');
-const TestUtils = require('react-addons-test-utils');
-const VelocityComponent = require('velocity-react').VelocityComponent;
-const defaultDecorators = require('../../../src/components/decorators');
-const factory = require('../utils/factory');
+import React from 'react';
+import TestUtils from 'react-dom/test-utils';
+
+import {VelocityComponent} from 'velocity-react';
+import sinon from 'sinon';
+
+import defaultDecorators from '../../../src/components/decorators';
+
+import {createAnimations, createDecorators} from '../utils/factory';
const defaults = {
style: {},
- node: { children: [] },
- animations: { toggle: {} },
+ node: {children: []},
+ animations: {toggle: {}},
terminal: false,
- decorators: factory.createDecorators(),
- onClick: function(){}
+ decorators: createDecorators(),
+ onClick: () => null
};
const Container = defaultDecorators.Container;
@@ -25,120 +28,139 @@ describe('container decorator component', () => {
const onClick = sinon.spy();
const container = TestUtils.renderIntoDocument(
+ onClick={onClick}/>
);
- const clickable = container.refs.clickable;
+ const clickable = container.clickableRef;
TestUtils.Simulate.click(clickable);
+
onClick.should.be.called.once;
});
it('should render the toggle decorator not terminal', () => {
- const toggleType = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ toggle: toggleType });
+ class toggleType extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({toggle: toggleType});
const container = TestUtils.renderIntoDocument(
+ decorators={decorators}
+ terminal={false}/>
);
const toggle = TestUtils.findRenderedComponentWithType(container, toggleType);
+
toggle.should.exist;
});
it('should not render the toggle decorator if the node is terminal', () => {
- const toggleType = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ toggle: toggleType });
+ class toggleType extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({toggle: toggleType});
const container = TestUtils.renderIntoDocument(
+ decorators={decorators}
+ terminal={true}/>
);
const toggle = TestUtils.scryRenderedComponentsWithType(container, toggleType);
+
toggle.should.be.empty;
});
it('should pass the style to the toggle decorator', () => {
- const style = { toggle: { color: 'red' } };
- const toggleType = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ toggle: toggleType });
+ const style = {toggle: {color: 'red'}};
+ class toggleType extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({toggle: toggleType});
const container = TestUtils.renderIntoDocument(
+ decorators={decorators}
+ style={style}/>
);
const toggle = TestUtils.findRenderedComponentWithType(container, toggleType);
+
toggle.props.style.should.equal(style.toggle);
});
it('should render the toggle decorator in a velocity component', () => {
- const container = TestUtils.renderIntoDocument(
-
- );
+ const container = TestUtils.renderIntoDocument();
const component = TestUtils.findRenderedComponentWithType(container, VelocityComponent);
+
component.should.exist;
});
it('should not render a velocity component if animations is false', () => {
const container = TestUtils.renderIntoDocument(
+ animations={false}/>
);
- const velocity = container.refs.velocity;
+ const velocity = container.velocityRef;
+
global.should.not.exist(velocity);
});
it('should render a velocity component if animations is an object', () => {
- const animations = factory.createAnimations();
+ const animations = createAnimations();
const container = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const velocity = container.refs.velocity;
+ const velocity = container.velocityRef;
+
velocity.should.exist;
});
it('should pass velocity the toggle animation and duration props', () => {
- const animations = { toggle: { duration: 1, animation: 'slideUp' } };
+ const animations = {toggle: {duration: 1, animation: 'slideUp'}};
const container = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const velocity = container.refs.velocity;
+ const velocity = container.velocityRef;
+
velocity.props.duration.should.equal(animations.toggle.duration);
velocity.props.animation.should.equal(animations.toggle.animation);
});
it('should render the header decorator', () => {
- const headType = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ header: headType });
+ class headType extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({header: headType});
const container = TestUtils.renderIntoDocument(
+ decorators={decorators}/>
);
const head = TestUtils.findRenderedComponentWithType(container, headType);
+
head.should.exist;
});
it('should pass the node and style to the header decorator', () => {
- const style = { header: { color: 'red' } };
- const node = { name: 'terminal-node' };
- const headType = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ header: headType });
+ const style = {header: {color: 'red'}};
+ const node = {name: 'terminal-node'};
+ class headType extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({header: headType});
const container = TestUtils.renderIntoDocument(
+ decorators={decorators}
+ node={node}
+ style={style}/>
);
const head = TestUtils.findRenderedComponentWithType(container, headType);
+
head.props.style.should.equal(style.header);
head.props.node.should.equal(node);
});
diff --git a/test/src/components/header-tests.js b/test/src/components/header-tests.js
index ce61edb..7aff8b4 100644
--- a/test/src/components/header-tests.js
+++ b/test/src/components/header-tests.js
@@ -2,131 +2,136 @@
'use strict';
-const React = require('react');
-const TestUtils = require('react-addons-test-utils');
-const Header = require('../../../src/components/header');
-const factory = require('../utils/factory');
+import React from 'react';
+import TestUtils from 'react-dom/test-utils';
-const ContainerType = React.createClass({ render: () => });
+import Header from '../../../src/components/header';
+
+import {createDecorators} from '../utils/factory';
+
+class ContainerType extends React.Component {
+ render() {
+ return ;
+ }
+}
const defaults = {
style: {},
- node: { children: [] },
- animations: { toggle: {} },
- decorators: factory.createDecorators({ container: ContainerType })
+ node: {children: []},
+ animations: {toggle: {}},
+ decorators: createDecorators({container: ContainerType})
};
describe('header component', () => {
it('should render the container decorator', () => {
- const header = TestUtils.renderIntoDocument(
-
- );
+ const header = TestUtils.renderIntoDocument();
const container = TestUtils.findRenderedComponentWithType(header, ContainerType);
+
container.should.exist;
});
it('should update the component if a prop changes', () => {
- const node = { toggled: false };
+ const node = {toggled: false};
const header = TestUtils.renderIntoDocument(
+ node={node}/>
);
- const nextProps = { node: { toggled: !node.toggled } };
+ const nextProps = {node: {toggled: !node.toggled}};
+
header.shouldComponentUpdate(nextProps).should.be.true;
});
it('should not update the component if no props change', () => {
- const node = { toggled: false };
+ const node = {toggled: false};
const header = TestUtils.renderIntoDocument(
+ node={node}/>
);
- const nextProps = Object.assign({}, defaults, { node: { toggled: node.toggled } });
+ const nextProps = Object.assign({}, defaults, {node: {toggled: node.toggled}});
+
header.shouldComponentUpdate(nextProps).should.be.false;
});
it('should not update when deep nested animation props have not changed value', () => {
- const animations = { nested: { prop: 'value' } };
+ const animations = {nested: {prop: 'value'}};
const header = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const sameAnimationProp = { animations: { nested: { prop: animations.nested.prop } } };
+ const sameAnimationProp = {animations: {nested: {prop: animations.nested.prop}}};
const nextProps = Object.assign({}, defaults, sameAnimationProp);
+
header.shouldComponentUpdate(nextProps).should.be.false;
});
it('should update when deep nested animation props have changed value', () => {
- const animations = { nested: { prop: 'value' } };
+ const animations = {nested: {prop: 'value'}};
const header = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const diffAnimationProp = { animations: { nested: { prop: 'new-value' } } };
+ const diffAnimationProp = {animations: {nested: {prop: 'new-value'}}};
const nextProps = Object.assign({}, defaults, diffAnimationProp);
+
header.shouldComponentUpdate(nextProps).should.be.true;
});
it('should pass a true terminal prop to the container when there are no children in the node', () => {
- const node = { name: 'terminal-node' };
+ const node = {name: 'terminal-node'};
const header = TestUtils.renderIntoDocument(
+ node={node}/>
);
const container = TestUtils.findRenderedComponentWithType(header, ContainerType);
+
container.props.terminal.should.be.true;
});
it('should pass a false terminal prop to the container when there are children in the node', () => {
- const node = { children: [{ name: 'child-node'}] };
+ const node = {children: [{name: 'child-node'}]};
const header = TestUtils.renderIntoDocument(
+ node={node}/>
);
const container = TestUtils.findRenderedComponentWithType(header, ContainerType);
+
container.props.terminal.should.be.false;
});
it('should pass in the high-level link style to the container', () => {
- const style = { link: { backgroundColor: 'black' } };
+ const style = {link: {backgroundColor: 'black'}};
const header = TestUtils.renderIntoDocument(
+ style={style}/>
);
const container = TestUtils.findRenderedComponentWithType(header, ContainerType);
+
container.props.style.container[0].should.equal(style.link);
});
it('should pass the active link style prop to the container when the node is active', () => {
- const node = { active: true };
- const style = { activeLink: { color: 'red' } };
+ const node = {active: true};
+ const style = {activeLink: {color: 'red'}};
const header = TestUtils.renderIntoDocument(
+ node={node}
+ style={style}/>
);
const container = TestUtils.findRenderedComponentWithType(header, ContainerType);
+
container.props.style.container[1].should.equal(style.activeLink);
});
it('should not pass the active link style prop to the container when the node is inactive', () => {
- const node = { active: false };
- const style = { activeLink: { color: 'red' } };
+ const node = {active: false};
+ const style = {activeLink: {color: 'red'}};
const header = TestUtils.renderIntoDocument(
+ node={node}
+ style={style}/>
);
const container = TestUtils.findRenderedComponentWithType(header, ContainerType);
+
global.should.not.exist(container.props.style.container[1]);
});
diff --git a/test/src/components/node-tests.js b/test/src/components/node-tests.js
index 2120965..892e7cb 100644
--- a/test/src/components/node-tests.js
+++ b/test/src/components/node-tests.js
@@ -2,40 +2,41 @@
'use strict';
-const sinon = require('sinon');
-const React = require('react');
-const ReactDOM = require('react-dom');
-const TestUtils = require('react-addons-test-utils');
-const TreeNode = require('../../../src/components/node');
-const factory = require('../utils/factory');
+import React from 'react';
+import TestUtils from 'react-dom/test-utils';
+
+import sinon from 'sinon';
+import {VelocityTransitionGroup as TransitionGroup} from 'velocity-react';
+
+import NodeHeader from '../../../src/components/header';
+import TreeNode from '../../../src/components/node';
+
+import {createAnimations, createDecorators} from '../utils/factory';
const defaults = {
style: {},
- node: { chilren: [] },
- animations: factory.createAnimations(),
- decorators: factory.createDecorators()
+ node: {chilren: []},
+ animations: createAnimations(),
+ decorators: createDecorators()
};
describe('node component', () => {
it('should not have any internal state', () => {
- const treeNode = TestUtils.renderIntoDocument(
-
- );
+ const treeNode = TestUtils.renderIntoDocument();
+
global.should.not.exist(treeNode.state);
});
it('should invert the toggle state on click', (done) => {
- const node = { toggled: true };
- const onToggle = function(toggledNode, toggled){
+ const node = {toggled: true};
+ const onToggle = (toggledNode, toggled) => {
toggled.should.equal(!toggledNode.toggled);
done();
};
const treeNode = TestUtils.renderIntoDocument(
-
+
);
treeNode.onClick();
});
@@ -49,276 +50,252 @@ describe('node component', () => {
/>
);
treeNode.onClick();
+
onToggle.should.be.called.once;
});
it('should not throw an exception if a callback is not registered on click', () => {
- const treeNode = TestUtils.renderIntoDocument(
-
- );
- (() => { treeNode.onClick(); }).should.not.throw(Error);
+ const treeNode = TestUtils.renderIntoDocument();
+
+ (() => treeNode.onClick()).should.not.throw(Error);
});
it('should use the node animations if defined', () => {
const nodeAnimations = {
- toggle: sinon.stub().returns({ duration: 0, animation: 'fadeIn' }),
- drawer: sinon.stub().returns({ duration: 0, animation: 'fadeIn' })
+ toggle: sinon.stub().returns({duration: 0, animation: 'fadeIn'}),
+ drawer: sinon.stub().returns({duration: 0, animation: 'fadeIn'})
};
- const node = { animations: nodeAnimations };
+ const node = {animations: nodeAnimations};
const treeNode = TestUtils.renderIntoDocument(
-
+
);
treeNode.animations();
+
nodeAnimations.toggle.should.be.calledWith(treeNode.props);
nodeAnimations.drawer.should.be.calledWith(treeNode.props);
});
it('should fallback to the prop animations if the node animations are not defined', () => {
const animations = {
- toggle: sinon.stub().returns({ duration: 0, animation: 'fadeIn' }),
- drawer: sinon.stub().returns({ duration: 0, animation: 'fadeIn' })
+ toggle: sinon.stub().returns({duration: 0, animation: 'fadeIn'}),
+ drawer: sinon.stub().returns({duration: 0, animation: 'fadeIn'})
};
const treeNode = TestUtils.renderIntoDocument(
-
+
);
treeNode.animations();
+
animations.toggle.should.be.calledWith(treeNode.props);
animations.drawer.should.be.calledWith(treeNode.props);
});
it('should use the node decorators if defined', () => {
- const ContainerDecorator = React.createClass({ render: () => });
+ class ContainerDecorator extends React.Component {
+ render() {
+ return ;
+ }
+ }
const nodeDecorators = {
Container: ContainerDecorator
};
- const node = { decorators: nodeDecorators, children: [] };
+ const node = {decorators: nodeDecorators, children: []};
const treeNode = TestUtils.renderIntoDocument(
-
+
);
- TestUtils.findRenderedComponentWithType(treeNode, ContainerDecorator).should.exist;
+ const component = TestUtils.findRenderedComponentWithType(treeNode, ContainerDecorator);
+
+ component.should.exist;
});
it('should fallback to the prop decorators if the node decorators are not defined', () => {
- const ContainerDecorator = React.createClass({ render: () => });
+ class ContainerDecorator extends React.Component {
+ render() {
+ return ;
+ }
+ }
const decorators = {
Container: ContainerDecorator
};
- const node = { children: [] };
+ const node = {children: []};
const treeNode = TestUtils.renderIntoDocument(
-
+
);
- TestUtils.findRenderedComponentWithType(treeNode, ContainerDecorator).should.exist;
+ const component = TestUtils.findRenderedComponentWithType(treeNode, ContainerDecorator);
+
+ component.should.exist;
});
it('should render a list item at the top level', () => {
const treeNode = TestUtils.renderIntoDocument(
);
- const topLevel = treeNode.refs.topLevel;
+ const topLevel = treeNode.topLevelRef;
topLevel.tagName.toLowerCase().should.equal('li');
});
it('should render the NodeHeader component', () => {
- const NodeHeader = require('../../../src/components/header');
- const treeNode = TestUtils.renderIntoDocument(
-
- );
- TestUtils.findRenderedComponentWithType(treeNode, NodeHeader).should.exist;
+ const treeNode = TestUtils.renderIntoDocument();
+ const component = TestUtils.findRenderedComponentWithType(treeNode, NodeHeader);
+
+ component.should.exist;
});
it('should render the subtree if toggled', () => {
- const node = { toggled: true };
- const treeNode = TestUtils.renderIntoDocument(
-
- );
- treeNode.refs.subtree.should.exist;
+ const node = {toggled: true};
+ const treeNode = TestUtils.renderIntoDocument();
+
+ treeNode.subtreeRef.should.exist;
});
it('should not render the children if not toggled', () => {
- const node = { toggled: false };
- const treeNode = TestUtils.renderIntoDocument(
-
- );
- global.should.not.exist(treeNode.refs.subtree);
+ const node = {toggled: false};
+ const treeNode = TestUtils.renderIntoDocument();
+
+ global.should.not.exist(treeNode.subtreeRef);
});
it('should wrap the children in a velocity transition group', () => {
- const TransitionGroup = require('velocity-react').VelocityTransitionGroup;
- const treeNode = TestUtils.renderIntoDocument(
-
- );
+ const treeNode = TestUtils.renderIntoDocument();
const component = TestUtils.findRenderedComponentWithType(treeNode, TransitionGroup);
+
component.should.exist;
});
it('should pass velocity the drawer enter animation and duration props', () => {
- const animations = factory.createAnimations();
+ const animations = createAnimations();
const treeNode = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const velocity = treeNode.refs.velocity;
+ const velocity = treeNode.velocityRef;
const drawer = animations.drawer();
+
velocity.props.enter.animation.should.equal(drawer.enter.animation);
velocity.props.enter.duration.should.equal(drawer.enter.duration);
});
it('should pass velocity the drawer leave animation and duration props', () => {
- const animations = factory.createAnimations();
+ const animations = createAnimations();
const treeNode = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const velocity = treeNode.refs.velocity;
+ const velocity = treeNode.velocityRef;
const drawer = animations.drawer();
+
velocity.props.leave.animation.should.equal(drawer.leave.animation);
velocity.props.leave.duration.should.equal(drawer.leave.duration);
});
it('should not render a velocity component if animations is false and not toggled', () => {
- const node = { toggled: false };
+ const node = {toggled: false};
const treeNode = TestUtils.renderIntoDocument(
+ animations={false}
+ node={node}/>
);
- const velocity = treeNode.refs.velocity;
+ const velocity = treeNode.velocityRef;
+
global.should.not.exist(velocity);
});
it('should not render a velocity component if animations is false and toggled', () => {
- const node = { toggled: true };
+ const node = {toggled: true};
const treeNode = TestUtils.renderIntoDocument(
+ animations={false}
+ node={node}/>
);
- const velocity = treeNode.refs.velocity;
+ const velocity = treeNode.velocityRef;
+
global.should.not.exist(velocity);
});
it('should render a velocity component if animations is an object', () => {
- const animations = factory.createAnimations();
+ const animations = createAnimations();
const treeNode = TestUtils.renderIntoDocument(
+ animations={animations}/>
);
- const velocity = treeNode.refs.velocity;
+ const velocity = treeNode.velocityRef;
+
velocity.should.exist;
});
it('should wrap the children in a list', () => {
- const node = { toggled: true };
+ const node = {toggled: true};
const treeNode = TestUtils.renderIntoDocument(
+ node={node}/>
);
- const subtree = treeNode.refs.subtree;
+ const subtree = treeNode.subtreeRef;
+
subtree.tagName.toLowerCase().should.equal('ul');
});
it('should render a TreeNode component for each child', () => {
const node = {
toggled: true,
- children: [ {node: {}}, {node: {}}, {node: {}} ]
+ children: [{node: {}}, {node: {}}, {node: {}}]
};
const treeNode = TestUtils.renderIntoDocument(
+ node={node}/>
);
// Find All TreeNodes (+ Top Level TreeNode)
const nodes = TestUtils.scryRenderedComponentsWithType(treeNode, TreeNode);
+
nodes.length.should.equal(node.children.length + 1);
});
it('should render the loading decorator if the node is loading and toggled', () => {
- const node = { toggled: true, loading: true };
- const LoadingDecorator = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ loading: LoadingDecorator });
+ const node = {toggled: true, loading: true};
+ class LoadingDecorator extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({loading: LoadingDecorator});
const treeNode = TestUtils.renderIntoDocument(
+ node={node}
+ decorators={decorators}/>
);
const loading = TestUtils.findRenderedComponentWithType(treeNode, LoadingDecorator);
+
loading.should.exist;
});
it('should not render the loading decorator if the node is not loading but toggled', () => {
- const node = { toggled: true, loading: false };
- const LoadingDecorator = React.createClass({ render: () => });
- const decorators = factory.createDecorators({ loading: LoadingDecorator });
+ const node = {toggled: true, loading: false};
+ class LoadingDecorator extends React.Component {
+ render() {
+ return ;
+ }
+ }
+ const decorators = createDecorators({loading: LoadingDecorator});
const treeNode = TestUtils.renderIntoDocument(
+ node={node}
+ decorators={decorators}/>
);
const loading = TestUtils.scryRenderedComponentsWithType(treeNode, LoadingDecorator);
+
loading.should.be.empty;
});
it('should not render the children if the node is Loading', () => {
- const node = { toggled: true, loading: true };
- const treeNode = TestUtils.renderIntoDocument(
-
- );
- global.should.not.exist(treeNode.refs.subtree);
- });
-
- it('should render a child with an id key if available', () => {
- const id = 'SpecialNode';
- const node = {
- toggled: true,
- children: [{ id }]
- };
+ const node = {toggled: true, loading: true};
const treeNode = TestUtils.renderIntoDocument(
+ node={node}/>
);
- const nodes = TestUtils.scryRenderedComponentsWithType(treeNode, TreeNode);
- const element = ReactDOM.findDOMNode(nodes[1]);
- const expectedId = '$' + id;
- element.dataset.reactid.should.contain(expectedId);
- });
- it('should render a child with an index key if id is not available', () => {
- const node = {
- toggled: true,
- children: [{ name: 'node' }]
- };
- const treeNode = TestUtils.renderIntoDocument(
-
- );
- const nodes = TestUtils.scryRenderedComponentsWithType(treeNode, TreeNode);
- const element = ReactDOM.findDOMNode(nodes[1]);
- const expectedId = '$0';
- element.dataset.reactid.should.contain(expectedId);
+ global.should.not.exist(treeNode.subtreeRef);
});
});
diff --git a/test/src/components/treebeard-tests.js b/test/src/components/treebeard-tests.js
index 7f3b35b..bea7134 100644
--- a/test/src/components/treebeard-tests.js
+++ b/test/src/components/treebeard-tests.js
@@ -2,11 +2,14 @@
'use strict';
-const React = require('react');
-const ReactDOM = require('react-dom');
-const TestUtils = require('react-addons-test-utils');
-const TreeNode = require('../../../src/components/node');
-const Treebeard = require('../../../src/components/treebeard');
+import React from 'react';
+import TestUtils from 'react-dom/test-utils';
+
+import defaultDecorators from '../../../src/components/decorators';
+import TreeNode from '../../../src/components/node';
+import Treebeard from '../../../src/components/treebeard';
+import defaultAnimations from '../../../src/themes/animations';
+import defaultTheme from '../../../src/themes/default';
const defaults = {
name: '',
@@ -15,93 +18,59 @@ const defaults = {
describe('treebeard component', () => {
it('should render the treebase as a list', () => {
- const treebeard = TestUtils.renderIntoDocument(
-
- );
- const treeBase = treebeard.refs.treeBase;
+ const treebeard = TestUtils.renderIntoDocument();
+ const treeBase = treebeard.treeBaseRef;
+
treeBase.tagName.toLowerCase().should.equal('ul');
});
it('should render the treebase as a list', () => {
- const treebeard = TestUtils.renderIntoDocument(
-
- );
+ const treebeard = TestUtils.renderIntoDocument();
const nodes = TestUtils.scryRenderedComponentsWithType(treebeard, TreeNode);
+
nodes.length.should.equal(1);
});
it('should pass the top level tree node the associated props', () => {
const treebeard = TestUtils.renderIntoDocument(
- {}}
- />
+ null}/>
);
const node = TestUtils.findRenderedComponentWithType(treebeard, TreeNode);
+
node.props.node.should.equal(treebeard.props.data);
node.props.onToggle.should.equal(treebeard.props.onToggle);
});
it('should use the default theme if none specified', () => {
- const treebeard = TestUtils.renderIntoDocument(
-
- );
+ const treebeard = TestUtils.renderIntoDocument();
const node = TestUtils.findRenderedComponentWithType(treebeard, TreeNode);
- const defaultTheme = require('../../../src/themes/default');
+
node.props.style.should.equal(defaultTheme.tree.node);
});
it('should use the default animations if none specified', () => {
- const treebeard = TestUtils.renderIntoDocument(
-
- );
+ const treebeard = TestUtils.renderIntoDocument();
const node = TestUtils.findRenderedComponentWithType(treebeard, TreeNode);
- const defaultAnimations = require('../../../src/themes/animations');
+
node.props.animations.should.equal(defaultAnimations);
});
it('should use the default decorators if none specified', () => {
- const treebeard = TestUtils.renderIntoDocument(
-
- );
+ const treebeard = TestUtils.renderIntoDocument();
const node = TestUtils.findRenderedComponentWithType(treebeard, TreeNode);
- const defaultDecorators = require('../../../src/components/decorators');
+
node.props.decorators.should.equal(defaultDecorators);
});
it('should support rendering multiple nodes at the root level', () => {
const multipleRootNodes = [
- { name: 'root-1', children: [] },
- { name: 'root-2', children: [] }
+ {name: 'root-1', children: []},
+ {name: 'root-2', children: []}
];
- const treebeard = TestUtils.renderIntoDocument(
-
- );
+ const treebeard = TestUtils.renderIntoDocument();
const nodes = TestUtils.scryRenderedComponentsWithType(treebeard, TreeNode);
- nodes.length.should.equal(multipleRootNodes.length);
- });
- it('should render a root node with an id key if available', () => {
- const id = 'RootNode';
- const rootNode = { id: id, name: 'root-1', children: [] };
- const treebeard = TestUtils.renderIntoDocument(
-
- );
- const node = TestUtils.findRenderedComponentWithType(treebeard, TreeNode);
- const element = ReactDOM.findDOMNode(node);
- const expectedId = '$' + id;
- element.dataset.reactid.should.contain(expectedId);
- });
-
- it('should render a root node with an index key if id is not available', () => {
- const rootNode = { name: 'root-1', children: [] };
- const treebeard = TestUtils.renderIntoDocument(
-
- );
- const node = TestUtils.findRenderedComponentWithType(treebeard, TreeNode);
- const element = ReactDOM.findDOMNode(node);
- const expectedId = '$0';
- element.dataset.reactid.should.contain(expectedId);
+ nodes.length.should.equal(multipleRootNodes.length);
});
-
});
diff --git a/test/src/utils/factory.js b/test/src/utils/factory.js
index c6d4fe0..30f9a35 100644
--- a/test/src/utils/factory.js
+++ b/test/src/utils/factory.js
@@ -1,46 +1,45 @@
'use strict';
-const React = require('react');
+import React from 'react';
-module.exports = {
- createDecorators: function(spec){
- spec = spec || {};
- return {
- Loading: (props) => {
- return spec.loading ? : ;
- },
- Toggle: (props) => {
- return spec.toggle ? : ;
- },
- Header: (props) => {
- return spec.header ? : ;
- },
- Container: (props) => {
- return spec.container ? : ;
- }
+export const createDecorators = (spec) => {
+ spec = spec || {};
+ return {
+ Loading: (props) => {
+ return spec.loading ? : ;
+ },
+ Toggle: (props) => {
+ return spec.toggle ? : ;
+ },
+ Header: (props) => {
+ return spec.header ? : ;
+ },
+ Container: (props) => {
+ return spec.container ? : ;
+ }
- };
- },
- createAnimations: function(){
- return {
- toggle: () => {
- return {
- animation: 'fadeOut',
+ };
+};
+
+export const createAnimations = () => {
+ return {
+ toggle: () => {
+ return {
+ animation: 'fadeOut',
+ duration: 0
+ };
+ },
+ drawer: () => {
+ return {
+ enter: {
+ animation: 'slideDown',
+ duration: 0
+ },
+ leave: {
+ animation: 'slideUp',
duration: 0
- };
- },
- drawer: () => {
- return {
- enter: {
- animation: 'slideDown',
- duration: 0
- },
- leave: {
- animation: 'slideUp',
- duration: 0
- }
- };
- }
- };
- }
+ }
+ };
+ }
+ };
};