@samitouri / QOS-React-2 / commits / 259392035d
Fork 0

The library for web and native user interfaces.

mapChildren

mapChilden() is similar to Array.map() and objMap() but handles deep nested structures and follows similar rules to flattenChildren()

CommitSyncScript committed Jun 4, 2013 at 12:15 UTC 259392035daac9cf8d45d909a175e640c496f547
2 files changed +152
src/utils/__tests__/mapChildren-test.js new
+117
@@ -0,0 +1,117 @@
1 +/**
2 + * @emails react-core
3 + * @jsx React.DOM
4 + */
5 +
6 +"use strict";
7 +
8 +describe('mapChildren', function() {
9 +
10 + var React;
11 + var ReactTestUtils;
12 +
13 + var mapChildren;
14 + var reactComponentExpect;
15 +
16 + var Wrap;
17 +
18 + beforeEach(function() {
19 + React = require('React');
20 + ReactTestUtils = require('ReactTestUtils');
21 +
22 + mapChildren = require('mapChildren');
23 + reactComponentExpect = require('reactComponentExpect');
24 +
25 + Wrap = React.createClass({
26 + render: function() {
27 + return (
28 + <div>
29 + {mapChildren(this.props.children, this.props.mapFn, this)}
30 + </div>
31 + );
32 + }
33 + });
34 +
35 + this.addMatchers({
36 + toHaveKeys: function(expected) {
37 + if (this.actual.length != expected.length) {
38 + return false;
39 + }
40 + return this.actual.every(function(component, index) {
41 + return component._key === expected[index];
42 + }, this);
43 + }
44 + });
45 + });
46 +
47 +
48 + it('should support identity for simple', function() {
49 + var mapFn = jasmine.createSpy().andCallFake(function (kid, key, index) {
50 + return kid;
51 + });
52 +
53 + var simpleKid = <span key="simple" />;
54 +
55 + var instance = <Wrap mapFn={mapFn}>{simpleKid}</Wrap>;
56 + ReactTestUtils.renderIntoDocument(instance);
57 +
58 + var rendered = reactComponentExpect(instance)
59 + .expectRenderedChild()
60 + .instance();
61 +
62 + expect(mapFn).toHaveBeenCalledWith(simpleKid, 'simple', 0);
63 + expect(rendered.props.children[0]).toBe(simpleKid);
64 + expect(rendered.props.children).toHaveKeys(['simple']);
65 + });
66 +
67 + it('should pass key to returned component', function() {
68 + var mapFn = function (kid, key, index) {
69 + return <div>{kid}</div>;
70 + };
71 +
72 + var simpleKid = <span key="simple" />;
73 +
74 + var instance = <Wrap mapFn={mapFn}>{simpleKid}</Wrap>;
75 + ReactTestUtils.renderIntoDocument(instance);
76 +
77 + var rendered = reactComponentExpect(instance)
78 + .expectRenderedChild()
79 + .instance();
80 +
81 + expect(rendered.props.children[0]).not.toBe(simpleKid);
82 + expect(rendered.props.children[0].props.children[0]).toBe(simpleKid);
83 + expect(rendered.props.children).toHaveKeys(['simple']);
84 + expect(rendered.props.children[0].props.children).toHaveKeys(['simple']);
85 + });
86 +
87 + it('should be called for each child', function() {
88 + var mapFn = jasmine.createSpy().andCallFake(function (kid, key, index) {
89 + return <div>{kid}</div>;
90 + });
91 +
92 + var kidOne = <div key="one" />;
93 + var kidTwo = <div key="two" />;
94 + var kidThree = <div key="three" />;
95 +
96 + var instance = ReactTestUtils.renderIntoDocument(
97 + <Wrap mapFn={mapFn}>
98 + {kidOne}
99 + {null}
100 + {kidTwo}
101 + {null}
102 + {kidThree}
103 + </Wrap>
104 + );
105 +
106 + var rendered = reactComponentExpect(instance)
107 + .expectRenderedChild()
108 + .instance();
109 +
110 + expect(mapFn.calls.length).toBe(3);
111 + expect(mapFn).toHaveBeenCalledWith(kidOne, 'one', 0);
112 + expect(mapFn).toHaveBeenCalledWith(kidTwo, 'two', 1);
113 + expect(mapFn).toHaveBeenCalledWith(kidThree, 'three', 2);
114 + expect(rendered.props.children).not.toEqual(instance.props.children);
115 + expect(rendered.props.children).toHaveKeys(['one', 'two', 'three']);
116 + });
117 +});
src/utils/mapChildren.js new
+35
@@ -0,0 +1,35 @@
1 +/**
2 + * @providesModule mapChildren
3 + */
4 +
5 +"use strict";
6 +
7 +/**
8 + * Maps children that are typically specified as `props.children`.
9 + *
10 + * The provided mapFunction(child, key, index) will be called for each
11 + * leaf child.
12 + *
13 + * Note: mapChildren assumes children have already been flattened.
14 + *
15 + * @param {array} children
16 + * @param {function(*, string, int)} mapFunction
17 + * @param {*} context
18 + * @return {array} mirrored array with mapped children
19 + */
20 +function mapChildren(children, mapFunction, context) {
21 + if (children == null) {
22 + return children;
23 + }
24 + var mappedChildren = [];
25 + for (var ii = 0; ii < children.length; ii++) {
26 + var child = children[ii];
27 + var key = child._key;
28 + var mappedChild = mapFunction.call(context, child, key, ii);
29 + mappedChild._key = key;
30 + mappedChildren.push(mappedChild);
31 + }
32 + return mappedChildren;
33 +}
34 +
35 +module.exports = mapChildren;