Skip to content

Commit 764fdeb

Browse files
committed
Add icons property to allow specification of icon components
Resolves #102.
1 parent 04664cb commit 764fdeb

File tree

7 files changed

+128
-32
lines changed

7 files changed

+128
-32
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CHANGELOG
22

3+
## v1.3.0 (TBA)
4+
5+
### New Features
6+
7+
* [#102]: Add `icons` property to allow specification of icon components
8+
39
## [v1.2.4](https://github.com/jakezatecky/react-checkbox-tree/compare/v1.2.3...v1.2.4) (2018-08-29)
410

511
### Bug Fixes

README.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -89,24 +89,25 @@ All node objects **must** have a unique `value`. This value is serialized into t
8989

9090
### Properties
9191

92-
| Property | Type | Description | Default |
93-
| -------------------- | -------- | ------------------------------------------------------------------------------------------------ | ----------- |
94-
| `nodes` | array | **Required**. Specifies the tree nodes and their children. | |
95-
| `checked` | array | An array of checked node values. | `[]` |
96-
| `disabled` | bool | If true, the component will be disabled and nodes cannot be checked. | `false` |
97-
| `expandDisabled` | bool | If true, the ability to expand nodes will be disabled. | `false` |
98-
| `expandOnClick` | bool | If true, nodes will be expanded by clicking on labels. Requires a non-empty `onClick` function. | `false` |
99-
| `expanded` | array | An array of expanded node values. | `[]` |
100-
| `name` | string | Optional name for the hidden `<input>` element. | `undefined` |
101-
| `nameAsArray` | bool | If true, the hidden `<input>` will encode its values as an array rather than a joined string. | `false` |
102-
| `nativeCheckboxes` | bool | If true, native browser checkboxes will be used instead of pseudo-checkbox icons. | `false` |
103-
| `noCascade` | bool | If true, toggling a parent node will **not** cascade its check state to its children. | `false` |
104-
| `onlyLeafCheckboxes` | bool | If true, checkboxes will only be shown for leaf nodes. | `false` |
105-
| `optimisticToggle` | bool | If true, toggling a partially-checked node will select all children. If false, it will deselect. | `true` |
106-
| `showNodeIcon` | bool | If true, each node will show a parent or leaf icon. | `true` |
107-
| `onCheck` | function | onCheck handler: `function(checked) {}` | `() => {}` |
108-
| `onClick` | function | onClick handler: `function(clicked) {}`. If set, it will be called when clicked on a node label. | `() => {}` |
109-
| `onExpand` | function | onExpand handler: `function(expanded) {}` | `() => {}` |
92+
| Property | Type | Description | Default |
93+
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | ----------- |
94+
| `nodes` | array | **Required**. Specifies the tree nodes and their children. | |
95+
| `checked` | array | An array of checked node values. | `[]` |
96+
| `disabled` | bool | If true, the component will be disabled and nodes cannot be checked. | `false` |
97+
| `expandDisabled` | bool | If true, the ability to expand nodes will be disabled. | `false` |
98+
| `expandOnClick` | bool | If true, nodes will be expanded by clicking on labels. Requires a non-empty `onClick` function. | `false` |
99+
| `icons` | object | Nodes for `check`, `uncheck`, `halfCheck`, `expandClose`, `expandOpen`, `parentClose`, `parentOpen`, and `leaf` icons. | { ... } |
100+
| `expanded` | array | An array of expanded node values. | `[]` |
101+
| `name` | string | Optional name for the hidden `<input>` element. | `undefined` |
102+
| `nameAsArray` | bool | If true, the hidden `<input>` will encode its values as an array rather than a joined string. | `false` |
103+
| `nativeCheckboxes` | bool | If true, native browser checkboxes will be used instead of pseudo-checkbox icons. | `false` |
104+
| `noCascade` | bool | If true, toggling a parent node will **not** cascade its check state to its children. | `false` |
105+
| `onlyLeafCheckboxes` | bool | If true, checkboxes will only be shown for leaf nodes. | `false` |
106+
| `optimisticToggle` | bool | If true, toggling a partially-checked node will select all children. If false, it will deselect. | `true` |
107+
| `showNodeIcon` | bool | If true, each node will show a parent or leaf icon. | `true` |
108+
| `onCheck` | function | onCheck handler: `function(checked) {}` | `() => {}` |
109+
| `onClick` | function | onClick handler: `function(clicked) {}`. If set, it will be called when clicked on a node label. | `() => {}` |
110+
| `onExpand` | function | onExpand handler: `function(expanded) {}` | `() => {}` |
110111

111112
#### Node Properties
112113

src/js/CheckboxTree.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import React from 'react';
55
import nanoid from 'nanoid';
66

77
import TreeNode from './TreeNode';
8+
import iconsShape from './iconsShape';
89
import listShape from './listShape';
910
import nodeShape from './nodeShape';
1011

@@ -17,6 +18,7 @@ class CheckboxTree extends React.Component {
1718
expandDisabled: PropTypes.bool,
1819
expandOnClick: PropTypes.bool,
1920
expanded: listShape,
21+
icons: iconsShape,
2022
name: PropTypes.string,
2123
nameAsArray: PropTypes.bool,
2224
nativeCheckboxes: PropTypes.bool,
@@ -35,6 +37,16 @@ class CheckboxTree extends React.Component {
3537
expandDisabled: false,
3638
expandOnClick: false,
3739
expanded: [],
40+
icons: {
41+
check: <span className="rct-icon rct-icon-check" />,
42+
uncheck: <span className="rct-icon rct-icon-uncheck" />,
43+
halfCheck: <span className="rct-icon rct-icon-half-check" />,
44+
expandClose: <span className="rct-icon rct-icon-expand-close" />,
45+
expandOpen: <span className="rct-icon rct-icon-expand-open" />,
46+
parentClose: <span className="rct-icon rct-icon-parent-close" />,
47+
parentOpen: <span className="rct-icon rct-icon-parent-open" />,
48+
leaf: <span className="rct-icon rct-icon-leaf" />,
49+
},
3850
name: undefined,
3951
nameAsArray: false,
4052
nativeCheckboxes: false,
@@ -213,12 +225,14 @@ class CheckboxTree extends React.Component {
213225
disabled,
214226
expandDisabled,
215227
expandOnClick,
228+
icons,
216229
noCascade,
217230
onlyLeafCheckboxes,
218231
optimisticToggle,
219232
showNodeIcon,
220233
onClick,
221234
} = this.props;
235+
const { icons: defaultIcons } = CheckboxTree.defaultProps;
222236
const treeNodes = nodes.map((node) => {
223237
const key = `${node.value}`;
224238
const checked = this.getCheckState(node, noCascade);
@@ -238,6 +252,7 @@ class CheckboxTree extends React.Component {
238252
expandOnClick={expandOnClick}
239253
expanded={node.expanded}
240254
icon={node.icon}
255+
icons={{ ...defaultIcons, ...icons }}
241256
label={node.label}
242257
optimisticToggle={optimisticToggle}
243258
rawChildren={node.children}

src/js/TreeNode.js

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
33
import React from 'react';
44

55
import NativeCheckbox from './NativeCheckbox';
6+
import iconsShape from './iconsShape';
67
import nodeShape from './nodeShape';
78

89
class TreeNode extends React.Component {
@@ -11,6 +12,7 @@ class TreeNode extends React.Component {
1112
disabled: PropTypes.bool.isRequired,
1213
expandDisabled: PropTypes.bool.isRequired,
1314
expanded: PropTypes.bool.isRequired,
15+
icons: iconsShape.isRequired,
1416
label: PropTypes.node.isRequired,
1517
optimisticToggle: PropTypes.bool.isRequired,
1618
showNodeIcon: PropTypes.bool.isRequired,
@@ -130,39 +132,45 @@ class TreeNode extends React.Component {
130132
}
131133

132134
renderCollapseIcon() {
133-
if (!this.props.expanded) {
134-
return <span className="rct-icon rct-icon-expand-close" />;
135+
const { expanded, icons: { expandClose, expandOpen } } = this.props;
136+
137+
if (!expanded) {
138+
return expandClose;
135139
}
136140

137-
return <span className="rct-icon rct-icon-expand-open" />;
141+
return expandOpen;
138142
}
139143

140144
renderCheckboxIcon() {
141-
if (this.props.checked === 0) {
142-
return <span className="rct-icon rct-icon-uncheck" />;
145+
const { checked, icons: { uncheck, check, halfCheck } } = this.props;
146+
147+
if (checked === 0) {
148+
return uncheck;
143149
}
144150

145-
if (this.props.checked === 1) {
146-
return <span className="rct-icon rct-icon-check" />;
151+
if (checked === 1) {
152+
return check;
147153
}
148154

149-
return <span className="rct-icon rct-icon-half-check" />;
155+
return halfCheck;
150156
}
151157

152158
renderNodeIcon() {
153-
if (this.props.icon !== null) {
154-
return this.props.icon;
159+
const { expanded, icon, icons: { leaf, parentClose, parentOpen } } = this.props;
160+
161+
if (icon !== null) {
162+
return icon;
155163
}
156164

157165
if (!this.hasChildren()) {
158-
return <span className="rct-icon rct-icon-leaf" />;
166+
return leaf;
159167
}
160168

161-
if (!this.props.expanded) {
162-
return <span className="rct-icon rct-icon-parent-close" />;
169+
if (!expanded) {
170+
return parentClose;
163171
}
164172

165-
return <span className="rct-icon rct-icon-parent-open" />;
173+
return parentOpen;
166174
}
167175

168176
renderBareLabel(children) {

src/js/iconsShape.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import PropTypes from 'prop-types';
2+
3+
const icons = PropTypes.shape({
4+
check: PropTypes.node,
5+
uncheck: PropTypes.node,
6+
halfCheck: PropTypes.node,
7+
expandClose: PropTypes.node,
8+
expandOpen: PropTypes.node,
9+
parentClose: PropTypes.node,
10+
parentOpen: PropTypes.node,
11+
leaf: PropTypes.node,
12+
});
13+
14+
export default icons;

test/CheckboxTree.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,34 @@ describe('<CheckboxTree />', () => {
5858
});
5959
});
6060

61+
describe('icons', () => {
62+
it('should pass the property directly to tree nodes', () => {
63+
const wrapper = shallow(
64+
<CheckboxTree
65+
icons={{ check: <span className="other-check" /> }}
66+
nodes={[{ value: 'jupiter', label: 'Jupiter' }]}
67+
/>,
68+
);
69+
70+
assert.equal('other-check', shallow(
71+
wrapper.find(TreeNode).prop('icons').check,
72+
).prop('className'));
73+
});
74+
75+
it('should be merged in with the defaults when keys are missing', () => {
76+
const wrapper = shallow(
77+
<CheckboxTree
78+
icons={{ check: <span className="other-check" /> }}
79+
nodes={[{ value: 'jupiter', label: 'Jupiter' }]}
80+
/>,
81+
);
82+
83+
assert.equal('rct-icon rct-icon-uncheck', shallow(
84+
wrapper.find(TreeNode).prop('icons').uncheck,
85+
).prop('className'));
86+
});
87+
});
88+
6189
describe('nativeCheckboxes', () => {
6290
it('should add the class rct-native-display to the root', () => {
6391
const wrapper = shallow(

test/TreeNode.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ const baseProps = {
99
disabled: false,
1010
expandDisabled: false,
1111
expanded: false,
12+
icons: {
13+
check: <span className="rct-icon rct-icon-check" />,
14+
uncheck: <span className="rct-icon rct-icon-uncheck" />,
15+
halfCheck: <span className="rct-icon rct-icon-half-check" />,
16+
expandClose: <span className="rct-icon rct-icon-expand-close" />,
17+
expandOpen: <span className="rct-icon rct-icon-expand-open" />,
18+
parentClose: <span className="rct-icon rct-icon-parent-close" />,
19+
parentOpen: <span className="rct-icon rct-icon-parent-open" />,
20+
leaf: <span className="rct-icon rct-icon-leaf" />,
21+
},
1222
label: 'Jupiter',
1323
optimisticToggle: true,
1424
showNodeIcon: true,
@@ -178,6 +188,20 @@ describe('<TreeNode />', () => {
178188
});
179189
});
180190

191+
describe('icons', () => {
192+
it('should replace the default set of icons with the provided values', () => {
193+
const wrapper = shallow(
194+
<TreeNode {...baseProps} icons={{ uncheck: <span className="other-uncheck" /> }} />,
195+
);
196+
197+
assert.isTrue(wrapper.contains(
198+
<span className="rct-checkbox">
199+
<span className="other-uncheck" />
200+
</span>,
201+
));
202+
});
203+
});
204+
181205
describe('label', () => {
182206
it('should render the node\'s label', () => {
183207
const wrapper = shallow(

0 commit comments

Comments
 (0)