From b2049e3ccb54335771c44f18373c8cbb93f92cff Mon Sep 17 00:00:00 2001 From: Hedger Wang Date: Wed, 23 Sep 2015 17:01:08 -0700 Subject: [PATCH] Hierarchical event bubbling - 1 Reviewed By: @fkgozali Differential Revision: D2469495 --- .../Navigation/NavigationTreeNode.js | 91 ++++++++++++++ .../__tests__/NavigationTreeNode-test.js | 111 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js create mode 100644 Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js diff --git a/Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js b/Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js new file mode 100644 index 000000000..b100ab5e4 --- /dev/null +++ b/Libraries/CustomComponents/Navigator/Navigation/NavigationTreeNode.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2015, Facebook, Inc. All rights reserved. + * + * @providesModule NavigationTreeNode + * @flow + * @typechecks + */ + +'use strict'; + +var invariant = require('invariant'); +var immutable = require('immutable'); + +var {List} = immutable; + +/** + * Utility to build a tree of nodes. + * Note that this tree does not perform cyclic redundancy check + * while appending child node. + */ +class NavigationTreeNode { + __parent: ?NavigationTreeNode; + + _children: List; + + _value: any; + + constructor(value: any) { + this.__parent = null; + this._children = new List(); + this._value = value; + } + + getValue(): any { + return this._value; + } + + getParent(): ?NavigationTreeNode { + return this.__parent; + } + + getChildrenCount(): number { + return this._children.size; + } + + getChildAt(index: number): ?NavigationTreeNode { + return index > -1 && index < this._children.size ? + this._children.get(index) : + null; + } + + appendChild(child: NavigationTreeNode): void { + if (child.__parent) { + child.__parent.removeChild(child); + } + child.__parent = this; + this._children = this._children.push(child); + } + + removeChild(child: NavigationTreeNode): void { + var index = this._children.indexOf(child); + + invariant( + index > -1, + 'The node to be removed is not a child of this node.' + ); + + child.__parent = null; + + this._children = this._children.splice(index, 1); + } + + indexOf(child: NavigationTreeNode): number { + return this._children.indexOf(child); + } + + forEach(callback: Function, context: any): void { + this._children.forEach(callback, context); + } + + map(callback: Function, context: any): Array { + return this._children.map(callback, context).toJS(); + } + + some(callback: Function, context: any): boolean { + return this._children.some(callback, context); + } +} + + +module.exports = NavigationTreeNode; diff --git a/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js new file mode 100644 index 000000000..e89da6a80 --- /dev/null +++ b/Libraries/CustomComponents/Navigator/Navigation/__tests__/NavigationTreeNode-test.js @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2015, Facebook, Inc. All rights reserved. + */ + +'use strict'; + +jest + .dontMock('Map') + .dontMock('NavigationTreeNode') + .dontMock('invariant') + .dontMock('immutable'); + +var NavigationTreeNode = require('NavigationTreeNode'); + +describe('NavigationTreeNode-test', () => { + it('should be empty', () => { + var node = new NavigationTreeNode(); + expect(node.getValue()).toEqual(undefined); + expect(node.getParent()).toEqual(null); + expect(node.getChildrenCount()).toEqual(0); + expect(node.getChildAt(0)).toEqual(null); + }); + + + it('should contain value', () => { + var node = new NavigationTreeNode(123); + expect(node.getValue()).toEqual(123); + }); + + it('should appendChild', () => { + var papa = new NavigationTreeNode('hedger'); + var baby = new NavigationTreeNode('hedger jr'); + papa.appendChild(baby); + expect(papa.getChildAt(0)).toEqual(baby); + expect(papa.getChildrenCount()).toEqual(1); + expect(baby.getParent()).toEqual(papa); + }); + + it('should removeChild', () => { + var papa = new NavigationTreeNode('Eddard Stark'); + var baby = new NavigationTreeNode('Robb Stark'); + papa.appendChild(baby); + + papa.removeChild(baby); + expect(papa.getChildAt(0)).toEqual(null); + expect(papa.getChildrenCount()).toEqual(0); + expect(baby.getParent()).toEqual(null); + }); + + it('should not remove non-child', () => { + var papa = new NavigationTreeNode('dog'); + var baby = new NavigationTreeNode('cat'); + expect(papa.removeChild.bind(papa, baby)).toThrow(); + }); + + it('should find child', () => { + var papa = new NavigationTreeNode('Eddard Stark'); + var baby = new NavigationTreeNode('Robb Stark'); + + papa.appendChild(baby); + expect(papa.indexOf(baby)).toEqual(0); + + papa.removeChild(baby); + expect(papa.indexOf(baby)).toEqual(-1); + }); + + + it('should traverse each child', () => { + var parent = new NavigationTreeNode(); + parent.appendChild(new NavigationTreeNode('a')); + parent.appendChild(new NavigationTreeNode('b')); + parent.appendChild(new NavigationTreeNode('c')); + var result = []; + parent.forEach((child, index) => { + result[index] = child.getValue(); + }); + + expect(result).toEqual(['a', 'b', 'c']); + }); + + it('should map children', () => { + var parent = new NavigationTreeNode(); + parent.appendChild(new NavigationTreeNode('a')); + parent.appendChild(new NavigationTreeNode('b')); + parent.appendChild(new NavigationTreeNode('c')); + var result = parent.map((child, index) => { + return child.getValue(); + }); + + expect(result).toEqual(['a', 'b', 'c']); + }); + + it('should traverse some children', () => { + var parent = new NavigationTreeNode(); + parent.appendChild(new NavigationTreeNode('a')); + parent.appendChild(new NavigationTreeNode('b')); + parent.appendChild(new NavigationTreeNode('c')); + + var result = []; + var value = parent.some((child, index) => { + if (index > 1) { + return true; + } else { + result[index] = child.getValue(); + } + }); + + expect(value).toEqual(true); + expect(result).toEqual(['a', 'b']); + }); +});