Skip to content
This repository was archived by the owner on Mar 1, 2024. It is now read-only.

Commit 51c20f6

Browse files
committed
fix(Dropdown): Fix Dropdown should when user clicks outside of it
Creates a new ClickOutside helper component and uses it in Dropdown and NavItem Fixes #165
1 parent 6d5c58c commit 51c20f6

3 files changed

Lines changed: 77 additions & 10 deletions

File tree

src/components/Dropdown/Dropdown.react.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import DropdownMenu from "./DropdownMenu.react";
77
import DropdownItem from "./DropdownItem.react";
88
import DropdownItemDivider from "./DropdownItemDivider.react";
99

10+
import ClickOutside from "../../helpers/ClickOutside.react";
11+
1012
import { Manager } from "react-popper";
1113

1214
import type { Placement } from "react-popper";
@@ -245,10 +247,14 @@ class Dropdown extends React.Component<Props, State> {
245247

246248
return (
247249
<Manager>
248-
<div className={classes}>
249-
{trigger}
250-
{menu || children}
251-
</div>
250+
<ClickOutside onOutsideClick={() => this.setState({ isOpen: false })}>
251+
{({ setElementRef }) => (
252+
<div className={classes} ref={setElementRef}>
253+
{trigger}
254+
{menu || children}
255+
</div>
256+
)}
257+
</ClickOutside>
252258
</Manager>
253259
);
254260
}

src/components/Nav/NavItem.react.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import cn from "classnames";
44
import Nav from "../Nav";
55
import Dropdown from "../Dropdown";
66
import type { subNavItem } from "./Nav.react";
7+
import ClickOutside from "../../helpers/ClickOutside.react";
78

89
import { Manager, Reference } from "react-popper";
910
import type { Placement, ReferenceChildrenProps } from "react-popper";
@@ -132,13 +133,29 @@ class NavItem extends React.PureComponent<Props, State> {
132133

133134
const wrappedChildren =
134135
type === "div" ? (
135-
<div className={wrapperClasses} onClick={this._handleOnClick}>
136-
{childrenForAll}
137-
</div>
136+
<ClickOutside onOutsideClick={() => this.setState({ isOpen: false })}>
137+
{({ setElementRef }) => (
138+
<div
139+
className={wrapperClasses}
140+
onClick={this._handleOnClick}
141+
ref={setElementRef}
142+
>
143+
{childrenForAll}
144+
</div>
145+
)}
146+
</ClickOutside>
138147
) : (
139-
<li className={wrapperClasses} onClick={this._handleOnClick}>
140-
{childrenForAll}
141-
</li>
148+
<ClickOutside onOutsideClick={() => this.setState({ isOpen: false })}>
149+
{({ setElementRef }) => (
150+
<li
151+
className={wrapperClasses}
152+
onClick={this._handleOnClick}
153+
ref={setElementRef}
154+
>
155+
{childrenForAll}
156+
</li>
157+
)}
158+
</ClickOutside>
142159
);
143160

144161
return hasSubNav ? <Manager>{wrappedChildren}</Manager> : wrappedChildren;

src/helpers/ClickOutside.react.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// @flow
2+
3+
import * as React from "react";
4+
5+
type Props = {|
6+
+children: ({| +setElementRef: (el: ?HTMLElement) => mixed |}) => React.Node,
7+
+onOutsideClick: () => void,
8+
|};
9+
10+
/**
11+
* A helper to help you do something when a user clicks outside of a component
12+
*/
13+
class ClickOutside extends React.PureComponent<Props, void> {
14+
elementRef: ?HTMLElement;
15+
16+
componentDidMount = () => {
17+
document.addEventListener("mousedown", this.handleOutsideOnClick, false);
18+
};
19+
20+
componentWillUnmount = () => {
21+
document.removeEventListener("mousedown", this.handleOutsideOnClick, false);
22+
};
23+
24+
setElementRef = (el: ?HTMLElement): mixed => {
25+
if (el) this.elementRef = el;
26+
};
27+
28+
isOutsideClick = (target: mixed) =>
29+
this.elementRef &&
30+
target instanceof Node &&
31+
!this.elementRef.contains(target);
32+
33+
handleOutsideOnClick: MouseEventListener = ({ target }) => {
34+
if (this.isOutsideClick(target)) this.props.onOutsideClick();
35+
return;
36+
};
37+
38+
render() {
39+
const { children } = this.props;
40+
return children({ setElementRef: this.setElementRef });
41+
}
42+
}
43+
44+
export default ClickOutside;

0 commit comments

Comments
 (0)