Skip to content

Commit 13b5329

Browse files
committed
Add support for nativeCheckboxes property
Resolves #53.
1 parent 7c8d711 commit 13b5329

File tree

10 files changed

+127
-24
lines changed

10 files changed

+127
-24
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.1.0 (TBA)
4+
5+
### New Features
6+
7+
* [#53]: Add `nativeCheckboxes` property to allow use of native browser checkboxes instead of pseudo-checkbox icons
8+
39
## [v1.0.1](https://github.com/jakezatecky/react-checkbox-tree/compare/v1.0.0...v1.0.1) (2017-09-30)
410

511
### Dependencies

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ All node objects **must** have a unique `value`. This value is serialized into t
9898
| `expanded` | array | An array of expanded node values. | `[]` |
9999
| `name` | string | Optional name for the hidden `<input>` element. | `undefined` |
100100
| `nameAsArray` | bool | If true, the hidden `<input>` will encode its values as an array rather than a joined string. | `false` |
101+
| `nativeCheckboxes` | bool | If true, native browser checkboxes will be used instead of pseudo-checkbox icons. | `false` |
101102
| `noCascade` | bool | If true, toggling a parent node will **not** cascade its check state to its children. | `false` |
102103
| `optimisticToggle` | bool | If true, toggling a partially-checked node will select all children. If false, it will deselect. | `true` |
103104
| `showNodeIcon` | bool | If true, each node will show a parent or leaf icon. | `true` |

src/js/CheckboxTree.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class CheckboxTree extends React.Component {
1717
expanded: PropTypes.arrayOf(PropTypes.string),
1818
name: PropTypes.string,
1919
nameAsArray: PropTypes.bool,
20+
nativeCheckboxes: PropTypes.bool,
2021
noCascade: PropTypes.bool,
2122
optimisticToggle: PropTypes.bool,
2223
showNodeIcon: PropTypes.bool,
@@ -31,6 +32,7 @@ class CheckboxTree extends React.Component {
3132
expanded: [],
3233
name: undefined,
3334
nameAsArray: false,
35+
nativeCheckboxes: false,
3436
noCascade: false,
3537
optimisticToggle: true,
3638
showNodeIcon: true,
@@ -280,6 +282,7 @@ class CheckboxTree extends React.Component {
280282
const className = classNames({
281283
'react-checkbox-tree': true,
282284
'rct-disabled': this.props.disabled,
285+
'rct-native-display': this.props.nativeCheckboxes,
283286
});
284287

285288
return (

src/js/NativeCheckbox.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
4+
class NativeCheckbox extends React.PureComponent {
5+
static propTypes = {
6+
indeterminate: PropTypes.bool,
7+
};
8+
9+
static defaultProps = {
10+
indeterminate: false,
11+
};
12+
13+
componentDidMount() {
14+
this.updateDeterminateProperty();
15+
}
16+
17+
componentDidUpdate() {
18+
this.updateDeterminateProperty();
19+
}
20+
21+
updateDeterminateProperty() {
22+
const { indeterminate } = this.props;
23+
24+
this.checkbox.indeterminate = indeterminate;
25+
}
26+
27+
render() {
28+
const props = { ...this.props };
29+
30+
// Remove property that does not exist in HTML
31+
delete props.indeterminate;
32+
33+
return <input {...props} ref={(c) => { this.checkbox = c; }} type="checkbox" />;
34+
}
35+
}
36+
37+
export default NativeCheckbox;

src/js/TreeNode.js

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

5+
import NativeCheckbox from './NativeCheckbox';
56
import nodeShape from './nodeShape';
67

78
class TreeNode extends React.Component {
@@ -161,11 +162,11 @@ class TreeNode extends React.Component {
161162
<span className="rct-text">
162163
{this.renderCollapseButton()}
163164
<label htmlFor={inputId}>
164-
<input
165+
<NativeCheckbox
165166
checked={checked === 1}
166167
disabled={disabled}
167168
id={inputId}
168-
type="checkbox"
169+
indeterminate={checked === 2}
169170
onChange={this.onCheck}
170171
/>
171172
<span className="rct-checkbox">

src/less/react-checkbox-tree.less

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@
4141
}
4242
}
4343

44-
input {
44+
&:not(.rct-native-display) input {
4545
display: none;
4646
}
4747

48+
&.rct-native-display input {
49+
margin: 0 5px;
50+
}
51+
4852
.rct-icon {
4953
font-family: "FontAwesome";
5054
font-style: normal;
@@ -66,6 +70,11 @@
6670
}
6771
}
6872

73+
.rct-text {
74+
display: flex;
75+
align-items: center;
76+
}
77+
6978
.rct-collapse,
7079
.rct-checkbox,
7180
.rct-node-icon {
@@ -78,11 +87,6 @@
7887
}
7988
}
8089

81-
.rct-text {
82-
display: flex;
83-
align-items: center;
84-
}
85-
8690
.rct-collapse {
8791
border: 0;
8892
background: none;
@@ -103,6 +107,12 @@
103107
}
104108
}
105109

110+
.rct-checkbox {
111+
.rct-native-display & {
112+
display: none;
113+
}
114+
}
115+
106116
.rct-node-icon {
107117
color: @rct-icon-color;
108118
}

src/scss/react-checkbox-tree.scss

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ $rct-label-active: rgba($rct-icon-color, .15) !default;
4141
}
4242
}
4343

44-
input {
44+
&:not(.rct-native-display) input {
4545
display: none;
4646
}
4747

48+
&.rct-native-display input {
49+
margin: 0 5px;
50+
}
51+
4852
.rct-icon {
4953
font-family: "FontAwesome";
5054
font-style: normal;
@@ -66,6 +70,11 @@ $rct-label-active: rgba($rct-icon-color, .15) !default;
6670
}
6771
}
6872

73+
.rct-text {
74+
display: flex;
75+
align-items: center;
76+
}
77+
6978
.rct-collapse,
7079
.rct-checkbox,
7180
.rct-node-icon {
@@ -78,11 +87,6 @@ $rct-label-active: rgba($rct-icon-color, .15) !default;
7887
}
7988
}
8089

