Skip to content

Commit 85b07ea

Browse files
ellingemrchief
authored andcommitted
feat: Added support for single select in tree dropdown (#217)
## What does it do? Adds support for single select in the tree dropdown. Simple select ignores any children so this is a hybrid between the two. Also ignores clicks for simple select when labels are disabled (only checked readonly) The middle dropdown is radioSelect and the last one a simpleSelect https://ellinge.github.io/react-dropdown-tree-select-test/#DevelopTemp-checkeddefault Fixes #119 ## Type of change - [x] Bug fix - [x] New feature
1 parent d7604e7 commit 85b07ea

25 files changed

+538
-135
lines changed

.codeclimate.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ plugins:
3434
eslint:
3535
enabled: true
3636
channel: 'eslint-5'
37+
duplication:
38+
enabled: true
39+
config:
40+
languages:
41+
javascript:
42+
mass_threshold : 50
3743
exclude_patterns:
3844
- 'docs/'
3945
- 'snapshots/'

README.md

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ A lightweight and fast control to render a select component that can display hie
4949
- [noMatchesText](#nomatchestext)
5050
- [keepTreeOnSearch](#keeptreeonsearch)
5151
- [keepChildrenOnSearch](#keepchildrenonsearch)
52+
- [keepOpenOnSelect](#keepopenonselect)
5253
- [simpleSelect](#simpleselect)
54+
- [radioSelect](#radioSelect)
5355
- [showPartiallySelected](#showpartiallyselected)
5456
- [showDropdown](#showDropdown)
5557
- [form states (disabled|readOnly)](#formstates)
@@ -142,11 +144,11 @@ const data = {
142144
children: [
143145
{
144146
label: 'No one can get me',
145-
value: 'anonymous'
146-
}
147-
]
148-
}
149-
]
147+
value: 'anonymous',
148+
},
149+
],
150+
},
151+
],
150152
}
151153

152154
const onChange = (currentNode, selectedNodes) => {
@@ -159,7 +161,10 @@ const onNodeToggle = currentNode => {
159161
console.log('onNodeToggle::', currentNode)
160162
}
161163

162-
ReactDOM.render(<DropdownTreeSelect data={data} onChange={onChange} onAction={onAction} onNodeToggle={onNodeToggle} />, document.body) // in real world, you'd want to render to an element, instead of body.
164+
ReactDOM.render(
165+
<DropdownTreeSelect data={data} onChange={onChange} onAction={onAction} onNodeToggle={onNodeToggle} />,
166+
document.body
167+
) // in real world, you'd want to render to an element, instead of body.
163168
```
164169

165170
## Props
@@ -219,7 +224,7 @@ Type: `function`
219224
Fires when a action is triggered. Example:
220225

221226
```jsx
222-
function onAction({action, id}) {
227+
function onAction({ action, id }) {
223228
console.log(`onAction:: [${action}]`, id)
224229
}
225230

@@ -298,14 +303,32 @@ Type: `bool`
298303

299304
Displays children of found nodes to allow searching for a parent node on then selecting any child node of the found node. Defaults to `false`
300305

301-
*NOTE* this works only in combination with `keepTreeOnSearch`
306+
_NOTE_ this works only in combination with `keepTreeOnSearch`
307+
308+
### keepOpenOnSelect
309+
310+
Type: `bool` (default: 'false')
311+
312+
Keeps single selects open after selection. Defaults to `false`
313+
314+
_NOTE_ this works only in combination with `simpleSelect` or `radioSelect`
302315

303316
### simpleSelect
304317

305318
Type: `bool` (default: `false`)
306319

307320
Turns the dropdown into a simple, single select dropdown. If you pass tree data, only immediate children are picked, grandchildren nodes are ignored. Defaults to `false`.
308321

322+
_NOTE_ if multiple nodes in data are selected, `checked` or `isDefaultValue`, only the first visited node is selected
323+
324+
### radioSelect
325+
326+
Type: `bool` (default: `false`)
327+
328+
Turns the dropdown into radio select dropdown. Similar to simpleSelect but keeps tree/children. Defaults to `false`.
329+
330+
_NOTE_ if multiple nodes in data are selected, `checked` or `isDefaultValue`, only the first visited node is selected
331+
309332
### showPartiallySelected
310333

311334
Type: `bool` (default: `false`)
@@ -353,12 +376,12 @@ module: {
353376
fallback: 'style-loader',
354377
use: [
355378
{
356-
loader: 'css-loader'
357-
}
358-
]
379+
loader: 'css-loader',
380+
},
381+
],
359382
}),
360-
include: /node_modules[/\\]react-dropdown-tree-select/
361-
}
383+
include: /node_modules[/\\]react-dropdown-tree-select/,
384+
},
362385
]
363386
}
364387
```
@@ -512,6 +535,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
512535
| [<img src="https://avatars1.githubusercontent.com/u/13344028?v=4" width="100px;" alt="BaarishRain"/><br /><sub><b>BaarishRain</b></sub>](https://github.com/BaarishRain)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3ABaarishRain "Bug reports") | [<img src="https://avatars0.githubusercontent.com/u/32507174?v=4" width="100px;" alt="Kovacs Alexandru Robert"/><br /><sub><b>Kovacs Alexandru Robert</b></sub>](http://kovacsalexandrurobert.ro)<br />[🤔](#ideas-akovacspentalog "Ideas, Planning, & Feedback") | [<img src="https://avatars2.githubusercontent.com/u/11201133?v=4" width="100px;" alt="Alexis Mondragon"/><br /><sub><b>Alexis Mondragon</b></sub>](https://github.com/amondragon)<br />[🤔](#ideas-amondragon "Ideas, Planning, & Feedback") | [<img src="https://avatars2.githubusercontent.com/u/13438795?v=4" width="100px;" alt="Charlie91"/><br /><sub><b>Charlie91</b></sub>](https://github.com/Charlie91)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3ACharlie91 "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/1930681?v=4" width="100px;" alt="Dhirendrasinh"/><br /><sub><b>Dhirendrasinh</b></sub>](https://github.com/dhirendrarathod2000)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Adhirendrarathod2000 "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/7006862?v=4" width="100px;" alt="JKapostins"/><br /><sub><b>JKapostins</b></sub>](https://github.com/JKapostins)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3AJKapostins "Bug reports") | [<img src="https://avatars0.githubusercontent.com/u/24354568?v=4" width="100px;" alt="josvegit"/><br /><sub><b>josvegit</b></sub>](https://github.com/josvegit)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Ajosvegit "Bug reports") |
513536
| [<img src="https://avatars1.githubusercontent.com/u/12422912?v=4" width="100px;" alt="Luis Locon"/><br /><sub><b>Luis Locon</b></sub>](https://twitter.com/LoconLuis)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Aloconluis "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/10121255?v=4" width="100px;" alt="Mikdat DOĞRU"/><br /><sub><b>Mikdat DOĞRU</b></sub>](https://github.com/mikdatdogru)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Amikdatdogru "Bug reports") | [<img src="https://avatars1.githubusercontent.com/u/7553535?v=4" width="100px;" alt="Will Izard"/><br /><sub><b>Will Izard</b></sub>](https://github.com/will-izard)<br />[🤔](#ideas-will-izard "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/4504265?v=4" width="100px;" alt="Nikola Peric"/><br /><sub><b>Nikola Peric</b></sub>](https://gitlab.com/nikperic)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Anikolap "Bug reports") | [<img src="https://avatars2.githubusercontent.com/u/6119839?v=4" width="100px;" alt="Ramón Alejandro Reyes Fajardo"/><br /><sub><b>Ramón Alejandro Reyes Fajardo</b></sub>](https://github.com/ramonrf)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Aramonrf "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/10716099?v=4" width="100px;" alt="Sarada Cherukupalli"/><br /><sub><b>Sarada Cherukupalli</b></sub>](https://github.com/sarada-Cheukupalli)<br />[🤔](#ideas-sarada-Cheukupalli "Ideas, Planning, & Feedback") | [<img src="https://avatars1.githubusercontent.com/u/45608461?v=4" width="100px;" alt="Dilip Gavara"/><br /><sub><b>Dilip Gavara</b></sub>](https://github.com/dilip025)<br />[💻](https://github.com/dowjones/react-dropdown-tree-select/commits?author=dilip025 "Code") |
514537
| [<img src="https://avatars3.githubusercontent.com/u/491877?v=4" width="100px;" alt="Lutz Lengemann"/><br /><sub><b>Lutz Lengemann</b></sub>](http://www.dealzeit.de)<br />[💻](https://github.com/dowjones/react-dropdown-tree-select/commits?author=mobilutz "Code") | [<img src="https://avatars0.githubusercontent.com/u/26381655?v=4" width="100px;" alt="Akshay Dipta"/><br /><sub><b>Akshay Dipta</b></sub>](https://github.com/Eainde)<br />[🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3AEainde "Bug reports") | [<img src="https://avatars3.githubusercontent.com/u/137158?v=4" width="100px;" alt="Ian Langworth ☠"/><br /><sub><b>Ian Langworth ☠</b></sub>](https://langworth.com/)<br />[🤔](#ideas-statico "Ideas, Planning, & Feedback") | [<img src="https://avatars1.githubusercontent.com/u/5932031?v=4" width="100px;" alt="Stoyan Berov"/><br /><sub><b>Stoyan Berov</b></sub>](https://github.com/stoberov)<br />[💻](https://github.com/dowjones/react-dropdown-tree-select/commits?author=stoberov "Code") [🐛](https://github.com/dowjones/react-dropdown-tree-select/issues?q=author%3Astoberov "Bug reports") | [<img src="https://avatars0.githubusercontent.com/u/17863113?v=4" width="100px;" alt="ellinge"/><br /><sub><b>ellinge</b></sub>](https://github.com/ellinge)<br />[💻](https://github.com/dowjones/react-dropdown-tree-select/commits?author=ellinge "Code") [🤔](#ideas-ellinge "Ideas, Planning, & Feedback") [🚧](#maintenance-ellinge "Maintenance") |
538+
515539
<!-- ALL-CONTRIBUTORS-LIST:END -->
516540

517541
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!

__snapshots__/src/checkbox/index.test.js.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ Generated by [AVA](https://ava.li).
1010
1111
<input
1212
className="sample"
13-
onChange={Function {}}
1413
type="checkbox"
1514
/>
1615

@@ -20,6 +19,5 @@ Generated by [AVA](https://ava.li).
2019
2120
<input
2221
disabled={true}
23-
onChange={Function {}}
2422
type="checkbox"
2523
/>
-53 Bytes
Binary file not shown.

__snapshots__/src/index.test.js.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,120 @@ Generated by [AVA](https://ava.li).
3232
</div>
3333
</div>
3434

35+
## renders default radio select state
36+
37+
> Snapshot 1
38+
39+
<DropdownTreeSelect
40+
data={
41+
[
42+
{
43+
children: [
44+
{
45+
children: [
46+
{
47+
label: 'item1-1-1',
48+
value: 'value1-1-1',
49+
},
50+
{
51+
label: 'item1-1-2',
52+
value: 'value1-1-2',
53+
},
54+
],
55+
label: 'item1-1',
56+
value: 'value1-1',
57+
},
58+
{
59+
label: 'item1-2',
60+
value: 'value1-2',
61+
},
62+
],
63+
label: 'item1',
64+
value: 'value1',
65+
},
66+
{
67+
children: [
68+
{
69+
children: [
70+
{
71+
label: 'item2-1-1',
72+
value: 'value2-1-1',
73+
},
74+
{
75+
label: 'item2-1-2',
76+
value: 'value2-1-2',
77+
},
78+
{
79+
children: [
80+
{
81+
label: 'item2-1-3-1',
82+
value: 'value2-1-3-1',
83+
},
84+
],
85+
label: 'item2-1-3',
86+
value: 'value2-1-3',
87+
},
88+
],
89+
label: 'item2-1',
90+
value: 'value2-1',
91+
},
92+
{
93+
label: 'item2-2',
94+
value: 'value2-2',
95+
},
96+
],
97+
label: 'item2',
98+
value: 'value2',
99+
},
100+
]
101+
}
102+
id="rdts"
103+
onBlur={Function onBlur {}}
104+
onChange={Function onChange {}}
105+
onFocus={Function onFocus {}}
106+
radioSelect={true}
107+
>
108+
<div
109+
className="react-dropdown-tree-select"
110+
id="rdts"
111+
>
112+
<div
113+
className="dropdown radio-select"
114+
>
115+
<a
116+
className="dropdown-trigger arrow bottom"
117+
onClick={Function {}}
118+
>
119+
<Input
120+
inputRef={Function inputRef {}}
121+
onBlur={Function {}}
122+
onFocus={Function {}}
123+
onInputChange={Function {}}
124+
onTagRemove={Function {}}
125+
tags={[]}
126+
>
127+
<ul
128+
className="tag-list"
129+
>
130+
<li
131+
className="tag-item"
132+
>
133+
<input
134+
className="search"
135+
onBlur={Function {}}
136+
onChange={Function {}}
137+
onFocus={Function {}}
138+
placeholder="Choose..."
139+
type="text"
140+
/>
141+
</li>
142+
</ul>
143+
</Input>
144+
</a>
145+
</div>
146+
</div>
147+
</DropdownTreeSelect>
148+
35149
## renders default state
36150

37151
> Snapshot 1
@@ -173,6 +287,7 @@ Generated by [AVA](https://ava.li).
173287
className="dropdown-content"
174288
>
175289
<Tree
290+
clientId="rdts"
176291
data={
177292
Map {
178293
'rdts-0' => {

__snapshots__/src/index.test.js.snap

508 Bytes
Binary file not shown.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Snapshot report for `src\radio\index.test.js`
2+
3+
The actual snapshot is saved in `index.test.js.snap`.
4+
5+
Generated by [AVA](https://ava.li).
6+
7+
## Radio component
8+
9+
> Snapshot 1
10+
11+
<input
12+
className="sample"
13+
name="sample"
14+
type="radio"
15+
/>
16+
17+
## renders disabled state
18+
19+
> Snapshot 1
20+
21+
<input
22+
disabled={true}
23+
name="sample"
24+
type="radio"
25+
/>
257 Bytes
Binary file not shown.

docs/src/stories/Options/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class WithOptions extends PureComponent {
1313
this.state = {
1414
clearSearchOnChange: false,
1515
keepTreeOnSearch: false,
16+
keepOpenOnSelect: false,
1617
simpleSelect: false,
18+
radioSelect: false,
1719
showPartiallySelected: false,
1820
disabled: false,
1921
readOnly: false,
@@ -39,7 +41,9 @@ class WithOptions extends PureComponent {
3941
const {
4042
clearSearchOnChange,
4143
keepTreeOnSearch,
44+
keepOpenOnSelect,
4245
simpleSelect,
46+
radioSelect,
4347
showPartiallySelected,
4448
disabled,
4549
readOnly,
@@ -70,7 +74,14 @@ class WithOptions extends PureComponent {
7074
checked={keepTreeOnSearch}
7175
onChange={this.onOptionsChange}
7276
/>
77+
<Checkbox
78+
label="Keep tree open on select"
79+
value="keepOpenOnSelect"
80+
checked={keepOpenOnSelect}
81+
onChange={this.onOptionsChange}
82+
/>
7383
<Checkbox label="Simple Select" value="simpleSelect" checked={simpleSelect} onChange={this.onOptionsChange} />
84+
<Checkbox label="Radio Select" value="radioSelect" checked={radioSelect} onChange={this.onOptionsChange} />
7485
<Checkbox
7586
label="Show Partially Selected"
7687
value="showPartiallySelected"
@@ -83,13 +94,16 @@ class WithOptions extends PureComponent {
8394
</div>
8495
<div>
8596
<DropdownTreeSelect
97+
id="rdts"
8698
data={data}
8799
onChange={this.onChange}
88100
onAction={this.onAction}
89101
onNodeToggle={this.onNodeToggle}
90102
clearSearchOnChange={clearSearchOnChange}
91103
keepTreeOnSearch={keepTreeOnSearch}
104+
keepOpenOnSelect={keepOpenOnSelect}
92105
simpleSelect={simpleSelect}
106+
radioSelect={radioSelect}
93107
showPartiallySelected={showPartiallySelected}
94108
disabled={disabled}
95109
readOnly={readOnly}

src/checkbox/index.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,6 @@ class Checkbox extends PureComponent {
1717
readOnly: PropTypes.bool,
1818
}
1919

20-
// this (stopPropagation) is needed since FireFox wrongly detects inside clicks
21-
// See https://github.com/dowjones/react-dropdown-tree-select/pull/154
22-
// and https://github.com/dowjones/react-dropdown-tree-select/issues/148
23-
handleChange = e => {
24-
e.stopPropagation()
25-
e.nativeEvent.stopImmediatePropagation()
26-
this.props.onChange(e)
27-
}
28-
2920
render() {
3021
const { checked, indeterminate = false, onChange, disabled, readOnly, ...rest } = this.props
3122

@@ -35,7 +26,7 @@ class Checkbox extends PureComponent {
3526
<input
3627
type="checkbox"
3728
ref={refUpdater({ checked, indeterminate })}
38-
onChange={this.handleChange}
29+
onChange={onChange}
3930
disabled={isDisabled}
4031
{...rest}
4132
/>

src/checkbox/index.test.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { shallow } from 'enzyme'
22
import React from 'react'
33
import test from 'ava'
44
import toJson from 'enzyme-to-json'
5-
import { spy } from 'sinon'
65

76
import Checkbox, { refUpdater } from './index'
87

@@ -27,16 +26,3 @@ test('renders disabled state', t => {
2726
const tree = toJson(shallow(<Checkbox disabled />))
2827
t.snapshot(tree)
2928
})
30-
31-
test('call stopPropagation and stopImmediatePropagation when clicked', t => {
32-
const onChange = spy()
33-
const wrapper = shallow(<Checkbox className="sample" onChange={onChange} />)
34-
const event = {
35-
stopPropagation: spy(),
36-
nativeEvent: { stopImmediatePropagation: spy() },
37-
}
38-
wrapper.simulate('change', event)
39-
t.true(onChange.called)
40-
t.true(event.stopPropagation.called)
41-
t.true(event.nativeEvent.stopImmediatePropagation.called)
42-
})

0 commit comments

Comments
 (0)