Skip to content

Commit

Permalink
Merge 1a52123 into 8071456
Browse files Browse the repository at this point in the history
  • Loading branch information
ellinge committed Jun 28, 2019
2 parents 8071456 + 1a52123 commit 49fc396
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 7 deletions.
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -60,6 +60,7 @@ A lightweight and fast control to render a select component that can display hie
- [always](#always)
- [form states (disabled|readOnly)](#formstates)
- [id](#id)
- [searchPredicate](#searchPredicate)
- [Styling and Customization](#styling-and-customization)
- [Using default styles](#default-styles)
- [Customizing with Bootstrap, Material Design styles](#customizing-styles)
Expand Down Expand Up @@ -386,6 +387,20 @@ Specific id for container. The container renders with a default id of `rdtsN` wh

Use to ensure a own unique id when a simple counter is not sufficient, e.g in a partial server render (SSR)

### searchPredicate

Type: `function`

Optional search predicate to override the default case insensitive contains match on node labels. Example:

```jsx
function searchPredicate(node, searchTerm) {
return node.customData && node.customData.toLower().indexOf(searchTerm) >= 0
}

return <DropdownTreeSelect data={data} searchPredicate={searchPredicate} />
```

## Styling and Customization

### Default styles
Expand Down
4 changes: 3 additions & 1 deletion src/index.js
Expand Up @@ -47,6 +47,7 @@ class DropdownTreeSelect extends Component {
disabled: PropTypes.bool,
readOnly: PropTypes.bool,
id: PropTypes.string,
searchPredicate: PropTypes.func,
}

static defaultProps = {
Expand All @@ -66,12 +67,13 @@ class DropdownTreeSelect extends Component {
this.clientId = props.id || clientIdGenerator.get(this)
}

initNewProps = ({ data, mode, showDropdown, showPartiallySelected }) => {
initNewProps = ({ data, mode, showDropdown, showPartiallySelected, searchPredicate }) => {
this.treeManager = new TreeManager({
data,
mode,
showPartiallySelected,
rootPrefixId: this.clientId,
searchPredicate,
})
// Restore focus-state
const currentFocusNode = this.state.currentFocus && this.treeManager.getNodeById(this.state.currentFocus)
Expand Down
22 changes: 16 additions & 6 deletions src/tree-manager/index.js
Expand Up @@ -5,11 +5,12 @@ import nodeVisitor from './nodeVisitor'
import keyboardNavigation, { FocusActionNames } from './keyboardNavigation'

class TreeManager {
constructor({ data, mode, showPartiallySelected, rootPrefixId }) {
constructor({ data, mode, showPartiallySelected, rootPrefixId, searchPredicate }) {
this._src = data
this.simpleSelect = mode === 'simpleSelect'
this.radioSelect = mode === 'radioSelect'
this.hierarchical = mode === 'hierarchical'
this.searchPredicate = searchPredicate
const { list, defaultValues, singleSelectedNode } = flattenTree({
tree: JSON.parse(JSON.stringify(data)),
simple: this.simpleSelect,
Expand Down Expand Up @@ -49,11 +50,7 @@ class TreeManager {

const matches = []

const addOnMatch = node => {
if (node.label.toLowerCase().indexOf(searchTerm) >= 0) {
matches.push(node._id)
}
}
const addOnMatch = this._getAddOnMatch(matches, searchTerm)

if (closestMatch !== searchTerm) {
const superMatches = this.searchMaps.get(closestMatch)
Expand Down Expand Up @@ -278,6 +275,19 @@ class TreeManager {

return keyboardNavigation.handleToggleNavigationkey(action, prevFocus, readOnly, onToggleChecked, onToggleExpanded)
}

_getAddOnMatch(matches, searchTerm) {
let isMatch = (node, term) => node.label.toLowerCase().indexOf(term) >= 0
if (typeof this.searchPredicate === 'function') {
isMatch = this.searchPredicate
}

return node => {
if (isMatch(node, searchTerm)) {
matches.push(node._id)
}
}
}
}

export default TreeManager
30 changes: 30 additions & 0 deletions src/tree-manager/tests/index.test.js
Expand Up @@ -463,6 +463,36 @@ test('should get matching nodes with mixed case when searched', t => {
t.is(matchTree.get('c2'), undefined)
})

test('should get matching nodes when using custom search predicate', t => {
const tree = {
id: 'i1',
label: 'search me',
value: 'v1',
children: [
{
id: 'c1',
label: 'SeaRch me too',
value: 'l1v1',
children: [
{
id: 'c2',
label: 'I have some extra data to filter on',
customField: "I'm a little TeApOt",
value: 'l2v1',
},
],
},
],
}
const searchPredicate = (node, term) => node.customField && node.customField.toLowerCase().indexOf(term) >= 0
const manager = new TreeManager({ data: tree, searchPredicate })
const { allNodesHidden, tree: matchTree } = manager.filterTree('tEaPoT')
t.false(allNodesHidden)
const nodes = ['i1', 'c1']
nodes.forEach(n => t.is(matchTree.get(n), undefined))
t.not(matchTree.get('c2'), undefined)
})

test('should uncheck previous node in simple select mode', t => {
const tree = [
{
Expand Down
2 changes: 2 additions & 0 deletions types/react-dropdown-tree-select.d.ts
Expand Up @@ -95,6 +95,8 @@ declare module 'react-dropdown-tree-select' {
* Use to ensure a own unique id when a simple counter is not sufficient, e.g in a partial server render (SSR)
*/
id?: string
/** Optional search predicate to override the default case insensitive contains match on node labels. */
searchPredicate?: (currentNode: TreeNode, searchTerm: string) => boolean
}

export interface DropdownTreeSelectState {
Expand Down

0 comments on commit 49fc396

Please sign in to comment.