Skip to content

Commit c5b148d

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](https://github.com/restrry/react-modal/commit/b9a34155bf37298e99922f77dec8e27292db06d9).
1 parent b67ad54 commit c5b148d

File tree

4 files changed

+24
-59
lines changed

4 files changed

+24
-59
lines changed

README.md

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

171171
### Appended to custom node
172172

173-
You can choose an element for the modal to be appended to, rather than using
174-
body tag. To do this, provide a function to `parentSelector` prop that return
175-
the element to be used.
176-
177-
```jsx
178-
179-
function getParent() {
180-
return document.querySelector('#root');
181-
}
182-
183-
<Modal
184-
...
185-
parentSelector={getParent}
186-
...
187-
>
188-
<p>Modal Content.</p>
189-
</Modal>
190-
```
173+
`parentSelector` is now deprecated. `<Modal />` can be appended on any place
174+
and it will correctly manage it's clean up.
191175

192176
### Body class
193177

docs/README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,6 @@ import ReactModal from 'react-modal';
7878
String indicating the role of the modal, allowing the 'dialog' role to be applied if desired.
7979
*/
8080
role="dialog"
81-
/*
82-
Function that will be called to get the parent element that the modal will be attached to.
83-
*/
84-
parentSelector={() => document.body}
8581
/*
8682
Additional aria attributes (optional).
8783
*/

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 & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ export const bodyOpenClassName = 'ReactModal__Body--open';
1010

1111
const renderSubtreeIntoContainer = ReactDOM.unstable_renderSubtreeIntoContainer;
1212

13-
function getParentElement(parentSelector) {
14-
return parentSelector();
15-
}
16-
1713
export default class Modal extends Component {
1814
static setAppElement(element) {
1915
ariaAppHider.setElement(element);
@@ -52,7 +48,6 @@ export default class Modal extends Component {
5248
closeTimeoutMS: PropTypes.number,
5349
ariaHideApp: PropTypes.bool,
5450
shouldCloseOnOverlayClick: PropTypes.bool,
55-
parentSelector: PropTypes.func,
5651
aria: PropTypes.object,
5752
role: PropTypes.string,
5853
contentLabel: PropTypes.string.isRequired
@@ -65,8 +60,7 @@ export default class Modal extends Component {
6560
bodyOpenClassName,
6661
ariaHideApp: true,
6762
closeTimeoutMS: 0,
68-
shouldCloseOnOverlayClick: true,
69-
parentSelector() { return document.body; }
63+
shouldCloseOnOverlayClick: true
7064
};
7165

7266
static defaultStyles = {
@@ -95,12 +89,6 @@ export default class Modal extends Component {
9589
};
9690

9791
componentDidMount() {
98-
this.node = document.createElement('div');
99-
this.node.className = this.props.portalClassName;
100-
101-
const parent = getParentElement(this.props.parentSelector);
102-
parent.appendChild(this.node);
103-
10492
this.renderPortal(this.props);
10593
}
10694

@@ -109,14 +97,6 @@ export default class Modal extends Component {
10997
// Stop unnecessary renders if modal is remaining closed
11098
if (!this.props.isOpen && !isOpen) return;
11199

112-
const currentParent = getParentElement(this.props.parentSelector);
113-
const newParent = getParentElement(newProps.parentSelector);
114-
115-
if (newParent !== currentParent) {
116-
currentParent.removeChild(this.node);
117-
newParent.appendChild(this.node);
118-
}
119-
120100
this.renderPortal(newProps);
121101
}
122102

@@ -146,10 +126,13 @@ export default class Modal extends Component {
146126
}
147127
}
148128

129+
setNodeRef = ref => {
130+
this.node = ref;
131+
}
132+
149133
removePortal = () => {
150134
ReactDOM.unmountComponentAtNode(this.node);
151-
const parent = getParentElement(this.props.parentSelector);
152-
parent.removeChild(this.node);
135+
this.portal = null;
153136
}
154137

155138
renderPortal = props => {
@@ -159,6 +142,10 @@ export default class Modal extends Component {
159142
}
160143

161144
render() {
162-
return null;
145+
return (
146+
<div
147+
ref={this.setNodeRef}
148+
className={this.props.portalClassName} />
149+
);
163150
}
164151
}

0 commit comments

Comments
 (0)