Skip to content
This repository was archived by the owner on Jan 16, 2023. It is now read-only.

Commit 63feb8a

Browse files
feat(makeKeyProp): allow custom function for React key prop generation
1 parent 36d72f1 commit 63feb8a

File tree

10 files changed

+95
-25
lines changed

10 files changed

+95
-25
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,17 @@ const decorators = {
179179
<Treebeard data={...} decorators={decorators}/>
180180
```
181181

182+
#### makeKeyProp
183+
Optional prop to generate your own [React key props](https://reactjs.org/docs/lists-and-keys.html). Function that takes the node as the only param. Example:
184+
```jsx
185+
<Treebeard
186+
data={data}
187+
onToggle={this.onToggle}
188+
onSelect={this.onSelect}
189+
makeKeyProp={(node) => node.yourUniqueId}
190+
/>
191+
```
192+
182193
### Data Attributes
183194

184195
```javascript

__tests__/Treebeard.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,9 @@ describe('<Treebeard/>', () => {
1717
const wrapper = renderComponent();
1818
expect(wrapper).toMatchSnapshot();
1919
});
20+
it('should handle custom makeKeyProp', () => {
21+
const wrapper = renderComponent({ makeKeyProp: (node) => node.sha });
22+
const firstNodeKey = wrapper.find('TreeNode').first().key();
23+
expect(firstNodeKey).toBe('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
24+
});
2025
});

