Skip to content

Commit efb0954

Browse files
committed
[feature] deprecate getParentElement in favor of a local element.
This patch will give us the ability to place the modal in any place in the react tree. Each modal can handle its own clean up. Test case was written by @restrry [b9a3415](mshustov@b9a3415).
1 parent f5d95e2 commit efb0954

File tree

4 files changed

+24
-63
lines changed

4 files changed

+24
-63
lines changed

README.md

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -124,24 +124,8 @@ object will apply to all instances of the modal.
124124

125125
### Appended to custom node
126126

127-
You can choose an element for the modal to be appended to, rather than using
128-
body tag. To do this, provide a function to `parentSelector` prop that return
129-
the element to be used.
130-
131-
```jsx
132-
133-
function getParent() {
134-
return document.querySelector('#root');
135-
}
136-
137-
<Modal
138-
...
139-
parentSelector={getParent}
140-
...
141-
>
142-
<p>Modal Content.</p>
143-
</Modal>
144-
```
127+
`parentSelector` is now deprecated. `<Modal />` can be appended on any place
128+
and it will correctly manage it's clean up.
145129

146130
### Body class
147131

docs/README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@ import ReactModal from 'react-modal';
6969
String indicating the role of the modal, allowing the 'dialog' role to be applied if desired.
7070
*/
7171
role="dialog"
72-
/*
73-
Function that will be called to get the parent element that the modal will be attached to.
74-
*/
75-
parentSelector={() => document.body}
7672
/>
7773
```
7874

specs/Modal.spec.js

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,24 @@ describe('State', () => {
5454
ReactDOM.unmountComponentAtNode(node);
5555
});
5656

57-
it('renders into the body, not in context', () => {
58-
const node = document.createElement('div');
57+
it('renders in context, never in document.body', function() {
58+
var node = document.createElement('div');
59+
var realRef = null;
5960
class App extends Component {
6061
render() {
61-
return (
62-
<div>
63-
<Modal isOpen>
64-
<span>hello</span>
65-
</Modal>
62+
return (
63+
<div ref={ref => { realRef = ref; }}>
64+
<Modal isOpen={true}>
65+
<span>hello</span>
66+
</Modal>
6667
</div>
67-
);
68+
);
6869
}
6970
}
7071
Modal.setAppElement(node);
7172
ReactDOM.render(<App />, node);
72-
expect(
73-
document.body.querySelector('.ReactModalPortal').parentNode
74-
).toEqual(
75-
document.body
76-
);
73+
var modalParent = node.querySelector('.ReactModalPortal').parentNode;
74+
expect(modalParent).toEqual(realRef);
7775
ReactDOM.unmountComponentAtNode(node);
7876
});
7977

src/components/Modal.js

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ const renderSubtreeIntoContainer = ReactDOM.unstable_renderSubtreeIntoContainer;
1313
const SafeHTMLElement = EE.canUseDOM ? window.HTMLElement : {};
1414
const AppElement = EE.canUseDOM ? document.body : { appendChild() {} };
1515

16-
function getParentElement(parentSelector) {
17-
return parentSelector();
18-
}
19-
2016
export default class Modal extends Component {
2117
static setAppElement(element) {
2218
ariaAppHider.setElement(element || AppElement);
@@ -55,7 +51,6 @@ export default class Modal extends Component {
5551
closeTimeoutMS: PropTypes.number,
5652
ariaHideApp: PropTypes.bool,
5753
shouldCloseOnOverlayClick: PropTypes.bool,
58-
parentSelector: PropTypes.func,
5954
role: PropTypes.string,
6055
contentLabel: PropTypes.string.isRequired
6156
};
@@ -67,8 +62,7 @@ export default class Modal extends Component {
6762
bodyOpenClassName: 'ReactModal__Body--open',
6863
ariaHideApp: true,
6964
closeTimeoutMS: 0,
70-
shouldCloseOnOverlayClick: true,
71-
parentSelector() { return document.body; }
65+
shouldCloseOnOverlayClick: true
7266
};
7367

7468
static defaultStyles = {
@@ -97,13 +91,8 @@ export default class Modal extends Component {
9791
};
9892

9993
componentDidMount() {
100-
this.node = document.createElement('div');
101-
this.node.className = this.props.portalClassName;
102-
10394
if (this.props.isOpen) refCount.add(this);
10495

105-
const parent = getParentElement(this.props.parentSelector);
106-
parent.appendChild(this.node);
10796
this.renderPortal(this.props);
10897
}
10998

@@ -114,23 +103,10 @@ export default class Modal extends Component {
114103

115104
if (isOpen) refCount.add(this);
116105
if (!isOpen) refCount.remove(this);
117-
const currentParent = getParentElement(this.props.parentSelector);
118-
const newParent = getParentElement(newProps.parentSelector);
119-
120-
if (newParent !== currentParent) {
121-
currentParent.removeChild(this.node);
122-
newParent.appendChild(this.node);
123-
}
124106

125107
this.renderPortal(newProps);
126108
}
127109

128-
componentWillUpdate(newProps) {
129-
if (newProps.portalClassName !== this.props.portalClassName) {
130-
this.node.className = newProps.portalClassName;
131-
}
132-
}
133-
134110
componentWillUnmount() {
135111
if (!this.node) return;
136112

@@ -157,10 +133,13 @@ export default class Modal extends Component {
157133
}
158134
}
159135

136+
setNodeRef = ref => {
137+
this.node = ref;
138+
}
139+
160140
removePortal = () => {
161141
ReactDOM.unmountComponentAtNode(this.node);
162-
const parent = getParentElement(this.props.parentSelector);
163-
parent.removeChild(this.node);
142+
this.portal = null;
164143

165144
if (refCount.count() === 0) {
166145
elementClass(document.body).remove(this.props.bodyOpenClassName);
@@ -184,6 +163,10 @@ export default class Modal extends Component {
184163
}
185164

186165
render() {
187-
return null;
166+
return (
167+
<div
168+
ref={this.setNodeRef}
169+
className={this.props.portalClassName} />
170+
);
188171
}
189172
}

0 commit comments

Comments
 (0)