Skip to content

Commit 4f19301

Browse files
jjavierdguezasCédric Delpoux
authored andcommitted
Hide suggestions when clicking outside them (#29)
Add workaround to hide suggestions when clicking outside the component solve issue #28
1 parent 64c78b2 commit 4f19301

File tree

4 files changed

+95
-4
lines changed

4 files changed

+95
-4
lines changed

.eslintrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@
1111
"plugin:jest/recommended",
1212
"plugin:react/recommended",
1313
"prettier"
14-
]
14+
],
15+
"rules": {
16+
"react/no-deprecated": "warn"
17+
}
1518
}

src/components/List/index.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ const Wrapper = styled.div`
1818
`
1919

2020
class List extends React.Component {
21+
constructor(props) {
22+
super(props)
23+
24+
this.handleMouseEnter = this.handleMouseEnter.bind(this)
25+
this.handleMouseLeave = this.handleMouseLeave.bind(this)
26+
}
27+
2128
renderDefault() {
2229
const {
2330
customRender,
@@ -29,7 +36,10 @@ class List extends React.Component {
2936

3037
if (items.length > 0) {
3138
return (
32-
<Wrapper>
39+
<Wrapper
40+
onMouseEnter={this.handleMouseEnter}
41+
onMouseLeave={this.handleMouseLeave}
42+
>
3343
{items.map((item, index) => (
3444
<ListItem
3545
key={index}
@@ -45,7 +55,10 @@ class List extends React.Component {
4555

4656
if (textNoResults || customRender) {
4757
return (
48-
<Wrapper>
58+
<Wrapper
59+
onMouseEnter={this.handleMouseEnter}
60+
onMouseLeave={this.handleMouseLeave}
61+
>
4962
<ListItem customRender={customRender} textNoResults={textNoResults} />
5063
</Wrapper>
5164
)
@@ -54,6 +67,20 @@ class List extends React.Component {
5467
return null
5568
}
5669

70+
handleMouseEnter() {
71+
const {onFocusChange} = this.props
72+
if (onFocusChange) {
73+
onFocusChange(true)
74+
}
75+
}
76+
77+
handleMouseLeave() {
78+
const {onFocusChange} = this.props
79+
if (onFocusChange) {
80+
onFocusChange(false)
81+
}
82+
}
83+
5784
render() {
5885
const {customContainerRender, items} = this.props
5986
return customContainerRender
@@ -80,6 +107,7 @@ List.propTypes = {
80107
PropTypes.instanceOf(ListItem),
81108
]),
82109
onSelect: PropTypes.func,
110+
onFocusChange: PropTypes.func,
83111
customContainerRender: PropTypes.func,
84112
customRender: PropTypes.func,
85113
textNoResults: PropTypes.string,

src/components/List/index.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,18 @@ const ListCustomItemFixture = (
3333
<List customRender={prediction => prediction && prediction.description} />
3434
)
3535

36+
const onFocusChange = jest.fn()
37+
const ListMouseEventsFixture = (
38+
<List items={[item]} onFocusChange={onFocusChange} />
39+
)
40+
3641
describe("Suggest", () => {
3742
it("renders", () => {
3843
mount(ListFixture)
3944
mount(ListEmptyFixture)
4045
mount(ListCustomFixture)
4146
mount(ListCustomItemFixture)
47+
mount(ListMouseEventsFixture)
4248
})
4349

4450
it("has one child", () => {
@@ -59,4 +65,30 @@ describe("Suggest", () => {
5965
.simulate("click")
6066
expect(onSelect).toHaveBeenCalled()
6167
})
68+
69+
it("calls onFocusChanged(true) only once when mouse enters", () => {
70+
onFocusChange.mockClear()
71+
const list = shallow(ListMouseEventsFixture)
72+
list.simulate("mouseenter")
73+
expect(onFocusChange).toHaveBeenCalledTimes(1)
74+
expect(onFocusChange).toHaveBeenCalledWith(true)
75+
})
76+
77+
it("calls onFocusChanged(false) only once when mouse leaves", () => {
78+
onFocusChange.mockClear()
79+
const list = shallow(ListMouseEventsFixture)
80+
list.simulate("mouseleave")
81+
expect(onFocusChange).toHaveBeenCalledTimes(1)
82+
expect(onFocusChange).toHaveBeenCalledWith(false)
83+
})
84+
85+
it("prevents exception when mouse enters and onFocusChanged is null", () => {
86+
const list = shallow(ListFixture)
87+
list.simulate("mouseenter")
88+
})
89+
90+
it("prevents exception when mouse leaves and onFocusChanged is null", () => {
91+
const list = shallow(ListFixture)
92+
list.simulate("mouseleave")
93+
})
6294
})

src/index.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* global document */
12
import PropTypes from "prop-types"
23
import React from "react"
34
import styled from "styled-components"
@@ -20,10 +21,19 @@ class GooglePlacesSuggest extends React.Component {
2021
}
2122

2223
this.handleKeyDown = this.handleKeyDown.bind(this)
24+
this.onFocusChange = this.onFocusChange.bind(this)
25+
this.handleDOMClick = this.handleDOMClick.bind(this)
2326
}
2427

28+
hasFocus = false
29+
2530
componentWillMount() {
2631
this.updatePredictions(this.props.autocompletionRequest)
32+
document.addEventListener("click", this.handleDOMClick)
33+
}
34+
35+
componentWillUnmount() {
36+
document.removeEventListener("click", this.handleDOMClick)
2737
}
2838

2939
componentWillReceiveProps(nextProps) {
@@ -43,6 +53,7 @@ class GooglePlacesSuggest extends React.Component {
4353
predictions: [],
4454
},
4555
() => {
56+
this.hasFocus = false
4657
this.geocodePrediction(suggest.description, result => {
4758
onSelectSuggest(result, suggest)
4859
})
@@ -54,7 +65,13 @@ class GooglePlacesSuggest extends React.Component {
5465
const {googleMaps} = this.props
5566
const autocompleteService = new googleMaps.places.AutocompleteService()
5667
if (!autocompletionRequest || !autocompletionRequest.input) {
57-
this.setState({open: false, predictions: []})
68+
this.setState(
69+
{
70+
open: false,
71+
predictions: [],
72+
},
73+
() => (this.hasFocus = false)
74+
)
5875
return
5976
}
6077

@@ -115,6 +132,16 @@ class GooglePlacesSuggest extends React.Component {
115132
this.setState({focusedPredictionIndex: index})
116133
}
117134

135+
onFocusChange(val) {
136+
this.hasFocus = val
137+
}
138+
139+
handleDOMClick() {
140+
if (!this.hasFocus && this.state.open) {
141+
this.setState({open: false})
142+
}
143+
}
144+
118145
render() {
119146
const {focusedPredictionIndex, open, predictions} = this.state
120147
const {
@@ -134,6 +161,7 @@ class GooglePlacesSuggest extends React.Component {
134161
customRender={customRender}
135162
onSelect={suggest => this.handleSelectPrediction(suggest)}
136163
textNoResults={textNoResults}
164+
onFocusChange={this.onFocusChange}
137165
/>
138166
)}
139167
</Wrapper>

0 commit comments

Comments
 (0)