Skip to content

Commit e45d4f6

Browse files
authored
Merge pull request #22 from ooade/beta
React portals support
2 parents 3e18fcf + f042568 commit e45d4f6

File tree

5 files changed

+90
-28
lines changed

5 files changed

+90
-28
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ yarn add react-click-away-listener
2020
- It's quite small in size.
2121
- It's built with TypeScript.
2222
- It supports both Mouse and Touch Events.
23+
- It works with Portals.
2324

2425
## Usage
2526

__tests__/index.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
2+
import ReactDOM from 'react-dom';
23
import { render, fireEvent } from '@testing-library/react';
34
import ClickAwayListener from '../src';
45

@@ -138,4 +139,44 @@ describe('ClickAway Listener', () => {
138139
fireEvent.click(getByTestId('some-other-button-two'));
139140
expect(fakeHandleClick2).toBeCalledTimes(7);
140141
});
142+
143+
it('should work with Portals', () => {
144+
const fakeHandleClick = jest.fn();
145+
let modalRoot = document.getElementById('modal-root');
146+
if (!modalRoot) {
147+
modalRoot = document.createElement('div');
148+
modalRoot.setAttribute('id', 'modal-root');
149+
document.body.appendChild(modalRoot);
150+
}
151+
152+
const Portal = ({ children }) => {
153+
const modalRoot = document.getElementById('modal-root');
154+
const element = document.createElement('div');
155+
156+
useEffect(() => {
157+
modalRoot.appendChild(element);
158+
159+
return () => {
160+
modalRoot.removeChild(element);
161+
};
162+
});
163+
164+
return ReactDOM.createPortal(children, element);
165+
};
166+
167+
const { getByText } = render(
168+
<React.Fragment>
169+
<ClickAwayListener onClickAway={fakeHandleClick}>
170+
<Portal>
171+
<div>Hello World</div>
172+
</Portal>
173+
</ClickAwayListener>
174+
<button>A button</button>
175+
</React.Fragment>
176+
);
177+
178+
fireEvent.click(getByText(/A button/i));
179+
fireEvent.click(getByText(/Hello World/i));
180+
expect(fakeHandleClick).toBeCalledTimes(1);
181+
});
141182
});

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-click-away-listener",
3-
"version": "1.4.4",
3+
"version": "1.5.0",
44
"description": "A simple click away listener built with React Hooks",
55
"main": "dist/react-click-away-listener.js",
66
"module": "dist/react-click-away-listener.es.js",
@@ -25,6 +25,9 @@
2525
],
2626
"author": "Ademola Adegbuyi <[email protected]>",
2727
"license": "MIT",
28+
"repository": {
29+
"url": "https://github.com/ooade/react-click-away-listener"
30+
},
2831
"devDependencies": {
2932
"@babel/preset-env": "^7.3.1",
3033
"@babel/preset-react": "^7.0.0",
@@ -46,16 +49,16 @@
4649
"jest": "^24.9.0",
4750
"npm-run-all": "^4.1.5",
4851
"prettier": "^2.0.5",
49-
"react": "^16.11.0",
50-
"react-dom": "^16.11.0",
52+
"react": "^17.0.1",
53+
"react-dom": "^17.0.1",
5154
"rimraf": "^3.0.2",
5255
"rollup": "^1.26.3",
5356
"rollup-plugin-terser": "^7.0.0",
5457
"typescript": "^3.6.4"
5558
},
5659
"peerDependencies": {
57-
"react": "^16.11.0",
58-
"react-dom": "^16.11.0"
60+
"react": "^17.0.1",
61+
"react-dom": "^17.0.1"
5962
},
6063
"jest": {
6164
"collectCoverageFrom": [

src/index.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
import React, { useEffect, useRef, FunctionComponent } from 'react';
1+
import React, {
2+
useRef,
3+
useEffect,
4+
MutableRefObject,
5+
FunctionComponent
6+
} from 'react';
27

38
type MouseEvents = 'click' | 'mousedown' | 'mouseup';
49
type TouchEvents = 'touchstart' | 'touchend';
10+
type Events = MouseEvent | TouchEvent;
511

612
interface Props extends React.HTMLAttributes<HTMLElement> {
7-
onClickAway: (event: MouseEvent | TouchEvent) => void;
13+
onClickAway: (event: Events) => void;
814
mouseEvent?: MouseEvents;
915
touchEvent?: TouchEvents;
1016
as?: React.ElementType;
@@ -17,11 +23,19 @@ const ClickAwayListener: FunctionComponent<Props> = ({
1723
touchEvent = 'touchend',
1824
...props
1925
}) => {
20-
let node = useRef<HTMLElement>(null);
26+
const node = useRef<HTMLElement>(null);
27+
const bubbledEvent: MutableRefObject<Events | null> = useRef(null);
28+
29+
const handleBubbledEvents = (event: Events | null) => {
30+
bubbledEvent.current = event;
31+
};
2132

2233
useEffect(() => {
23-
const handleEvents = (event: MouseEvent | TouchEvent): void => {
24-
if (node.current && node.current.contains(event.target as Node)) {
34+
const handleEvents = (event: Events): void => {
35+
if (
36+
(node.current && node.current.contains(event.target as Node)) ||
37+
(bubbledEvent.current && bubbledEvent.current.target === event.target)
38+
) {
2539
return;
2640
}
2741

@@ -37,7 +51,12 @@ const ClickAwayListener: FunctionComponent<Props> = ({
3751
};
3852
}, [mouseEvent, onClickAway, touchEvent]);
3953

40-
return React.createElement(as, { ref: node, ...props });
54+
return React.createElement(as, {
55+
ref: node,
56+
onClick: handleBubbledEvents,
57+
onTouchEnd: handleBubbledEvents,
58+
...props
59+
});
4160
};
4261

4362
export default ClickAwayListener;

yarn.lock

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4420,7 +4420,7 @@ prompts@^2.0.1:
44204420
kleur "^3.0.3"
44214421
sisteransi "^1.0.4"
44224422

4423-
prop-types@^15.6.2, prop-types@^15.7.2:
4423+
prop-types@^15.7.2:
44244424
version "15.7.2"
44254425
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
44264426
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -4459,29 +4459,27 @@ randombytes@^2.1.0:
44594459
dependencies:
44604460
safe-buffer "^5.1.0"
44614461

4462-
react-dom@^16.11.0:
4463-
version "16.13.1"
4464-
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
4465-
integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
4462+
react-dom@^17.0.1:
4463+
version "17.0.1"
4464+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
4465+
integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==
44664466
dependencies:
44674467
loose-envify "^1.1.0"
44684468
object-assign "^4.1.1"
4469-
prop-types "^15.6.2"
4470-
scheduler "^0.19.1"
4469+
scheduler "^0.20.1"
44714470

44724471
react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4:
44734472
version "16.13.1"
44744473
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
44754474
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
44764475

4477-
react@^16.11.0:
4478-
version "16.13.1"
4479-
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
4480-
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
4476+
react@^17.0.1:
4477+
version "17.0.1"
4478+
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
4479+
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
44814480
dependencies:
44824481
loose-envify "^1.1.0"
44834482
object-assign "^4.1.1"
4484-
prop-types "^15.6.2"
44854483

44864484
read-pkg-up@^2.0.0:
44874485
version "2.0.0"
@@ -4806,10 +4804,10 @@ sax@^1.2.4:
48064804
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
48074805
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
48084806

4809-
scheduler@^0.19.1:
4810-
version "0.19.1"
4811-
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
4812-
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
4807+
scheduler@^0.20.1:
4808+
version "0.20.1"
4809+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c"
4810+
integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==
48134811
dependencies:
48144812
loose-envify "^1.1.0"
48154813
object-assign "^4.1.1"

0 commit comments

Comments
 (0)