Skip to content

Commit

Permalink
feat: Expose focus, blur events ✨ (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrchief authored Jun 22, 2018
1 parent 09067f0 commit c801e60
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 66 deletions.
96 changes: 54 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[travis-url]: https://travis-ci.org/dowjones/react-dropdown-tree-select
[coveralls-image]: https://img.shields.io/coveralls/dowjones/react-dropdown-tree-select.svg?style=flat-square
[coveralls-url]: https://coveralls.io/r/dowjones/react-dropdown-tree-select?branch=master
[download-image]: https://img.shields.io/npm/dm/react-dropdown-tree-select.svg?style=flat-square
[download-image]: https://img.shields.io/npm/dt/react-dropdown-tree-select.svg?style=flat-square
[semantic-release]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square
[semantic-release-url]: https://github.com/semantic-release/semantic-release
[commitizen]: https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square
Expand All @@ -28,40 +28,40 @@ A lightweight and fast control to render a select component that can display hie

## Table of Contents

* [Screenshot](#screenshot)
* [Demo](#example)
* [Vanilla (no framework)](#vanilla-no-framework)
* [With Bootstrap](#with-bootstrap)
* [With Material Design](#with-material-design)
* [As Single Select](#as-single-select)
* [Install](#install)
* [As NPM package](#as-npm-package)
* [Using a CDN](#using-a-cdn)
* [Peer Dependencies](#peer-dependencies)
* [Usage](#usage)
* [Props](#props)
* [className](#classname)
* [clearSearchOnChange](#clearsearchonchange)
* [onChange](#onchange)
* [onNodeToggle](#onnodetoggle)
* [data](#data)
* [placeholderText](#placeholdertext)
* [noMatchesText](#nomatchestext)
* [keepTreeOnSearch](#keeptreeonsearch)
* [simpleSelect](#simpleselect)
* [showPartiallySelected](#showpartiallyselected)
* [Styling and Customization](#styling-and-customization)
* [Using default styles](#default-styles)
* [Customizing with Bootstrap, Material Design styles](#customizing-styles)
* [Performance](#performance)
* [Search optimizations](#search-optimizations)
* [Search debouncing](#search-debouncing)
* [Virtualized rendering](#virtualized-rendering)
* [Reducing costly DOM manipulations](#reducing-costly-dom-manipulations)
* [FAQ](#faq)
* [Doing more with HOCs](/docs/HOC.md)
* [Development](#development)
* [License](#license)
- [Screenshot](#screenshot)
- [Demo](#example)
- [Vanilla (no framework)](#vanilla-no-framework)
- [With Bootstrap](#with-bootstrap)
- [With Material Design](#with-material-design)
- [As Single Select](#as-single-select)
- [Install](#install)
- [As NPM package](#as-npm-package)
- [Using a CDN](#using-a-cdn)
- [Peer Dependencies](#peer-dependencies)
- [Usage](#usage)
- [Props](#props)
- [className](#classname)
- [clearSearchOnChange](#clearsearchonchange)
- [onChange](#onchange)
- [onNodeToggle](#onnodetoggle)
- [data](#data)
- [placeholderText](#placeholdertext)
- [noMatchesText](#nomatchestext)
- [keepTreeOnSearch](#keeptreeonsearch)
- [simpleSelect](#simpleselect)
- [showPartiallySelected](#showpartiallyselected)
- [Styling and Customization](#styling-and-customization)
- [Using default styles](#default-styles)
- [Customizing with Bootstrap, Material Design styles](#customizing-styles)
- [Performance](#performance)
- [Search optimizations](#search-optimizations)
- [Search debouncing](#search-debouncing)
- [Virtualized rendering](#virtualized-rendering)
- [Reducing costly DOM manipulations](#reducing-costly-dom-manipulations)
- [FAQ](#faq)
- [Doing more with HOCs](/docs/HOC.md)
- [Development](#development)
- [License](#license)

## Screenshot

Expand Down Expand Up @@ -178,8 +178,8 @@ Type: `function`

Fires when a node change event occurs. Currently the following actions trigger a node change:

* Checkbox click which checks/unchecks the item
* Closing of pill (which unchecks the corresponding checkbox item)
- Checkbox click which checks/unchecks the item
- Closing of pill (which unchecks the corresponding checkbox item)

Calls the handler with the current node object and all selected nodes (if any). Example:

Expand Down Expand Up @@ -208,6 +208,18 @@ function onNodeToggle(currentNode) {
return <DropdownTreeSelect data={data} onNodeToggle={onNodeToggle} />
```

### onFocus

Type: `function`

Fires when input box receives focus or the dropdown arrow is clicked. This is helpful for setting `dirty` or `touched` flags with forms.

### onBlur

Type: `function`

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.

### data

Type: `Object` or `Array`
Expand Down Expand Up @@ -323,16 +335,16 @@ You can reference the files from `node_modules/react-dropdown-tree-select/dist/s

Once you import default styles, it is easy to add/override the provided styles to match popular frameworks. Checkout `/docs` folder for some examples.

* [With Bootstrap](/docs/examples/bootstrap)
* [With Material Design ](/docs/examples/material)
- [With Bootstrap](/docs/examples/bootstrap)
- [With Material Design ](/docs/examples/material)

## Performance

### Search optimizations

* The tree creates a flat list of nodes from hierarchical tree data to perform searches that are linear in time irrespective of the tree depth or size.
* It also memoizes each search term, so subsequent searches are instantaneous (almost).
* Last but not the least, the search employs progressive filtering technique where subsequent searches are performed on the previous search set. E.g., say the tree has 4000 nodes altogether and the user wants to filter nodes that contain the text: "2002". As the user enters each key press the search goes like this:
- The tree creates a flat list of nodes from hierarchical tree data to perform searches that are linear in time irrespective of the tree depth or size.
- It also memoizes each search term, so subsequent searches are instantaneous (almost).
- Last but not the least, the search employs progressive filtering technique where subsequent searches are performed on the previous search set. E.g., say the tree has 4000 nodes altogether and the user wants to filter nodes that contain the text: "2002". As the user enters each key press the search goes like this:

```
key press : 2-----20-----200-----2002
Expand Down
3 changes: 3 additions & 0 deletions __snapshots__/src/index.test.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ Generated by [AVA](https://ava.li).
},
]
}
onBlur={Function onBlur {}}
onChange={Function onChange {}}
onFocus={Function onFocus {}}
>
<div
className="react-dropdown-tree-select"
Expand Down
Binary file modified __snapshots__/src/index.test.js.snap
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/bundle.js

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion docs/src/stories/Simple/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ const onNodeToggle = curNode => {
console.log('onNodeToggle::', curNode)
}

const onFocus = () => {
console.log('onFocus')
}

const onBlur = () => {
console.log('onBlur')
}


const Simple = () => (
<div>
<h1>Basic component</h1>
Expand All @@ -30,7 +39,7 @@ const Simple = () => (
As a side effect, it also helps rule out issues arising out of using custom frameworks (if something doesn&apos;t look right in your app but
looks OK here, you know what is messing things up).
</p>
<DropdownTreeSelect data={data} onChange={onChange} onAction={onAction} onNodeToggle={onNodeToggle} className="demo" />
<DropdownTreeSelect data={data} onChange={onChange} onAction={onAction} onNodeToggle={onNodeToggle} onFocus={onFocus} onBlur={onBlur} className="demo" />
</div>
)

Expand Down
19 changes: 12 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,19 @@ class DropdownTreeSelect extends Component {
onChange: PropTypes.func,
onAction: PropTypes.func,
onNodeToggle: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
simpleSelect: PropTypes.bool,
noMatchesText: PropTypes.string,
showPartiallySelected: PropTypes.bool
}

static defaultProps = {
onFocus: () => {},
onBlur: () => {},
onChange: () => {}
}

constructor(props) {
super(props)
this.state = {
Expand All @@ -43,10 +51,6 @@ class DropdownTreeSelect extends Component {
}
}

notifyChange = (...args) => {
typeof this.props.onChange === 'function' && this.props.onChange(...args)
}

createList = (tree, simple, showPartial) => {
this.treeManager = new TreeManager(tree, simple, showPartial)
return this.treeManager.tree
Expand Down Expand Up @@ -88,6 +92,9 @@ class DropdownTreeSelect extends Component {
}
}

if (showDropdown) this.props.onFocus()
else this.props.onBlur()

return !showDropdown ? { showDropdown, ...this.resetSearchState() } : { showDropdown }
})
}
Expand All @@ -111,8 +118,6 @@ class DropdownTreeSelect extends Component {
})
}

// isOutSideClick = e =>

onTagRemove = id => {
this.onCheckboxChange(id, false)
}
Expand Down Expand Up @@ -148,7 +153,7 @@ class DropdownTreeSelect extends Component {
}

this.setState(nextState)
this.notifyChange(this.treeManager.getNodeById(id), tags)
this.props.onChange(this.treeManager.getNodeById(id), tags)
}

onAction = (actionId, nodeId) => {
Expand Down
15 changes: 0 additions & 15 deletions src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,6 @@ test('shows dropdown', t => {
t.snapshot(toJson(wrapper))
})

test('notifies on change', t => {
const handler = spy()
const dummyNode = {
_id: '0',
_parent: null,
_children: ['0-0', '0-1'],
label: 'item1',
value: 'value1'
}
const { tree } = t.context
const wrapper = shallow(<DropdownTreeSelect data={tree} onChange={handler} />)
wrapper.instance().notifyChange(dummyNode, [dummyNode])
t.true(handler.calledWithExactly(dummyNode, [dummyNode]))
})

test('notifies on action', t => {
const handler = spy()
const { tree } = t.context
Expand Down

0 comments on commit c801e60

Please sign in to comment.