Skip to content

Commit f03b2e5

Browse files
committed
feat: Add new props for search and keyboard navigation
The new prop searchTerm can be used to set the initial search term or fully control the search semantics for a higher-order component. The new prop onSearchChange allows specification of a callback for when the search term changes or the search mode is activated or deactivated. The new prop disableKeyboardNavigation allows disabling all actions associated with a key down event in the search input box.
1 parent 1e891d0 commit f03b2e5

File tree

6 files changed

+86
-6
lines changed

6 files changed

+86
-6
lines changed

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ A lightweight and fast control to render a select component that can display hie
4141
- [Usage](#usage)
4242
- [Props](#props)
4343
- [className](#classname)
44+
- [searchTerm](#searchterm)
4445
- [clearSearchOnChange](#clearsearchonchange)
4546
- [onChange](#onchange)
4647
- [onNodeToggle](#onnodetoggle)
4748
- [onAction](#onaction)
4849
- [onFocus](#onfocus)
4950
- [onBlur](#onblur)
51+
- [onSearchChange](#onsearchchange)
5052
- [data](#data)
5153
- [texts](#texts)
5254
- [keepTreeOnSearch](#keeptreeonsearch)
@@ -66,7 +68,8 @@ A lightweight and fast control to render a select component that can display hie
6668
- [searchPredicate](#searchpredicate)
6769
- [inlineSearchInput](#inlinesearchinput)
6870
- [tabIndex](#tabIndex)
69-
- [disablePoppingOnBackspace](#disablePoppingOnBackspace)
71+
- [disablePoppingOnBackspace](#disablepoppingonbackspace)
72+
- [disableKeyboardNavigation](#disablekeyboardnavigation)
7073
- [Styling and Customization](#styling-and-customization)
7174
- [Using default styles](#default-styles)
7275
- [Customizing with Bootstrap, Material Design styles](#customizing-styles)
@@ -188,6 +191,12 @@ Type: `string`
188191

189192
Additional classname for container. The container renders with a default classname of `react-dropdown-tree-select`.
190193

194+
### searchTerm
195+
196+
Type: `string`
197+
198+
Initializes or adjusts the active search term. Set to an empty string or `undefined` to turn search mode off.
199+
191200
### clearSearchOnChange
192201

193202
Type: `bool`
@@ -256,6 +265,24 @@ Type: `function`
256265

257266
Fires when input box loses focus or the dropdown arrow is clicked again (and the dropdown collapses). This is helpful for setting `dirty` or `touched` flags with forms.
258267

268+
### onSearchChange
269+
270+
Type: `function`
271+
272+
Called when the search input box is changed with the current search term. This can be fired either through user input or automatically due to `clearSearchOnChange`. Example:
273+
274+
```jsx
275+
function onSearchChange(searchTerm: str) {
276+
if (searchTerm) {
277+
console.log('New search term is', searchTerm)
278+
} else {
279+
console.log('Search mode has been disabled')
280+
}
281+
}
282+
283+
return <DropdownTreeSelect data={data} onSearchChange={onSearchChange} />
284+
```
285+
259286
### data
260287

261288
Type: `Object` or `Array`
@@ -428,6 +455,12 @@ Type: `bool` (default: `false`)
428455

429456
`disablePoppingOnBackspace=true` attribute indicates that when a user triggers a 'backspace' keyDown in the empty search bar, the tree will not deselect nodes.
430457

458+
### disableKeyboardNavigation
459+
460+
Type: `bool` (default: `false`)
461+
462+
`disableKeyboardNavigation=true` prevents keyboard navigation actions from being taken on the nodes when the user triggers a keyDown in the search bar. This restores standard input box semantics.
463+
431464
## Styling and Customization
432465

433466
### Default styles

docs/src/stories/Options/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class WithOptions extends PureComponent {
1111
super(props)
1212

1313
this.state = {
14+
searchTerm: '',
1415
clearSearchOnChange: false,
1516
keepTreeOnSearch: false,
1617
keepOpenOnSelect: false,
@@ -39,8 +40,13 @@ class WithOptions extends PureComponent {
3940
this.setState({ [value]: !this.state[value] })
4041
}
4142

43+
onSearchChange = searchTerm => {
44+
this.setState({ searchTerm: searchTerm })
45+
}
46+
4247
render() {
4348
const {
49+
searchTerm,
4450
clearSearchOnChange,
4551
keepTreeOnSearch,
4652
keepOpenOnSelect,
@@ -105,6 +111,15 @@ class WithOptions extends PureComponent {
105111
onChange={e => this.setState({ inlineSearchPlaceholder: e.target.value })}
106112
/>
107113
</div>
114+
<div style={{ marginBottom: '10px' }}>
115+
<label htmlFor="searchTerm">Search term: </label>
116+
<input
117+
id="searchTerm"
118+
type="text"
119+
value={searchTerm}
120+
onChange={e => this.setState({ searchTerm: e.target.value })}
121+
/>
122+
</div>
108123
<Checkbox
109124
label="Inline Search Input"
110125
value="inlineSearchInput"
@@ -142,9 +157,11 @@ class WithOptions extends PureComponent {
142157
<DropdownTreeSelect
143158
id="rdts"
144159
data={data}
160+
searchTerm={searchTerm}
145161
onChange={this.onChange}
146162
onAction={this.onAction}
147163
onNodeToggle={this.onNodeToggle}
164+
onSearchChange={this.onSearchChange}
148165
clearSearchOnChange={clearSearchOnChange}
149166
keepTreeOnSearch={keepTreeOnSearch}
150167
keepOpenOnSelect={keepOpenOnSelect}

docs/src/stories/Simple/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ const onBlur = () => {
2323
console.log('onBlur')
2424
}
2525

26+
const onSearchChange = searchTerm => {
27+
console.log('onSearchChange::', searchTerm)
28+
}
29+
2630
const Simple = () => (
2731
<div>
2832
<h1>Basic component</h1>
@@ -45,6 +49,7 @@ const Simple = () => (
4549
onNodeToggle={onNodeToggle}
4650
onFocus={onFocus}
4751
onBlur={onBlur}
52+
onSearchChange={onSearchChange}
4853
className="demo"
4954
/>
5055
</div>

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "react-dropdown-tree-select",
3-
"version": "0.0.0-semantic-release",
2+
"name": "@gandhis1/react-dropdown-tree-select",
3+
"version": "2.7.1-fork-2",
44
"description": "Lightweight, customizable and fast Dropdown Tree Select component for React",
55
"keywords": [
66
"react",

src/index.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getAriaLabel } from './a11y'
2323
class DropdownTreeSelect extends Component {
2424
static propTypes = {
2525
data: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
26+
searchTerm: PropTypes.string,
2627
clearSearchOnChange: PropTypes.bool,
2728
keepTreeOnSearch: PropTypes.bool,
2829
keepChildrenOnSearch: PropTypes.bool,
@@ -41,6 +42,7 @@ class DropdownTreeSelect extends Component {
4142
onNodeToggle: PropTypes.func,
4243
onFocus: PropTypes.func,
4344
onBlur: PropTypes.func,
45+
onSearchChange: PropTypes.func,
4446
mode: PropTypes.oneOf(['multiSelect', 'simpleSelect', 'radioSelect', 'hierarchical']),
4547
showPartiallySelected: PropTypes.bool,
4648
disabled: PropTypes.bool,
@@ -50,18 +52,21 @@ class DropdownTreeSelect extends Component {
5052
inlineSearchInput: PropTypes.bool,
5153
tabIndex: PropTypes.number,
5254
disablePoppingOnBackspace: PropTypes.bool,
55+
disableKeyboardNavigation: PropTypes.bool,
5356
}
5457

5558
static defaultProps = {
5659
onAction: () => {},
5760
onFocus: () => {},
5861
onBlur: () => {},
5962
onChange: () => {},
63+
onSearchChange: _ => {},
6064
texts: {},
6165
showDropdown: 'default',
6266
inlineSearchInput: false,
6367
tabIndex: 0,
6468
disablePoppingOnBackspace: false,
69+
disableKeyboardNavigation: true,
6570
}
6671

6772
constructor(props) {
@@ -73,7 +78,7 @@ class DropdownTreeSelect extends Component {
7378
this.clientId = props.id || clientIdGenerator.get(this)
7479
}
7580

76-
initNewProps = ({ data, mode, showDropdown, showPartiallySelected, searchPredicate }) => {
81+
initNewProps = ({ data, searchTerm, mode, showDropdown, showPartiallySelected, searchPredicate }) => {
7782
this.treeManager = new TreeManager({
7883
data,
7984
mode,
@@ -86,7 +91,12 @@ class DropdownTreeSelect extends Component {
8691
if (currentFocusNode) {
8792
currentFocusNode._focused = true
8893
}
94+
const searchTermChanged = searchTerm !== prevState.searchTerm
95+
if (this.searchInput && searchTermChanged) {
96+
this.searchInput.value = searchTerm
97+
}
8998
return {
99+
searchModeOn: searchTermChanged,
90100
showDropdown: /initial|always/.test(showDropdown) || prevState.showDropdown === true,
91101
...this.treeManager.getTreeAndTags(),
92102
}
@@ -96,7 +106,10 @@ class DropdownTreeSelect extends Component {
96106
resetSearchState = () => {
97107
// clear the search criteria and avoid react controlled/uncontrolled warning
98108
if (this.searchInput) {
99-
this.searchInput.value = ''
109+
if (this.searchInput.value !== '') {
110+
this.props.onSearchChange(this.searchInput.value)
111+
this.searchInput.value = ''
112+
}
100113
}
101114

102115
return {
@@ -154,6 +167,7 @@ class DropdownTreeSelect extends Component {
154167
this.props.keepChildrenOnSearch
155168
)
156169
const searchModeOn = value.length > 0
170+
this.props.onSearchChange(value)
157171

158172
this.setState({
159173
tree,
@@ -238,6 +252,10 @@ class DropdownTreeSelect extends Component {
238252
}
239253

240254
onKeyboardKeyDown = e => {
255+
if (this.props.disableKeyboardNavigation) {
256+
return // Will fire the default action
257+
}
258+
241259
const { readOnly, mode, disablePoppingOnBackspace } = this.props
242260
const { showDropdown, tags, searchModeOn, currentFocus } = this.state
243261
const tm = this.treeManager

types/react-dropdown-tree-select.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// tslint:disable:interface-name
2-
declare module 'react-dropdown-tree-select' {
2+
declare module '@gandhis1/react-dropdown-tree-select' {
33
import * as React from 'react'
44

55
export type TreeData = Object | TreeNodeProps[]
@@ -10,6 +10,8 @@ declare module 'react-dropdown-tree-select' {
1010

1111
export interface DropdownTreeSelectProps {
1212
data: TreeData
13+
/** Initialize the search input with the specified term and search the nodes */
14+
searchTerm?: str
1315
/** Clear the input search if a node has been selected/unselected */
1416
clearSearchOnChange?: boolean
1517
/** Displays search results as a tree instead of flattened results */
@@ -54,6 +56,9 @@ declare module 'react-dropdown-tree-select' {
5456
* This is helpful for setting dirty or touched flags with forms
5557
*/
5658
onBlur?: () => void
59+
/** Fires when search input is modified.
60+
*/
61+
onSearchChange?: (searchTerm: str) => void
5762
/** Defines how the dropdown is rendered / behaves
5863
*
5964
* - multiSelect
@@ -103,6 +108,8 @@ declare module 'react-dropdown-tree-select' {
103108
* search bar, the tree will not deselect nodes.
104109
*/
105110
disablePoppingOnBackspace?: boolean
111+
/** dsiableKeyboardNavigation will disable keyboard navigation of the tree */
112+
dsiableKeyboardNavigation?: boolean
106113
}
107114

108115
export interface DropdownTreeSelectState {

0 commit comments

Comments
 (0)