Skip to content

Commit 4d2ff12

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 73db6dd commit 4d2ff12

File tree

4 files changed

+23
-58
lines changed

4 files changed

+23
-58
lines changed

README.md

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

172172
### Appended to custom node
173173

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

193177
### Body class
194178

docs/README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ import ReactModal from 'react-modal';
8383
String indicating the role of the modal, allowing the 'dialog' role to be applied if desired.
8484
*/
8585
role="dialog"
86-
/*
87-
Function that will be called to get the parent element that the modal will be attached to.
88-
*/
89-
parentSelector={() => document.body}
9086
/*
9187
Additional aria attributes (optional).
9288
*/

specs/Modal.spec.js

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,24 @@ export default () => {
6161
ReactDOM.unmountComponentAtNode(node);
6262
});
6363

64-
it('renders into the body, not in context', () => {
65-
const node = document.createElement('div');
64+
it('renders in context, never in document.body', function() {
65+
var node = document.createElement('div');
66+
var realRef = null;
6667
class App extends Component {
6768
render() {
68-
return (
69-
<div>
70-
<Modal isOpen>
71-
<span>hello</span>
72-
</Modal>
69+
return (
70+
<div ref={ref => { realRef = ref; }}>
71+
<Modal isOpen={true}>
72+
<span>hello</span>
73+
</Modal>
7374
</div>
74-
);
75+
);
7576
}
7677
}
7778
Modal.setAppElement(node);
7879
ReactDOM.render(<App />, node);
79-
document.body.querySelector(
80-
'.ReactModalPortal'
81-
).parentNode.should.be.eql(
82-
document.body
83-
);
80+
var modalParent = node.querySelector('.ReactModalPortal').parentNode;
81+
expect(modalParent).toEqual(realRef);
8482
ReactDOM.unmountComponentAtNode(node);
8583
});
8684

src/components/Modal.js

Lines changed: 10 additions & 23 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);
@@ -53,7 +49,6 @@ export default class Modal extends Component {
5349
ariaHideApp: PropTypes.bool,
5450
shouldFocusAfter: PropTypes.bool,
5551
shouldCloseOnOverlayClick: PropTypes.bool,
56-
parentSelector: PropTypes.func,
5752
aria: PropTypes.object,
5853
role: PropTypes.string,
5954
contentLabel: PropTypes.string
@@ -68,7 +63,6 @@ export default class Modal extends Component {
6863
closeTimeoutMS: 0,
6964
shouldFocusAfterRender: true,
7065
shouldCloseOnOverlayClick: true,
71-
parentSelector() { return document.body; }
7266
};
7367

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

9993
componentDidMount() {
100-
this.node = document.createElement('div');
101-
this.node.className = this.props.portalClassName;
102-
103-
const parent = getParentElement(this.props.parentSelector);
104-
parent.appendChild(this.node);
105-
10694
this.renderPortal(this.props);
10795
}
10896

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

114-
const currentParent = getParentElement(this.props.parentSelector);
115-
const newParent = getParentElement(newProps.parentSelector);
116-
117-
if (newParent !== currentParent) {
118-
currentParent.removeChild(this.node);
119-
newParent.appendChild(this.node);
120-
}
121-
122102
this.renderPortal(newProps);
123103
}
124104

@@ -148,10 +128,13 @@ export default class Modal extends Component {
148128
}
149129
}
150130

131+
setNodeRef = ref => {
132+
this.node = ref;
133+
}
134+
151135
removePortal = () => {
152136
ReactDOM.unmountComponentAtNode(this.node);
153-
const parent = getParentElement(this.props.parentSelector);
154-
parent.removeChild(this.node);
137+
this.portal = null;
155138
}
156139

157140
renderPortal = props => {
@@ -161,6 +144,10 @@ export default class Modal extends Component {
161144
}
162145

163146
render() {
164-
return null;
147+
return (
148+
<div
149+
ref={this.setNodeRef}
150+
className={this.props.portalClassName} />
151+
);
165152
}
166153
}

0 commit comments

Comments
 (0)