diff --git a/App.js b/App.js
index fbb0478..9678b90 100644
--- a/App.js
+++ b/App.js
@@ -1,10 +1,12 @@
import React from 'react';
-import {autobind} from 'core-decorators';
+import { Map } from 'immutable';
+
+import { autobind } from 'core-decorators';
import R from 'ramda';
import classNames from 'classnames';
import Tree from './tree-component';
import './styles.less';
-import {deleteNodeWithChildren} from './helpers';
+import { deleteNodeWithChildren } from './helpers';
import TreeRow from './tree-row-component';
import RowRenderer from './row-renderer';
import nodes from './mock-data';
@@ -22,22 +24,22 @@ export default class App extends React.Component {
state = {
nodes
- }
+ };
getRandomWord() {
- return fetch('http://www.setgetgo.com/randomword/get.php?len=4')
- .then((resp) => resp.text());
+ return fetch(
+ 'http://www.setgetgo.com/randomword/get.php?len=4'
+ ).then(resp => resp.text());
}
- @autobind
- handleExpand(nodeId) {
+ @autobind handleExpand(nodeId) {
const parentNode = this.state.nodes.byId[nodeId];
- if(parentNode.lazyLoad) {
+ if (parentNode.lazyLoad) {
setTimeout(() => {
const randomWords = [];
this.getRandomWord()
- .then((word) => randomWords.push(word))
+ .then(word => randomWords.push(word))
.then(this.getRandomWord)
- .then((word) => randomWords.push(word))
+ .then(word => randomWords.push(word))
.then(() => {
const newNode1 = {
id: `${nodeId}.${randomWords[0]}`,
@@ -54,7 +56,7 @@ export default class App extends React.Component {
[nodeId]: R.assoc('childIds', [newNode1.id, newNode2.id])
}),
R.assoc(newNode1.id, newNode1),
- R.assoc(newNode2.id, newNode2),
+ R.assoc(newNode2.id, newNode2)
)(this.state.nodes.byId);
this.setState({
nodes: {
@@ -62,13 +64,12 @@ export default class App extends React.Component {
byId
}
});
- })
- }, MOCK_SERVER_TIME)
+ });
+ }, MOCK_SERVER_TIME);
}
}
- @autobind
- handleClick(event, nodeId, parentId) {
+ @autobind handleClick(event, nodeId, parentId) {
const classNames = event.target.className;
if (R.contains('delete', classNames)) {
this.setState({
@@ -77,17 +78,36 @@ export default class App extends React.Component {
}
}
+ @autobind shouldSelectNode(node) {
+ console.log(
+ 'shouldSelectNode ',
+ node.id,
+ '---',
+ this.tree.getSelectedNode()
+ );
+
+ if (!node || node.id === this.tree.getSelectedNode()) {
+ return false; // Prevent from deselecting the current node
+ }
+ return true;
+ }
+
render() {
- const {nodes} = this.state;
+ const { nodes } = this.state;
return (
-
+
+ nodes={nodes}
+ onClick={this.handleClick}
+ onExpand={this.handleExpand}
+ ref={c => {
+ this.tree = c && c.state;
+ console.log('Tree ', c);
+ }}
+ shouldSelectNode={this.shouldSelectNode}
+ rowRenderer={RowRenderer}
+ onCollapse={this.handleCollapse}
+ />
);
}
diff --git a/__tests__/row.test.js b/__tests__/row.test.js
new file mode 100644
index 0000000..e52b7d6
--- /dev/null
+++ b/__tests__/row.test.js
@@ -0,0 +1,99 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import { fromJS, List } from 'immutable';
+import toJSON from 'enzyme-to-json';
+import Row from '../row';
+import TitleComponent from '../title';
+import logger from '../utils/logger';
+
+const mockData = {
+ byId: {
+ 1: {
+ id: '1',
+ name: 'One',
+ lazyLoad: true,
+ },
+ 2: {
+ id: '2',
+ name: 'Two',
+ childIds: ['2.1', '2.2'],
+ },
+ 2.1: {
+ id: '2.1',
+ name: 'Two.One',
+ },
+ 2.2: {
+ id: '2.2',
+ name: 'Two.Two',
+ },
+ },
+};
+
+describe('
|
', () => {
+ const states = {
+ expander:
+,
+ collapser:
-,
+ loader:
^,
+ };
+ const requiredProps = {
+ expandedNodeIds: List(),
+ loadingNodeIds: List(),
+ nodes: fromJS(mockData),
+ nodeId: '2',
+ parentId: '2',
+ rowRenderer: TitleComponent,
+ renderedNodeIds: List(),
+ selectedNodeIds: List(),
+ };
+ test('should load without crashing', () => {
+ const wrapper = shallow(
|
);
+ expect(wrapper.length).toBe(1);
+ });
+
+ test('should have required default props', () => {
+ const wrapper = shallow(
|
);
+ 'expander,collapser,loader,expanded,depth'.split(',').forEach((value) => {
+ expect(wrapper.instance().props[value]).toBeDefined();
+ });
+ });
+
+ test('should show expand icon if node have childs or is lazy loaded', () => {});
+
+ test('should have required methods passed as props', () => {
+ // onClick={this.handleClick}
+ // onToggle={this.handleToggle}
+ });
+
+ test('should call `toggle` function when clicked on expand icon', () => {
+ const onToggleFn = jest.fn();
+ const wrapper = mount(
|
);
+ // Row.prototype.mockToggleFn = jest.fn();
+ // const spy = jest.spyOn(Row.prototype, "mockToggleFn");
+ const ele = wrapper
+ .find(`#row-${requiredProps.nodeId}`)
+ .children('.node-header')
+ .children('.toggle-wrapper')
+ .find('a')
+ .simulate('click');
+ // expect(spy).toHaveBeenCalled();
+ expect(onToggleFn).toHaveBeenCalledWith(requiredProps.nodeId);
+ // expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+
+ test('should call `onClick` function to `delete node` with proper args when clicked', () => {
+ const onClick = jest.fn();
+ const wrapper = mount(
|
);
+ const ele = wrapper
+ .find(`#row-${requiredProps.nodeId}`)
+ .children('.node-header')
+ .children('.title-wrapper');
+ const event = ele.simulate('click');
+ logger.log('ele', ele.length);
+ // Unable to test this
+ // expect(onClick).toBeCalledWith(event,requiredProps.nodeId,requiredProps.parentId);
+ expect(onClick).toBeCalled();
+ });
+ test('should show loading indicator when node is lazily loaded', () => {});
+ test('should select the node when user clicks on any node', () => {});
+ test('should be able render the child nodes', () => {});
+});
diff --git a/__tests__/rowRenderer.test.js b/__tests__/rowRenderer.test.js
new file mode 100644
index 0000000..b098741
--- /dev/null
+++ b/__tests__/rowRenderer.test.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import RowRenderer from '../row-renderer';
+import logger from '../utils/logger';
+import toJson from 'enzyme-to-json';
+import TreeRowComonent from '../tree-row-component';
+import { shallow, mount } from 'enzyme';
+
+describe('
', () => {
+ test('should render without crashing', () => {
+ const mockData = {
+ id: 1,
+ name: 'Foo',
+ };
+ const wrapper = shallow(
);
+ expect(wrapper.length).toBe(1);
+ });
+
+ test('should allow user to rename the node', () => {
+ const mockData = {
+ id: 1,
+ name: 'Foo',
+ renameMode: true,
+ state: {
+ selected: false,
+ },
+ };
+ const wrapper = mount(
+
,
+ );
+ expect(wrapper.find('.infinite-tree-rename-input').length).toBe(0);
+ expect(wrapper.find('.infinite-tree-title').length).toBe(1);
+
+ wrapper.setProps({ node: mockData });
+ expect(wrapper.find('.infinite-tree-rename-input').length).toBe(1);
+ expect(wrapper.find('.infinite-tree-title').length).toBe(0);
+ });
+
+ test('should have default props defined', () => {
+ const wrapper = shallow(
);
+ expect(wrapper.instance().props.treeOptions).toEqual({});
+ });
+});
diff --git a/__tests__/title.test.js b/__tests__/title.test.js
new file mode 100644
index 0000000..b873f85
--- /dev/null
+++ b/__tests__/title.test.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Title from '../title';
+
+describe('
', () => {
+ test('should render without crashing', () => {
+ const mockData = {
+ name: 'Foo',
+ id: '1',
+ };
+ const wrapper = mount(
);
+ // wrapper.setProps({ node: mockData });
+ expect(wrapper.length).toBe(1);
+ });
+});
diff --git a/__tests__/treeComponent.test.js b/__tests__/treeComponent.test.js
new file mode 100644
index 0000000..26e1f02
--- /dev/null
+++ b/__tests__/treeComponent.test.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+describe('
', () => {
+ test('should pass', () => {
+ expect(1 === 1).toBe(true);
+ });
+
+ // test("should show collapse icon when node is expanded", () => {
+ // let $toggleWrapper = wrapper
+ // .find(`#row-${requiredProps.nodeId}`)
+ // .children(".toggle-wrapper");
+
+ // $toggleWrapper.find("a").simulate("click");
+ // // expect($toggleWrapper.find("a").html()).toEqual(states.collapser);
+ // });
+
+ // test("should call collapse function when clicked on collapse icon", () => {});
+ // test("should show loading indicator when node is lazily loaded", () => {});
+ // test("should select the node when user clicks on any node", () => {});
+ // test("should be able render the child nodes", () => {});
+});
diff --git a/__tests__/treeRow.test.js b/__tests__/treeRow.test.js
new file mode 100644
index 0000000..a7b3751
--- /dev/null
+++ b/__tests__/treeRow.test.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import TreeRowComponent from '../tree-row-component';
+import logger from '../utils/logger';
+
+describe('
', () => {
+ test('should load without crashing', () => {});
+
+ test('should add appropriate class whenever a node is selected', () => {
+ const mockData = { state: {} };
+ const wrapper = shallow(
);
+ let component = wrapper.find('.infinite-tree-item');
+ let classNames = component.props().className.split(' ');
+
+ expect(classNames).not.toContain('infinite-tree-selected');
+
+ wrapper.setProps({
+ node: Object.assign({}, mockData, { state: { selected: true } }),
+ });
+
+ component = wrapper.find('.infinite-tree-item');
+ classNames = component.props().className.split(' ');
+
+ expect(classNames).toContain('infinite-tree-selected');
+ });
+});
diff --git a/helpers.js b/helpers.js
index 1df581e..d0f6998 100644
--- a/helpers.js
+++ b/helpers.js
@@ -13,16 +13,66 @@ export const removeNodeWithChildren = R.curry((nodesById, id) => {
export const getNodeIdsToDelete = (byId, id) => {
if (byId[id].childIds && byId[id].childIds.length) {
- return R.reduce((currentNodes, nodeId) =>
- R.concat(currentNodes, getNodeIdsToDelete(byId, nodeId)), [id], byId[id].childIds);
+ return R.reduce(
+ (currentNodes, nodeId) =>
+ R.concat(currentNodes, getNodeIdsToDelete(byId, nodeId)),
+ [id],
+ byId[id].childIds,
+ );
}
return [id];
};
-export const deleteNodeWithChildren = (nodes, id) => {
- const nodesToDelete = getNodeIdsToDelete(nodes.byId, id);
- return R.evolve({
- byId: R.pickBy(node => !R.contains(node.id, nodesToDelete)),
- rootIds: R.filter(R.complement(R.equals(id))),
- }, nodes);
+/**
+ * Filter out garbage references from node's childIds
+ */
+const deleteReferencesFromChildIds = R.curry((parentId, nodesToDelete) =>
+ R.pipe(
+ R.tap(console.log),
+ R.over(
+ R.lensProp(parentId),
+ R.evolve({
+ childIds: R.reject(value => R.contains(value, nodesToDelete)),
+ }),
+ ),
+ ),
+);
+
+/**
+ * Deletes the node from `nodes` object
+ * @param {string} nodeId
+ * @param {array} nodesToDelete
+ * @param {object} nodes
+ */
+const deleteNodeById = (nodeId, nodesToDelete, nodes) =>
+ R.evolve(
+ {
+ byId: R.pickBy(node => !R.contains(node.id, nodesToDelete)),
+ rootIds: R.reject(R.contains(nodeId)),
+ },
+ nodes,
+ );
+
+/**
+ * Wrapper around `deleteReferencesFromChildIds` for
+ * removing the nodes references which got deleted.
+ * @param {string} parentId
+ * @param {array} nodesToDelete
+ * @param {object} nodes
+ */
+const removeIds = (parentId, nodesToDelete, nodes) =>
+ R.evolve(
+ {
+ byId: deleteReferencesFromChildIds(parentId, nodesToDelete),
+ rootIds: R.identity,
+ },
+ nodes,
+ );
+
+export const deleteNodeWithChildren = (nodes, nodeId, parentId) => {
+ const nodesToDelete = getNodeIdsToDelete(nodes.byId, nodeId);
+ return R.pipe(
+ R.partial(deleteNodeById, [nodeId, nodesToDelete]),
+ R.partial(removeIds, [parentId, nodesToDelete]),
+ )(nodes);
};
diff --git a/index.html b/index.html
index 02c72f6..b2d9bb6 100644
--- a/index.html
+++ b/index.html
@@ -1,15 +1,18 @@
+
-
+
+
Infinite Tree
+
-
+
\ No newline at end of file
diff --git a/jest.config.js b/jest.config.js
new file mode 100644
index 0000000..b97ca56
--- /dev/null
+++ b/jest.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ moduleFileExtensions: ['js'],
+ testPathIgnorePatterns: ['/node_modules/'],
+ verbose: true,
+};
diff --git a/package.json b/package.json
index 081390b..f82ddd2 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"description": "",
"main": "main.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
+ "test": "jest --config jest.config.js --coverage",
"start": "concurrently --kill-others \"webpack-dev-server\""
},
"author": "",
@@ -39,12 +39,17 @@
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"concurrently": "^3.1.0",
+ "enzyme": "^2.8.2",
+ "enzyme-to-json": "^1.5.1",
"eslint": "^3.10.2",
"eslint-config-airbnb": "^14.0.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.7.1",
+ "jest": "^20.0.4",
"less": "^2.7.1",
+ "react-addons-test-utils": "^15.5.1",
+ "sinon": "^2.3.2",
"webpack": "2.4.1",
"webpack-dev-server": "2.4.2"
}
diff --git a/row-renderer.js b/row-renderer.js
index 96f2447..b153d71 100644
--- a/row-renderer.js
+++ b/row-renderer.js
@@ -2,40 +2,42 @@ import React, { PropTypes } from 'react';
import classNames from 'classnames';
import TreeRow from './tree-row-component';
-const RowRendererComponent = (props) => {
+const RowRendererComponent = props => {
const { node, treeOptions } = props;
- const {
- id, name, type, renameMode, contextMenu = true,
- } = node;
+ const { id, name, type, renameMode, contextMenu = true } = node;
const { deleteNode } = treeOptions;
- const label = renameMode ?
- (
) :
{name};
+ type='text'
+ />
+ :
{name};
- const className = classNames('infinite-tree-closed', { 'context-menu-button': contextMenu }, 'pull-right');
+ const className = classNames(
+ 'infinite-tree-closed',
+ { 'context-menu-button': contextMenu },
+ 'pull-right'
+ );
return (
-
+
{label}
- D
- );
+ D
+
+ );
};
RowRendererComponent.propTypes = {
node: PropTypes.shape().isRequired,
- treeOptions: PropTypes.shape(),
+ treeOptions: PropTypes.shape()
};
RowRendererComponent.defaultProps = {
treeOptions: {},
+ node: {}
};
export default RowRendererComponent;
diff --git a/row.js b/row.js
index 7ff33a4..9a0f158 100644
--- a/row.js
+++ b/row.js
@@ -1,6 +1,6 @@
import React from 'react';
import Title from './title';
-import {autobind} from 'core-decorators';
+import { autobind } from 'core-decorators';
import R from 'ramda';
export default class Row extends React.Component {
@@ -10,45 +10,55 @@ export default class Row extends React.Component {
expanded: React.PropTypes.bool,
name: React.PropTypes.string,
node: React.PropTypes.object,
- rowRenderer: React.PropTypes.func,
+ rowRenderer: React.PropTypes.func
};
static defaultProps = {
- expander:
+,
- collapser:
-,
+ expander:
,
+ collapser:
,
loader:
^,
expanded: false,
- depth: 0,
- }
+ depth: 0
+ };
constructor(props) {
super(props);
}
- @autobind
- handleExpand() {
+ @autobind handleExpand() {
if (typeof this.props.children === 'function') {
- return this.props.children();
+ return this.props.children();
}
return this.setState({
- expanded: true
+ expanded: true
});
}
- @autobind
- handleToggle(e) {
+ @autobind handleToggle(e) {
this.props.onToggle(this.props.nodeId);
e.stopPropagation();
}
- @autobind
- handleClick(event) {
- this.props.onClick(event, this.props.nodeId, this.props.parentId);
- event.stopPropagation();
+ @autobind handleClick(event) {
+ this.props.onClick(event, this.props.nodeId, this.props.parentId);
+ event.stopPropagation();
}
render() {
- const {expander, collapser, nodeId, depth, nodes, selectedNodeIds, expandedNodeIds, loadingNodeIds, onToggle, onClick, loader, rowRenderer} = this.props;
+ const {
+ expander,
+ collapser,
+ nodeId,
+ depth,
+ nodes,
+ selectedNodeIds,
+ expandedNodeIds,
+ loadingNodeIds,
+ onToggle,
+ onClick,
+ loader,
+ rowRenderer
+ } = this.props;
const node = nodes.get('byId').get(nodeId);
const expanded = expandedNodeIds.indexOf(node.get('id')) >= 0;
const internalLoading = loadingNodeIds.indexOf(node.get('id')) >= 0;
@@ -56,48 +66,66 @@ export default class Row extends React.Component {
const lazyLoad = node.get('lazyLoad');
const internalOrExternalLoading = loading || internalLoading;
- const expand = !expanded && (lazyLoad || (!expanded && ((node.get('childIds')
- && !node.get('childIds').isEmpty())))) &&
+ const expand =
+ !expanded &&
+ (lazyLoad || (node.get('childIds') && !node.get('childIds').isEmpty())) &&
{expander};
- const collapse = expanded && node.get('childIds') && node.get('childIds').size &&
-
{collapser}
+ const collapse =
+ expanded &&
+ node.get('childIds') &&
+ node.get('childIds').size &&
+
{collapser};
+
const TitleRenderer = rowRenderer;
const nodeState = {
- expanded,
- selected: selectedNodeIds.contains(nodeId),
- depth,
- }
+ expanded,
+ selected: selectedNodeIds.contains(nodeId),
+ depth
+ };
const nodeWithState = {
- ...node.toJS(),
- state: nodeState
- }
+ ...node.toJS(),
+ state: nodeState
+ };
const toggler = expand || collapse;
return (
-
-
+
+
+
{!internalOrExternalLoading ? toggler : loader}
+
+
+ {expand
+ ?
+ : }
+
+
+
+
-
-
-
-
- {expanded && node.get('childIds') &&
- node.get('childIds').map((childId) => {
- const childNode = nodes.get('byId').get(childId);
- return
|
})}
+
+
+ {expanded &&
+ node.get('childIds') &&
+ node.get('childIds').map(childId => {
+ const childNode = nodes.get('byId').get(childId);
+ return (
+
+ );
+ })}
+
);
diff --git a/styles.less b/styles.less
index 7e59c8b..32ef7db 100644
--- a/styles.less
+++ b/styles.less
@@ -1,35 +1,89 @@
+@import url(https://fonts.googleapis.com/css?family=Roboto:100,200,300,400,500);
+body {
+ font-family: Roboto
+}
+
.tree-wrapper {
- display: flex;
- width: 400px;
- .rows-container {
- width: 100%;
- .row-wrapper {
- display: flex;
- flex: 1;
- flex-wrap: wrap;
- padding-left: 20px;
- .toggle-wrapper {
- min-width: 20px;
- }
- .title-wrapper {
- flex: 1;
- }
+ width: 400px;
+ .rows-container {
+ width: 100%;
+ }
+}
+
+.row-wrapper {
+ display: flex;
+ flex-direction: column;
+ .node-header {
+ display: flex;
+ .toggle-wrapper {
+ min-width: 15px;
+ }
+ .title-wrapper {
+ padding-left: 5px;
+ }
}
- .children-container {
- flex-basis: 100%;
+ .node-body {
+ margin-left: 20px;
}
- }
+}
- #app div div div div {
- margin-left: 0px;
- }
+.expand,
+.collapse {
+ position: relative;
+}
- .infinite-tree-selected {
- background-color: rgba(0, 44, 51, 0.3);
- }
+.collapse:after {
+ content: '';
+ position: absolute;
+ border-left: 8px solid transparent;
+ border-top: 8px solid black;
+ border-right: 8px solid transparent;
+}
- .infinite-tree-item {
- display: flex;
- flex: 1;
- }
-}
\ No newline at end of file
+.expand:after {
+ content: '';
+ position: absolute;
+ border-left: 8px solid black;
+ border-top: 8px solid transparent;
+ border-bottom: 8px solid transparent;
+}
+
+
+/*
+********************************************************************
+Old Styles
+********************************************************************
+*/
+
+// .tree-wrapper {
+// display: flex;
+// width: 400px;
+// .rows-container {
+// width: 100%;
+// .row-wrapper {
+// display: flex;
+// flex: 1;
+// flex-wrap: wrap;
+// padding-left: 20px;
+// .toggle-wrapper {
+// min-width: 20px;
+// }
+// .title-wrapper {
+// flex: 1;
+// }
+// }
+// .children-container {
+// flex-basis: 100%;
+// }
+// }
+// #app div div div div {
+// margin-left: 0px;
+// }
+// .infinite-tree-selected {
+// background-color: rgba(0, 44, 51, 0.3);
+// }
+// .infinite-tree-item {
+// display: flex;
+// flex: 1;
+// }
+// }
\ No newline at end of file
diff --git a/tree-component.js b/tree-component.js
index 217c295..0623a6d 100644
--- a/tree-component.js
+++ b/tree-component.js
@@ -1,8 +1,8 @@
import React from 'react';
import Row from './row';
import R from 'ramda';
-import {fromJS, List, updateAt} from 'immutable';
-import {autobind} from 'core-decorators';
+import { fromJS, List, updateAt } from 'immutable';
+import { autobind } from 'core-decorators';
import Title from './title';
export default class TreeComponent extends React.Component {
@@ -21,88 +21,126 @@ export default class TreeComponent extends React.Component {
nodes: this.getImmutableData(this.props.nodes),
renderedNodeIds: List(),
selectedNodeIds: List(),
- }
+ getSelectedNode: this.getSelectedNode
+ };
static defaultProps = {
- shouldSelectNode: R.T,
+ shouldSelectNode: null,
rowRenderer: Title
+ };
+
+ /**
+ * An public api which returns the selected node id
+ */
+ @autobind getSelectedNode() {
+ return this.state.selectedNodeIds.get(0);
}
- @autobind
- handleToggle(nodeId) {
+ @autobind handleToggle(nodeId) {
const index = this.state.expandedNodeIds.indexOf(nodeId);
if (index >= 0) {
- return this.setState({
- expandedNodeIds: this.state.expandedNodeIds.remove(index)
- });
+ return this.setState({
+ expandedNodeIds: this.state.expandedNodeIds.remove(index)
+ });
}
this.props.onExpand(nodeId);
- if(this.state.nodes.getIn(['byId', nodeId, 'lazyLoad'])) {
+ if (this.state.nodes.getIn(['byId', nodeId, 'lazyLoad'])) {
this.setState({
loadingNodeIds: this.state.loadingNodeIds.push(nodeId)
- })
+ });
}
return this.setState({
- expandedNodeIds: this.state.expandedNodeIds.push(nodeId)
+ expandedNodeIds: this.state.expandedNodeIds.push(nodeId)
});
}
- @autobind
- handleClick(event, nodeId, parentId) {
+ @autobind handleClick(event, nodeId, parentId) {
+ //Handles node deletion
this.props.onClick(event, nodeId, parentId);
- if (!this.props.multiselectMode) {
+ //Allows user to choose whether node should get selected
+ // or not
+ const shouldGetSelected = this.props.shouldSelectNode(
+ this.props.nodes.byId[nodeId]
+ );
+
+ if (!this.props.multiselectMode && shouldGetSelected) {
if (this.state.selectedNodeIds.contains(nodeId)) {
this.setState({
selectedNodeIds: List()
- })
+ });
} else {
this.setState({
selectedNodeIds: List([nodeId])
});
- this.props.onSelect(nodeId, this.state.selectedNodeIds, parentId);
}
}
}
filterCurrentNodes(newNodes, nodesToFilter) {
- return nodesToFilter.filter((nodeId) => R.contains(nodeId, R.keys(newNodes.byId)));
+ console.log(
+ 'filterCurrentNodes ',
+ newNodes.get('byId'),
+ ' ',
+ nodesToFilter
+ );
+ return nodesToFilter.filter(nodeId =>
+ R.contains(nodeId, R.keys(newNodes.get('byId').keys))
+ );
}
componentWillReceiveProps(newProps) {
- const loadingNodeIds = this.state.loadingNodeIds.filter((nodeId) => !newProps.nodes.byId[nodeId].childIds);
+ const loadingNodeIds = this.state.loadingNodeIds.filter(
+ nodeId => !newProps.nodes.byId[nodeId].childIds
+ );
const newNodes = this.getImmutableData(newProps.nodes);
this.setState({
nodes: newNodes,
- loadingNodeIds,
+ loadingNodeIds
});
this.setState({
- expandedNodeIds: this.filterCurrentNodes(newNodes, this.state.expandedNodeIds),
- loadingNodeIds: this.filterCurrentNodes(newNodes, this.state.loadingNodeIds),
- selectedNodeIds: this.filterCurrentNodes(newNodes, this.state.selectedNodeIds),
+ // expandedNodeIds: this.filterCurrentNodes(
+ // newNodes,
+ // this.state.expandedNodeIds
+ // ),
+ // loadingNodeIds: this.filterCurrentNodes(
+ // newNodes,
+ // this.state.loadingNodeIds
+ // ),
+ selectedNodeIds: this.filterCurrentNodes(
+ newNodes,
+ this.state.selectedNodeIds
+ )
});
}
render() {
- const {selectedNodeIds, expandedNodeIds, loadingNodeIds, nodes} = this.state;
- const {rowRenderer} = this.props;
+ const {
+ selectedNodeIds,
+ expandedNodeIds,
+ loadingNodeIds,
+ nodes
+ } = this.state;
+ const { rowRenderer } = this.props;
const rootNodeIds = nodes.get('rootIds');
//TODO: can try to optimise to send only children to rows
return (
-
- {rootNodeIds.map((nodeId) => {
- const node = nodes.getIn(['byId', nodeId]);
- return
|
})
- }
+
+ {rootNodeIds.map(nodeId => {
+ const node = nodes.getIn(['byId', nodeId]);
+ return (
+
+ );
+ })}
);
}
diff --git a/utils/logger.js b/utils/logger.js
new file mode 100644
index 0000000..d4bd78a
--- /dev/null
+++ b/utils/logger.js
@@ -0,0 +1,5 @@
+export default {
+ log: (message, value) => {
+ console.log(`***************${message}***************`, value);
+ },
+};