Skip to content

Commit aedad0b

Browse files
committed
Allows nesting root close wrappers
1 parent c3613fb commit aedad0b

File tree

2 files changed

+79
-8
lines changed

2 files changed

+79
-8
lines changed

src/RootCloseWrapper.js

+21-8
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ import ownerDocument from './utils/ownerDocument';
66
// TODO: Consider using an ES6 symbol here, once we use babel-runtime.
77
const CLICK_WAS_INSIDE = '__click_was_inside';
88

9-
function suppressRootClose(event) {
10-
// Tag the native event to prevent the root close logic on document click.
11-
// This seems safer than using event.nativeEvent.stopImmediatePropagation(),
12-
// which is only supported in IE >= 9.
13-
event.nativeEvent[CLICK_WAS_INSIDE] = true;
9+
let counter = 0;
10+
11+
function getSuppressRootClose() {
12+
let id = CLICK_WAS_INSIDE + '_' + counter++;
13+
return {
14+
id,
15+
suppressRootClose(event) {
16+
// Tag the native event to prevent the root close logic on document click.
17+
// This seems safer than using event.nativeEvent.stopImmediatePropagation(),
18+
// which is only supported in IE >= 9.
19+
event.nativeEvent[id] = true;
20+
}
21+
};
1422
}
1523

1624
export default class RootCloseWrapper extends React.Component {
@@ -19,6 +27,11 @@ export default class RootCloseWrapper extends React.Component {
1927

2028
this.handleDocumentClick = this.handleDocumentClick.bind(this);
2129
this.handleDocumentKeyUp = this.handleDocumentKeyUp.bind(this);
30+
31+
let { id, suppressRootClose } = getSuppressRootClose();
32+
33+
this._suppressRootId = id;
34+
this._suppressRootClosehHandler = suppressRootClose;
2235
}
2336

2437
bindRootCloseHandlers() {
@@ -33,7 +46,7 @@ export default class RootCloseWrapper extends React.Component {
3346

3447
handleDocumentClick(e) {
3548
// This is now the native event.
36-
if (e[CLICK_WAS_INSIDE]) {
49+
if (e[this._suppressRootId]) {
3750
return;
3851
}
3952

@@ -66,14 +79,14 @@ export default class RootCloseWrapper extends React.Component {
6679

6780
if (noWrap) {
6881
return React.cloneElement(child, {
69-
onClick: createChainedFunction(suppressRootClose, child.props.onClick)
82+
onClick: createChainedFunction(this._suppressRootClosehHandler, child.props.onClick)
7083
});
7184
}
7285

7386
// Wrap the child in a new element, so the child won't have to handle
7487
// potentially combining multiple onClick listeners.
7588
return (
76-
<div onClick={suppressRootClose}>
89+
<div onClick={this._suppressRootClosehHandler}>
7790
{child}
7891
</div>
7992
);

test/RootCloseSpec.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import RootCloseWrapper from '../src/RootCloseWrapper';
4+
import { render } from './helpers';
5+
import simulant from 'simulant';
6+
7+
describe('RootCloseWrapper', function () {
8+
let mountPoint;
9+
10+
beforeEach(()=>{
11+
mountPoint = document.createElement('div');
12+
document.body.appendChild(mountPoint);
13+
});
14+
15+
afterEach(function () {
16+
ReactDOM.unmountComponentAtNode(mountPoint);
17+
document.body.removeChild(mountPoint);
18+
});
19+
20+
it('should close when clicked outside', () => {
21+
let spy = sinon.spy();
22+
render(
23+
<RootCloseWrapper onRootClose={spy}>
24+
<div id='my-div'>hello there</div>
25+
</RootCloseWrapper>
26+
, mountPoint);
27+
28+
simulant.fire(document.getElementById('my-div'), 'click');
29+
30+
expect(spy).to.not.have.been.called;
31+
32+
simulant.fire(document.body, 'click');
33+
34+
expect(spy).to.have.been.calledOnce;
35+
});
36+
37+
it('should close when inside another RootCloseWrapper', () => {
38+
let outerSpy = sinon.spy();
39+
let innerSpy = sinon.spy();
40+
41+
render(
42+
<RootCloseWrapper onRootClose={outerSpy}>
43+
<div>
44+
<div id='my-div'>hello there</div>
45+
<RootCloseWrapper onRootClose={innerSpy}>
46+
<div id='my-other-div'>hello there</div>
47+
</RootCloseWrapper>
48+
</div>
49+
</RootCloseWrapper>
50+
, mountPoint);
51+
52+
simulant.fire(document.getElementById('my-div'), 'click');
53+
54+
expect(outerSpy).to.have.not.been.called;
55+
expect(innerSpy).to.have.been.calledOnce;
56+
});
57+
58+
});

0 commit comments

Comments
 (0)