Skip to content

Commit a3356e5

Browse files
committed
Add support for custom node icons
Resolves #26.
1 parent 09c4e7d commit a3356e5

File tree

10 files changed

+137
-9
lines changed

10 files changed

+137
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### New Features
1010

1111
* [#2]: Allow customization of icons via CSS
12+
* [#26]: Allow icon customization at node level
1213

1314
## [v0.4.2](https://github.com/jakezatecky/react-checkbox-tree/compare/v0.4.1...v0.4.2) (2016-02-27)
1415

examples/src/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ <h1>Examples</h1>
2525
<h2>Basic Example</h2>
2626
<div id="basic-example"></div>
2727

28+
<h2>Custom Icons Example</h2>
29+
<div id="custom-icons-example"></div>
30+
2831
<h2>Pessimistic Toggle Example</h2>
2932
<p>Try clicking a partially-checked node. Instead of select all children, the pessimistic model will uncheck them.</p>
3033
<div id="pessimistic-toggle-example"></div>

examples/src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33

44
import BasicExample from './js/BasicExample';
5+
import CustomIconsExample from './js/CustomIconsExample';
56
import PessimisticToggleExample from './js/PessimisticToggleExample';
67

78
ReactDOM.render(<BasicExample />, document.getElementById('basic-example'));
9+
ReactDOM.render(<CustomIconsExample />, document.getElementById('custom-icons-example'));
810
ReactDOM.render(<PessimisticToggleExample />, document.getElementById('pessimistic-toggle-example'));
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import React from 'react';
2+
3+
import CheckboxTree from '../../../src/index';
4+
5+
const nodes = [
6+
{
7+
value: 'Documents',
8+
label: 'Documents',
9+
children: [
10+
{
11+
value: 'Employee Evaluations.zip',
12+
label: 'Employee Evaluations.zip',
13+
icon: <i className="fa fa-file-archive-o" />,
14+
},
15+
{
16+
value: 'Expense Report.pdf',
17+
label: 'Expense Report.pdf',
18+
icon: <i className="fa fa-file-pdf-o" />,
19+
},
20+
{
21+
value: 'notes.txt',
22+
label: 'notes.txt',
23+
icon: <i className="fa fa-file-text-o" />,
24+
},
25+
],
26+
},
27+
{
28+
value: 'Photos',
29+
label: 'Photos',
30+
children: [
31+
{
32+
value: 'nyan-cat.gif',
33+
label: 'nyan-cat.gif',
34+
icon: <i className="fa fa-file-image-o" />,
35+
},
36+
{
37+
value: 'SpaceX Falcon9 liftoff.jpg',
38+
label: 'SpaceX Falcon9 liftoff.jpg',
39+
icon: <i className="fa fa-file-image-o" />,
40+
},
41+
],
42+
},
43+
];
44+
45+
class CustomIconsExamples extends React.Component {
46+
constructor() {
47+
super();
48+
49+
this.state = {
50+
checked: [],
51+
expanded: [
52+
'Documents',
53+
],
54+
};
55+
56+
this.onCheck = this.onCheck.bind(this);
57+
this.onExpand = this.onExpand.bind(this);
58+
}
59+
60+
onCheck(checked) {
61+
this.setState({ checked });
62+
}
63+
64+
onExpand(expanded) {
65+
this.setState({ expanded });
66+
}
67+
68+
render() {
69+
const { checked, expanded } = this.state;
70+
71+
return (
72+
<CheckboxTree
73+
checked={checked}
74+
expanded={expanded}
75+
nodes={nodes}
76+
onCheck={this.onCheck}
77+
onExpand={this.onExpand}
78+
/>
79+
);
80+
}
81+
}
82+
83+
export default CustomIconsExamples;

src/js/CheckboxTree.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class CheckboxTree extends React.Component {
141141
key={key}
142142
checked={checked}
143143
expanded={node.expanded}
144+
icon={node.icon}
144145
label={node.label}
145146
optimisticToggle={this.props.optimisticToggle}
146147
rawChildren={node.children}

src/js/TreeNode.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ class TreeNode extends React.Component {
1414
onExpand: React.PropTypes.func.isRequired,
1515

1616
children: React.PropTypes.node,
17+
icon: React.PropTypes.node,
1718
rawChildren: React.PropTypes.arrayOf(nodeShape),
1819
};
1920

2021
static defaultProps = {
2122
children: null,
23+
icon: null,
2224
rawChildren: undefined,
2325
};
2426

@@ -93,6 +95,10 @@ class TreeNode extends React.Component {
9395
}
9496

9597
renderNodeIcon() {
98+
if (this.props.icon !== null) {
99+
return this.props.icon;
100+
}
101+
96102
if (this.props.rawChildren === null) {
97103
return <i className="rct-icon rct-icon-leaf" />;
98104
}

src/js/nodeShape.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React from 'react';
22

33
const nodeShape = {
4+
label: React.PropTypes.string.isRequired,
45
value: React.PropTypes.oneOfType([
56
React.PropTypes.string,
67
React.PropTypes.number,
78
]).isRequired,
8-
label: React.PropTypes.string.isRequired,
9+
10+
icon: React.PropTypes.node,
911
};
1012

1113
const nodeShapeWithChildren = React.PropTypes.oneOfType([

src/less/react-checkbox-tree.less

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,23 @@
3737
}
3838

3939
.rct-icon {
40-
display: inline-block;
41-
margin: 0 5px;
42-
width: 14px;
4340
font-family: "FontAwesome";
4441
font-style: normal;
4542
}
4643
}
4744

45+
.rct-collapse,
46+
.rct-node-icon,
47+
.rct-checkbox {
48+
padding: 0 5px;
49+
50+
* {
51+
display: inline-block;
52+
margin: 0;
53+
width: 14px;
54+
}
55+
}
56+
4857
.rct-text {
4958
display: flex;
5059
align-items: center;
@@ -53,7 +62,6 @@
5362
.rct-collapse {
5463
border: 0;
5564
background: none;
56-
padding: 0;
5765
line-height: normal;
5866
color: inherit;
5967
font-size: 12px;

src/sass/react-checkbox-tree.scss

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,23 @@ $rct-label-hover: rgba($rct-icon-color, .1) !default;
3737
}
3838

3939
.rct-icon {
40-
display: inline-block;
41-
margin: 0 5px;
42-
width: 14px;
4340
font-family: "FontAwesome";
4441
font-style: normal;
4542
}
4643
}
4744

45+
.rct-collapse,
46+
.rct-node-icon,
47+
.rct-checkbox {
48+
padding: 0 5px;
49+
50+
* {
51+
display: inline-block;
52+
margin: 0;
53+
width: 14px;
54+
}
55+
}
56+
4857
.rct-text {
4958
display: flex;
5059
align-items: center;
@@ -53,7 +62,6 @@ $rct-label-hover: rgba($rct-icon-color, .1) !default;
5362
.rct-collapse {
5463
border: 0;
5564
background: none;
56-
padding: 0;
5765
line-height: normal;
5866
color: inherit;
5967
font-size: 12px;

test/TreeNode.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ describe('<TreeNode />', () => {
113113
});
114114
});
115115

116+
describe('icon', () => {
117+
it('should replace the node\'s icons with the supplied value', () => {
118+
const wrapper = shallow(
119+
<TreeNode {...baseProps} icon={<i className="fa fa-plus" />} />,
120+
);
121+
122+
assert.isTrue(wrapper.contains(
123+
<span className="rct-node-icon">
124+
<i className="fa fa-plus" />
125+
</span>,
126+
));
127+
});
128+
});
129+
116130
describe('label', () => {
117131
it('should render the node\'s label', () => {
118132
const wrapper = shallow(

0 commit comments

Comments
 (0)