Skip to content

Commit

Permalink
feat(props): add onOuterClick prop (#208)
Browse files Browse the repository at this point in the history
This PR is not ready to be merged yet. When merged, this will fix #206

This PR does the following (Will appreciate early feedback and comments):-

1. Add onOuterClick description to README. I can remove the link to a specific issue from there

2. Add onOuterClick to propTypes with type PropTypes.func

3. Call this.props.onOuterClick as a callback to reset function when it is called under onMouseUp event

4. Add a test case for newly added functionality. This is still incomplete. I started with the test case expecting it to fail in its current state where I open the menu and simulate a outside click with click on document.body. With the current state of the onOuterClick callback this should have resulted in isOpen to be false there by failing the test but it is not the case. Exploring.

Also, to simulate the click on document.body. I borrowed the function `mouseDownAndUp` from `downshift.lifecycle.js`. I can either move this test over there or expose that function in a better way than duplicating.

Let me know your feedback on the approach? Also, please point out if you believe that I understood the problem incorrectly.
Divyendu Singh authored and Kent C. Dodds committed Oct 4, 2017

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 23008b6 commit b9f8cf8
Showing 6 changed files with 52 additions and 8 deletions.
5 changes: 4 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
@@ -109,7 +109,10 @@
"avatar_url": "https://avatars3.githubusercontent.com/u/746482?v=4",
"profile": "https://divyendusingh.com",
"contributions": [
"example"
"example",
"code",
"doc",
"test"
]
},
{
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -303,6 +303,25 @@ server rendering items (which each have an `id` prop generated based on the
You should not normally need to set this prop. It's only useful if you're
rendering into a different `window` context from where your JavaScript is running, for example an iframe.


### onOuterClick

> `function` | optional
A helper callback to help control internal state of downshift like `isOpen` as mentioned in
[this issue](https://github.com/paypal/downshift/issues/206). The same behavior can be achieved
using `onStateChange`, but this prop is provided as a helper because it's a fairly common
use-case if you're controlling the `isOpen` state:

```jsx
<Downshift
isOpen={this.state.menuIsOpen}
onOuterClick={() => this.setState({menuIsOpen: false})}
>
{/* your callback */}
</Downshift>
```

## Control Props

downshift manages its own state internally and calls your `onChange` and
@@ -601,7 +620,7 @@ Thanks goes to these people ([emoji key][emojis]):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](https://kentcdodds.com)<br />[💻](https://github.com/paypal/downshift/commits?author=kentcdodds "Code") [📖](https://github.com/paypal/downshift/commits?author=kentcdodds "Documentation") [🚇](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/paypal/downshift/commits?author=kentcdodds "Tests") | [<img src="https://avatars0.githubusercontent.com/u/100200?v=4" width="100px;"/><br /><sub>Ryan Florence</sub>](http://twitter.com/ryanflorence)<br />[🤔](#ideas-ryanflorence "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/112170?v=4" width="100px;"/><br /><sub>Jared Forsyth</sub>](http://jaredforsyth.com)<br />[🤔](#ideas-jaredly "Ideas, Planning, & Feedback") [📖](https://github.com/paypal/downshift/commits?author=jaredly "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/8162598?v=4" width="100px;"/><br /><sub>Jack Moore</sub>](https://github.com/jtmthf)<br />[💡](#example-jtmthf "Examples") | [<img src="https://avatars1.githubusercontent.com/u/2762082?v=4" width="100px;"/><br /><sub>Travis Arnold</sub>](http://travisrayarnold.com)<br />[💻](https://github.com/paypal/downshift/commits?author=souporserious "Code") [📖](https://github.com/paypal/downshift/commits?author=souporserious "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1045233?v=4" width="100px;"/><br /><sub>Marcy Sutton</sub>](http://marcysutton.com)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Amarcysutton "Bug reports") [🤔](#ideas-marcysutton "Ideas, Planning, & Feedback") | [<img src="https://avatars2.githubusercontent.com/u/244704?v=4" width="100px;"/><br /><sub>Jeremy Gayed</sub>](http://www.jeremygayed.com)<br />[💡](#example-tizmagik "Examples") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [<img src="https://avatars3.githubusercontent.com/u/6270048?v=4" width="100px;"/><br /><sub>Haroen Viaene</sub>](https://haroen.me)<br />[💡](#example-Haroenv "Examples") | [<img src="https://avatars2.githubusercontent.com/u/15073300?v=4" width="100px;"/><br /><sub>monssef</sub>](https://github.com/rezof)<br />[💡](#example-rezof "Examples") | [<img src="https://avatars2.githubusercontent.com/u/5382443?v=4" width="100px;"/><br /><sub>Federico Zivolo</sub>](https://fezvrasta.github.io)<br />[📖](https://github.com/paypal/downshift/commits?author=FezVrasta "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/746482?v=4" width="100px;"/><br /><sub>Divyendu Singh</sub>](https://divyendusingh.com)<br />[💡](#example-divyenduz "Examples") | [<img src="https://avatars1.githubusercontent.com/u/841955?v=4" width="100px;"/><br /><sub>Muhammad Salman</sub>](https://github.com/salmanmanekia)<br />[💻](https://github.com/paypal/downshift/commits?author=salmanmanekia "Code") | [<img src="https://avatars3.githubusercontent.com/u/10820159?v=4" width="100px;"/><br /><sub>João Alberto</sub>](https://twitter.com/psicotropidev)<br />[💻](https://github.com/paypal/downshift/commits?author=psicotropicos "Code") | [<img src="https://avatars0.githubusercontent.com/u/16327281?v=4" width="100px;"/><br /><sub>Bernard Lin</sub>](https://github.com/bernard-lin)<br />[💻](https://github.com/paypal/downshift/commits?author=bernard-lin "Code") [📖](https://github.com/paypal/downshift/commits?author=bernard-lin "Documentation") |
| [<img src="https://avatars3.githubusercontent.com/u/6270048?v=4" width="100px;"/><br /><sub>Haroen Viaene</sub>](https://haroen.me)<br />[💡](#example-Haroenv "Examples") | [<img src="https://avatars2.githubusercontent.com/u/15073300?v=4" width="100px;"/><br /><sub>monssef</sub>](https://github.com/rezof)<br />[💡](#example-rezof "Examples") | [<img src="https://avatars2.githubusercontent.com/u/5382443?v=4" width="100px;"/><br /><sub>Federico Zivolo</sub>](https://fezvrasta.github.io)<br />[📖](https://github.com/paypal/downshift/commits?author=FezVrasta "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/746482?v=4" width="100px;"/><br /><sub>Divyendu Singh</sub>](https://divyendusingh.com)<br />[💡](#example-divyenduz "Examples") [💻](https://github.com/paypal/downshift/commits?author=divyenduz "Code") [📖](https://github.com/paypal/downshift/commits?author=divyenduz "Documentation") [⚠️](https://github.com/paypal/downshift/commits?author=divyenduz "Tests") | [<img src="https://avatars1.githubusercontent.com/u/841955?v=4" width="100px;"/><br /><sub>Muhammad Salman</sub>](https://github.com/salmanmanekia)<br />[💻](https://github.com/paypal/downshift/commits?author=salmanmanekia "Code") | [<img src="https://avatars3.githubusercontent.com/u/10820159?v=4" width="100px;"/><br /><sub>João Alberto</sub>](https://twitter.com/psicotropidev)<br />[💻](https://github.com/paypal/downshift/commits?author=psicotropicos "Code") | [<img src="https://avatars0.githubusercontent.com/u/16327281?v=4" width="100px;"/><br /><sub>Bernard Lin</sub>](https://github.com/bernard-lin)<br />[💻](https://github.com/paypal/downshift/commits?author=bernard-lin "Code") [📖](https://github.com/paypal/downshift/commits?author=bernard-lin "Documentation") |
| [<img src="https://avatars1.githubusercontent.com/u/7330124?v=4" width="100px;"/><br /><sub>Geoff Davis</sub>](https://geoffdavis.info)<br />[💡](#example-geoffdavis92 "Examples") | [<img src="https://avatars0.githubusercontent.com/u/3415488?v=4" width="100px;"/><br /><sub>Anup</sub>](https://github.com/reznord)<br />[📖](https://github.com/paypal/downshift/commits?author=reznord "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/340520?v=4" width="100px;"/><br /><sub>Ferdinand Salis</sub>](http://ferdinandsalis.com)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Aferdinandsalis "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=ferdinandsalis "Code") | [<img src="https://avatars2.githubusercontent.com/u/662750?v=4" width="100px;"/><br /><sub>Kye Hohenberger</sub>](https://github.com/tkh44)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Atkh44 "Bug reports") | [<img src="https://avatars0.githubusercontent.com/u/1443499?v=4" width="100px;"/><br /><sub>Julien Goux</sub>](https://github.com/jgoux)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ajgoux "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=jgoux "Code") [⚠️](https://github.com/paypal/downshift/commits?author=jgoux "Tests") | [<img src="https://avatars2.githubusercontent.com/u/9586897?v=4" width="100px;"/><br /><sub>Joachim Seminck</sub>](https://github.com/jseminck)<br />[💻](https://github.com/paypal/downshift/commits?author=jseminck "Code") | [<img src="https://avatars3.githubusercontent.com/u/954596?v=4" width="100px;"/><br /><sub>Jesse Harlin</sub>](http://jesseharlin.net/)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Athe-simian "Bug reports") [💡](#example-the-simian "Examples") |
| [<img src="https://avatars0.githubusercontent.com/u/1402095?v=4" width="100px;"/><br /><sub>Matt Parrish</sub>](https://github.com/pbomb)<br />[🔧](#tool-pbomb "Tools") | [<img src="https://avatars1.githubusercontent.com/u/11661846?v=4" width="100px;"/><br /><sub>thom</sub>](http://thom.kr)<br />[💻](https://github.com/paypal/downshift/commits?author=thomhos "Code") | [<img src="https://avatars2.githubusercontent.com/u/1088312?v=4" width="100px;"/><br /><sub>Vu Tran</sub>](http://twitter.com/tranvu)<br />[💻](https://github.com/paypal/downshift/commits?author=vutran "Code") | [<img src="https://avatars1.githubusercontent.com/u/74193?v=4" width="100px;"/><br /><sub>Codie Mullins</sub>](https://github.com/codiemullins)<br />[💻](https://github.com/paypal/downshift/commits?author=codiemullins "Code") [💡](#example-codiemullins "Examples") | [<img src="https://avatars3.githubusercontent.com/u/12202757?v=4" width="100px;"/><br /><sub>Mohammad Rajabifard</sub>](https://morajabi.me)<br />[📖](https://github.com/paypal/downshift/commits?author=morajabi "Documentation") [🤔](#ideas-morajabi "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/9488719?v=4" width="100px;"/><br /><sub>Frank Tan</sub>](https://github.com/tansongyang)<br />[💻](https://github.com/paypal/downshift/commits?author=tansongyang "Code") | [<img src="https://avatars3.githubusercontent.com/u/5093058?v=4" width="100px;"/><br /><sub>Kier Borromeo</sub>](https://kierb.com)<br />[💡](#example-srph "Examples") |
| [<img src="https://avatars1.githubusercontent.com/u/8969456?v=4" width="100px;"/><br /><sub>Paul Veevers</sub>](https://github.com/paul-veevers)<br />[💻](https://github.com/paypal/downshift/commits?author=paul-veevers "Code") |
2 changes: 1 addition & 1 deletion other/ssr/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ test('does not throw an error when server rendering', () => {
<input {...getInputProps({id: 'my-autocomplete-input'})} />
</div>
)}
</Downshift>
</Downshift>,
)
}).not.toThrow()
})
21 changes: 21 additions & 0 deletions src/__tests__/downshift.props.js
Original file line number Diff line number Diff line change
@@ -74,6 +74,27 @@ test('uses given environment', () => {
expect(environment.document.getElementById).toHaveBeenCalledTimes(1)
})

test('can override onOuterClick callback to maintain isOpen state', () => {
const children = () => <div />
const onOuterClick = jest.fn()
const {openMenu} = setup({children, onOuterClick})
openMenu()
mouseDownAndUp(document.body)
expect(onOuterClick).toHaveBeenCalledTimes(1)
expect(onOuterClick).toHaveBeenCalledWith(
expect.objectContaining({
// just verify that it's the controller object
isOpen: false,
getItemProps: expect.any(Function),
}),
)
})

function mouseDownAndUp(node) {
node.dispatchEvent(new window.MouseEvent('mousedown', {bubbles: true}))
node.dispatchEvent(new window.MouseEvent('mouseup', {bubbles: true}))
}

function setup({children = () => <div />, ...props} = {}) {
let renderArg
const childSpy = jest.fn(controllerArg => {
6 changes: 5 additions & 1 deletion src/downshift.js
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ class Downshift extends Component {
onStateChange: PropTypes.func,
onUserAction: PropTypes.func,
onClick: PropTypes.func,
onOuterClick: PropTypes.func,
itemCount: PropTypes.number,
id: PropTypes.string,
environment: PropTypes.shape({
@@ -63,6 +64,7 @@ class Downshift extends Component {
onStateChange: () => {},
onUserAction: () => {},
onChange: () => {},
onOuterClick: () => {},
environment:
typeof window === 'undefined' /* istanbul ignore next (ssr) */
? {}
@@ -671,7 +673,9 @@ class Downshift extends Component {
!this._rootNode.contains(event.target)) &&
this.getState().isOpen
) {
this.reset({type: Downshift.stateChangeTypes.mouseUp})
this.reset({type: Downshift.stateChangeTypes.mouseUp}, () =>
this.props.onOuterClick(this.getStateAndHelpers()),
)
}
}
this.props.environment.addEventListener('mousedown', onMouseDown)
5 changes: 1 addition & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -3,8 +3,5 @@
"jsx": "react",
"noUnusedLocals": true
},
"include": [
"test/**/*.tsx"
]
"include": ["test/**/*.tsx"]
}

0 comments on commit b9f8cf8

Please sign in to comment.