81-
.rct-text {
82-
display: flex;
83-
align-items: center;
84-
}
85-
8690
.rct-collapse {
8791
border: 0;
8892
background: none;
@@ -103,6 +107,12 @@ $rct-label-active: rgba($rct-icon-color, .15) !default;
103107
}
104108
}
105109

110+
.rct-checkbox {
111+
.rct-native-display & {
112+
display: none;
113+
}
114+
}
115+
106116
.rct-node-icon {
107117
color: $rct-icon-color;
108118
}

test/CheckboxTree.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ describe('<CheckboxTree />', () => {
3232
});
3333
});
3434

35+
describe('nativeCheckboxes', () => {
36+
it('should add the class rct-native-display to the root', () => {
37+
const wrapper = shallow(
38+
<CheckboxTree nativeCheckboxes nodes={[]} />,
39+
);
40+
41+
assert.isTrue(wrapper.find('.react-checkbox-tree.rct-native-display').exists());
42+
});
43+
});
44+
3545
describe('nodes', () => {
3646
it('should render the node\'s label', () => {
3747
const wrapper = shallow(

test/NativeCheckbox.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
import { assert } from 'chai';
4+
5+
import NativeCheckbox from '../src/js/NativeCheckbox';
6+
7+
describe('<NativeCheckbox />', () => {
8+
describe('indeterminate', () => {
9+
it('should set the JavaScript property to true when true', () => {
10+
const wrapper = mount(
11+
<NativeCheckbox indeterminate />,
12+
);
13+
14+
assert.isTrue(wrapper.find('input').getDOMNode().indeterminate);
15+
});
16+
17+
it('should set the JavaScript property to false when not true', () => {
18+
const wrapper = mount(
19+
<NativeCheckbox />,
20+
);
21+
22+
assert.isFalse(wrapper.find('input').getDOMNode().indeterminate);
23+
});
24+
});
25+
});

test/TreeNode.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('<TreeNode />', () => {
3434
);
3535

3636
assert.equal('planets-jupiter', wrapper.find('label').prop('htmlFor'));
37-
assert.equal('planets-jupiter', wrapper.find('label input[type="checkbox"]').prop('id'));
37+
assert.equal('planets-jupiter', wrapper.find('label NativeCheckbox').prop('id'));
3838
});
3939
});
4040

@@ -63,16 +63,16 @@ describe('<TreeNode />', () => {
6363
<TreeNode {...baseProps} checked={2} />,
6464
);
6565

66-
assert.isFalse(wrapper1.find('input[type="checkbox"]').prop('checked'));
67-
assert.isFalse(wrapper2.find('input[type="checkbox"]').prop('checked'));
66+
assert.isFalse(wrapper1.find('NativeCheckbox').prop('checked'));
67+
assert.isFalse(wrapper2.find('NativeCheckbox').prop('checked'));
6868
});
6969

7070
it('should render a checked input element when set to 1', () => {
7171
const wrapper = shallow(
7272
<TreeNode {...baseProps} checked={1} />,
7373
);
7474

75-
assert.isTrue(wrapper.find('input[type="checkbox"]').prop('checked'));
75+
assert.isTrue(wrapper.find('NativeCheckbox').prop('checked'));
7676
});
7777
});
7878

@@ -92,7 +92,7 @@ describe('<TreeNode />', () => {
9292
<TreeNode {...baseProps} disabled />,
9393
);
9494

95-
assert.isTrue(wrapper.find('input[disabled]').exists());
95+
assert.isTrue(wrapper.find('NativeCheckbox[disabled]').exists());
9696
});
9797
});
9898

@@ -212,7 +212,7 @@ describe('<TreeNode />', () => {
212212
/>,
213213
);
214214

215-
wrapper.find('input[type="checkbox"]').simulate('change');
215+
wrapper.find('NativeCheckbox').simulate('change');
216216

217217
assert.equal('jupiter', actual.value);
218218
});
@@ -231,7 +231,7 @@ describe('<TreeNode />', () => {
231231
/>,
232232
);
233233

234-
wrapper.find('input[type="checkbox"]').simulate('change');
234+
wrapper.find('NativeCheckbox').simulate('change');
235235

236236
assert.isTrue(actual.checked);
237237
});
@@ -250,7 +250,7 @@ describe('<TreeNode />', () => {
250250
/>,
251251
);
252252

253-
wrapper.find('input[type="checkbox"]').simulate('change');
253+
wrapper.find('NativeCheckbox').simulate('change');
254254

255255
assert.isFalse(actual.checked);
256256
});
@@ -269,7 +269,7 @@ describe('<TreeNode />', () => {
269269
/>,
270270
);
271271

272-
wrapper.find('input[type="checkbox"]').simulate('change');
272+
wrapper.find('NativeCheckbox').simulate('change');
273273

274274
assert.isTrue(actual.checked);
275275
});
@@ -290,7 +290,7 @@ describe('<TreeNode />', () => {
290290
/>,
291291
);
292292

293-
wrapper.find('input[type="checkbox"]').simulate('change');
293+
wrapper.find('NativeCheckbox').simulate('change');
294294

295295
assert.isFalse(actual.checked);
296296
});

0 commit comments

Comments
 (0)