__tests__/__snapshots__/Treebeard.test.js.snap

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,76 +46,95 @@ exports[`<Treebeard/> should match default snapshot 1`] = `
4646
}
4747
}
4848
key="1"
49+
makeKeyProp={[Function]}
4950
node={
5051
Object {
5152
"children": Array [
5253
Object {
5354
"children": Array [
5455
Object {
5556
"name": "app.js",
57+
"sha": "828270ffd0e8ee6f32eb2a83bc3da31f3a9ac73f4833de481f65337c0802a9aa",
5658
},
5759
Object {
5860
"name": "data.js",
61+
"sha": "3ae815b93875786c05cba19df444662a789640e83ab65f4b84e9c16474cb1ed0",
5962
},
6063
Object {
6164
"name": "index.html",
65+
"sha": "f82ec4ae606600a57396ca47e21e0f013bded8523dfaffee040eefa4ea815962",
6266
},
6367
Object {
6468
"name": "styles.js",
69+
"sha": "3ba988e33afe3c6e453fc86bf26813ad90e5fe2dd7f55527a8c1408f861bfeac",
6570
},
6671
Object {
6772
"name": "webpack.config.js",
73+
"sha": "017cbc93d594c0f410b0c4c86c6b160a421674ddf72ab10201a16646f02fcb9c",
6874
},
6975
],
7076
"name": "example",
77+
"sha": "f05bcd2dffa3e047ce58c0f1a1877dcbe4d868cf0eac58bc2f7f9ab805289b0b",
7178
},
7279
Object {
7380
"children": Array [],
7481
"loading": true,
7582
"name": "node_modules",
83+
"sha": "bd9d2e8d246bc323081327ef12d3d40e0fc09cc7d36ea05bc286ea74e47c3515",
7684
},
7785
Object {
7886
"children": Array [
7987
Object {
8088
"children": Array [
8189
Object {
8290
"name": "decorators.js",
91+
"sha": "cc37c52929a8f16cfbf7b1881119f963e5a332507fac7d7f4fa9a271ad58534e",
8392
},
8493
Object {
8594
"name": "treebeard.js",
95+
"sha": "177c1b4a2b85d1c2a15ed481dbcefd9a43a901d6efa05c41dbb7e1859d5547e2",
8696
},
8797
],
8898
"name": "components",
8999
},
90100
Object {
91101
"name": "index.js",
102+
"sha": "cc02786ca507e473781c0c08a2738e921eaa49c061515598d0ef1dff3054ea7b",
92103
},
93104
],
94105
"name": "src",
106+
"sha": "da5bd1bf868d4b2a68ce84c5e13545523ddc58232fe59e74a3d8ccb9be9e6db6",
95107
},
96108
Object {
97109
"children": Array [
98110
Object {
99111
"name": "animations.js",
112+
"sha": "1fde2cd5a18a6bd17660167f0fe5296a2ea1275da38961632b5630abf9c7a85d",
100113
},
101114
Object {
102115
"name": "default.js",
116+
"sha": "a59dc347bb2eb8f5a51f3b7f65dd63580829bcdfee89d5331f9ff063438b6dbd",
103117
},
104118
],
105119
"name": "themes",
120+
"sha": "8c382185c33911dbc2e2a76094b73a02644635ed9de1e9995a8b06f96bdbae32",
106121
},
107122
Object {
108123
"name": "gulpfile.js",
124+
"sha": "f68fe2b19685fb03204cfb3a0c7e58c7698fa9abc595185e39028fbc6e0531e1",
109125
},
110126
Object {
111127
"name": "index.js",
128+
"sha": "08f982599837827c163a787c6ae0a5cf1b493829493c1f29d707a5026b28ed0e",
112129
},
113130
Object {
114131
"name": "package.json",
132+
"sha": "c31e4a244c319e43a03cd3fbfabd0c6d976567c5b79e8b06a218b2aae3637839",
115133
},
116134
],
117135
"id": 1,
118136
"name": "react-treebeard",
137+
"sha": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
119138
"toggled": true,
120139
}
121140
}

__tests__/mocks/data.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,55 @@ export default {
22
name: 'react-treebeard',
33
id: 1,
44
toggled: true,
5+
sha: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
56
children: [
67
{
78
name: 'example',
9+
sha: 'f05bcd2dffa3e047ce58c0f1a1877dcbe4d868cf0eac58bc2f7f9ab805289b0b',
810
children: [
9-
{ name: 'app.js' },
10-
{ name: 'data.js' },
11-
{ name: 'index.html' },
12-
{ name: 'styles.js' },
13-
{ name: 'webpack.config.js' }
11+
{ name: 'app.js', sha: '828270ffd0e8ee6f32eb2a83bc3da31f3a9ac73f4833de481f65337c0802a9aa' },
12+
{ name: 'data.js', sha: '3ae815b93875786c05cba19df444662a789640e83ab65f4b84e9c16474cb1ed0' },
13+
{ name: 'index.html', sha: 'f82ec4ae606600a57396ca47e21e0f013bded8523dfaffee040eefa4ea815962' },
14+
{ name: 'styles.js', sha: '3ba988e33afe3c6e453fc86bf26813ad90e5fe2dd7f55527a8c1408f861bfeac' },
15+
{ name: 'webpack.config.js', sha: '017cbc93d594c0f410b0c4c86c6b160a421674ddf72ab10201a16646f02fcb9c' }
1416
]
1517
},
1618
{
1719
name: 'node_modules',
1820
loading: true,
21+
sha: 'bd9d2e8d246bc323081327ef12d3d40e0fc09cc7d36ea05bc286ea74e47c3515',
1922
children: []
2023
},
2124
{
2225
name: 'src',
26+
sha: 'da5bd1bf868d4b2a68ce84c5e13545523ddc58232fe59e74a3d8ccb9be9e6db6',
2327
children: [
2428
{
2529
name: 'components',
2630
children: [
27-
{ name: 'decorators.js' },
28-
{ name: 'treebeard.js' }
31+
{
32+
name: 'decorators.js',
33+
sha: 'cc37c52929a8f16cfbf7b1881119f963e5a332507fac7d7f4fa9a271ad58534e'
34+
},
35+
{
36+
name: 'treebeard.js',
37+
sha: '177c1b4a2b85d1c2a15ed481dbcefd9a43a901d6efa05c41dbb7e1859d5547e2'
38+
}
2939
]
3040
},
31-
{ name: 'index.js' }
41+
{ name: 'index.js', sha: 'cc02786ca507e473781c0c08a2738e921eaa49c061515598d0ef1dff3054ea7b' }
3242
]
3343
},
3444
{
3545
name: 'themes',
46+
sha: '8c382185c33911dbc2e2a76094b73a02644635ed9de1e9995a8b06f96bdbae32',
3647
children: [
37-
{ name: 'animations.js' },
38-
{ name: 'default.js' }
48+
{ name: 'animations.js', sha: '1fde2cd5a18a6bd17660167f0fe5296a2ea1275da38961632b5630abf9c7a85d' },
49+
{ name: 'default.js', sha: 'a59dc347bb2eb8f5a51f3b7f65dd63580829bcdfee89d5331f9ff063438b6dbd' }
3950
]
4051
},
41-
{ name: 'gulpfile.js' },
42-
{ name: 'index.js' },
43-
{ name: 'package.json' }
52+
{ name: 'gulpfile.js', sha: 'f68fe2b19685fb03204cfb3a0c7e58c7698fa9abc595185e39028fbc6e0531e1' },
53+
{ name: 'index.js', sha: '08f982599837827c163a787c6ae0a5cf1b493829493c1f29d707a5026b28ed0e' },
54+
{ name: 'package.json', sha: 'c31e4a244c319e43a03cd3fbfabd0c6d976567c5b79e8b06a218b2aae3637839' }
4455
]
4556
};

__tests__/utils.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {makeKeyProp} from '../src/util';
2+
3+
describe('makeKeyProp', () => {
4+
it('should return id first', () => {
5+
expect(makeKeyProp({id: 'uniqueId'})).toBe('uniqueId');
6+
});
7+
it('should return random string if no id is present', () => {
8+
expect(typeof makeKeyProp({})).toBe('string');
9+
});
10+
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-treebeard",
3-
"version": "3.2.4",
3+
"version": "3.2.5",
44
"description": "React Tree View Component",
55
"main": "index.js",
66
"scripts": {

src/components/TreeNode/index.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import styled from '@emotion/styled';
44
import {isArray, isFunction} from 'lodash';
55

66
import defaultAnimations from '../../themes/animations';
7-
import {randomString} from '../../util';
7+
import {makeKeyProp} from '../../util';
88
import {Ul} from '../common';
99
import NodeHeader from '../NodeHeader';
1010
import Drawer from './Drawer';
@@ -45,7 +45,7 @@ class TreeNode extends PureComponent {
4545

4646
renderChildren(decorators) {
4747
const {
48-
animations, decorators: propDecorators, node, style, onToggle, onSelect, customStyles
48+
animations, decorators: propDecorators, node, style, onToggle, onSelect, customStyles, makeKeyProp
4949
} = this.props;
5050

5151
if (node.loading) {
@@ -69,7 +69,7 @@ class TreeNode extends PureComponent {
6969
style={style}
7070
customStyles={customStyles}
7171
decorators={propDecorators}
72-
key={child.id || randomString()}
72+
key={makeKeyProp(child)}
7373
node={child}
7474
/>
7575
))}
@@ -113,11 +113,13 @@ TreeNode.propTypes = {
113113
animations: PropTypes.oneOfType([
114114
PropTypes.object,
115115
PropTypes.bool
116-
]).isRequired
116+
]).isRequired,
117+
makeKeyProp: PropTypes.func
117118
};
118119

119120
TreeNode.defaultProps = {
120-
customStyles: {}
121+
customStyles: {},
122+
makeKeyProp
121123
};
122124

123125
export default TreeNode;

src/components/index.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import {castArray} from 'lodash';
44

55
import defaultTheme from '../themes/default';
66
import defaultAnimations from '../themes/animations';
7-
import {randomString} from '../util';
7+
import {makeKeyProp} from '../util';
88
import {Ul} from './common';
99
import defaultDecorators from './Decorators';
1010
import TreeNode from './TreeNode';
1111

1212
const TreeBeard = ({
13-
animations, decorators, data, onToggle, style, onSelect, customStyles
13+
animations, decorators, data, onToggle, style, onSelect, customStyles, makeKeyProp
1414
}) => (
1515
<Ul style={{...defaultTheme.tree.base, ...style.tree.base}}>
1616
{castArray(data).map(node => (
@@ -21,7 +21,8 @@ const TreeBeard = ({
2121
animations={animations}
2222
onSelect={onSelect}
2323
customStyles={customStyles}
24-
key={node.id || randomString()}
24+
makeKeyProp={makeKeyProp}
25+
key={makeKeyProp(node)}
2526
style={{...defaultTheme.tree.node, ...style.tree.node}}
2627
/>
2728
))}
@@ -41,14 +42,16 @@ TreeBeard.propTypes = {
4142
]),
4243
onToggle: PropTypes.func,
4344
onSelect: PropTypes.func,
44-
decorators: PropTypes.object
45+
decorators: PropTypes.object,
46+
makeKeyProp: PropTypes.func
4547
};
4648

4749
TreeBeard.defaultProps = {
4850
style: defaultTheme,
4951
animations: defaultAnimations,
5052
decorators: defaultDecorators,
51-
customStyles: {}
53+
customStyles: {},
54+
makeKeyProp
5255
};
5356

5457
export default TreeBeard;

src/util/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import makeKeyProp from './makeKeyProp';
12
import randomString from './randomString';
23

3-
export {randomString};
4+
export {
5+
makeKeyProp,
6+
randomString
7+
};

src/util/makeKeyProp.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import randomString from './randomString';
2+
3+
const makeKeyProp = (node) => node.id || randomString();
4+
5+
export default makeKeyProp;

0 commit comments

Comments
 (0)