Skip to content

Commit b93af85

Browse files
committed
Support the render prop given as children too
1 parent 6adfedf commit b93af85

File tree

4 files changed

+84
-53
lines changed

4 files changed

+84
-53
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ The most important thing you have to tell to `Treefold` besides the actual tree
145145
/>
146146
```
147147

148+
> You can also pass it as the `children` prop if you prefer to do things that way `<Treefold>{/* right here*/}</Treefold>`
149+
148150
`Treefold` takes care of calling this function as it traverses the tree, and passes to it an object (`props` in the example just above) with the properties documented below:
149151

150152
### node

src/Treefold.js

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import ToggleController from './ToggleController';
77
class Treefold extends Component {
88
static propTypes = {
99
nodes: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
10-
render: PropTypes.func.isRequired,
10+
render: PropTypes.func,
11+
children: PropTypes.func,
1112
isNodeExpanded: PropTypes.func,
1213
onToggleExpand: PropTypes.func,
1314
getNodeId: PropTypes.func,
@@ -29,18 +30,21 @@ class Treefold extends Component {
2930
}
3031

3132
internalRender = ({ isOn, onToggle }) => {
32-
const { nodes, render, getNodeId, getNodeChildren } = this.props;
33-
return nodes.map(node =>
34-
React.createElement(Node, {
35-
key: getNodeId(node),
36-
node,
37-
render,
38-
getNodeId,
39-
getNodeChildren,
40-
isNodeExpanded: isOn,
41-
onToggleExpand: onToggle,
42-
})
43-
);
33+
const { nodes, getNodeId, getNodeChildren } = this.props;
34+
const render = getRenderProp(this.props);
35+
return render
36+
? nodes.map(node =>
37+
React.createElement(Node, {
38+
key: getNodeId(node),
39+
node,
40+
render,
41+
getNodeId,
42+
getNodeChildren,
43+
isNodeExpanded: isOn,
44+
onToggleExpand: onToggle,
45+
})
46+
)
47+
: null;
4448
};
4549

4650
render() {
@@ -58,11 +62,25 @@ class Treefold extends Component {
5862
}
5963
}
6064

61-
function checkProps({ isNodeExpanded, onToggleExpand }) {
65+
function getRenderProp({ render, children }) {
66+
return typeof render === 'function'
67+
? render
68+
: typeof children === 'function' ? children : null;
69+
}
70+
71+
function checkProps({ isNodeExpanded, onToggleExpand, render, children }) {
6272
warning(
6373
typeof isNodeExpanded === typeof onToggleExpand,
6474
'Treefold: You must pass both isNodeExpanded and onToggleExpand, or none.'
6575
);
76+
warning(
77+
!(typeof render === 'function' && typeof children === 'function'),
78+
'You should not use <Treefold render /> and <Treefold>{children}</Treefold> in the same Treefold component; `children` will be ignored'
79+
);
80+
warning(
81+
!(typeof render !== 'function' && typeof children !== 'function'),
82+
'You should specify one of <Treefold render /> or <Treefold>{children}</Treefold>; your component will render nothing unless you do so'
83+
);
6684
}
6785

6886
export default Treefold;

storybook/UnorderedList.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import './styles.css';
44

55
const UnorderedList = props => (
66
<ul className="unordered-list">
7-
<Treefold
8-
{...props}
9-
render={({
7+
<Treefold {...props}>
8+
{({
109
node,
1110
isFolder,
1211
isExpanded,
@@ -33,7 +32,7 @@ const UnorderedList = props => (
3332
)}
3433
</li>
3534
)}
36-
/>
35+
</Treefold>
3736
</ul>
3837
);
3938

tests/Treefold.test.js

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,46 +51,43 @@ const buildNodes = ({ idKey = 'id', childrenKey = 'children' } = {}) => [
5151

5252
const defaultNodes = buildNodes();
5353

54-
const Tree = props => (
55-
<Treefold
56-
{...props}
57-
render={({
58-
node,
59-
isFolder,
60-
isExpanded,
61-
getToggleProps,
62-
hasChildNodes,
63-
renderChildNodes,
64-
}) => (
65-
<li>
66-
{isFolder ? (
67-
<a
68-
id={node.id}
69-
href="#"
70-
{...getToggleProps()}
71-
className={
72-
isExpanded ? 'item folder expanded' : 'item folder collapsed'
73-
}
74-
>
75-
{node.name}
76-
</a>
54+
const Node = ({
55+
node,
56+
isFolder,
57+
isExpanded,
58+
getToggleProps,
59+
hasChildNodes,
60+
renderChildNodes,
61+
}) => (
62+
<li>
63+
{isFolder ? (
64+
<a
65+
id={node.id}
66+
href="#"
67+
{...getToggleProps()}
68+
className={
69+
isExpanded ? 'item folder expanded' : 'item folder collapsed'
70+
}
71+
>
72+
{node.name}
73+
</a>
74+
) : (
75+
<span className="item leaf">{node.name}</span>
76+
)}
77+
{isExpanded && (
78+
<ul>
79+
{hasChildNodes ? (
80+
renderChildNodes()
7781
) : (
78-
<span className="item leaf">{node.name}</span>
79-
)}
80-
{isExpanded && (
81-
<ul>
82-
{hasChildNodes ? (
83-
renderChildNodes()
84-
) : (
85-
<li className="empty">Empty node</li>
86-
)}
87-
</ul>
82+
<li className="empty">Empty node</li>
8883
)}
89-
</li>
84+
</ul>
9085
)}
91-
/>
86+
</li>
9287
);
9388

89+
const Tree = props => <Treefold {...props} render={Node} />;
90+
9491
const setup = ({ expanded = [], nodes = defaultNodes, ...props } = {}) => {
9592
const isNodeExpanded = item =>
9693
expanded.includes(props.getNodeId ? props.getNodeId(item) : item.id);
@@ -174,4 +171,19 @@ describe('Treefold', () => {
174171
expect(wrapper.find('.item')).toHaveLength(3);
175172
});
176173
});
174+
175+
describe('render prop', () => {
176+
it('can be given via the children prop', () => {
177+
const wrapper = mount(
178+
<Treefold
179+
nodes={defaultNodes}
180+
onToggleExpand={() => {}}
181+
isNodeExpanded={() => true}
182+
>
183+
{Node}
184+
</Treefold>
185+
);
186+
expect(wrapper.find('.item')).toHaveLength(12);
187+
});
188+
});
177189
});

0 commit comments

Comments
 (0)