@@ -186,4 +186,225 @@ describe('ReactDOMFiber', () => {
186186 expect(firstNode.tagName).toBe('DIV');
187187 });
188188 }
189+
190+ if (ReactDOMFeatureFlags.useFiber) {
191+ it('should render portal children', () => {
192+ var portalContainer1 = document.createElement('div');
193+ var portalContainer2 = document.createElement('div');
194+
195+ var ops = [];
196+ class Child extends React.Component {
197+ componentDidMount() {
198+ ops.push(`${this.props.name} componentDidMount`);
199+ }
200+ componentDidUpdate() {
201+ ops.push(`${this.props.name} componentDidUpdate`);
202+ }
203+ componentWillUnmount() {
204+ ops.push(`${this.props.name} componentWillUnmount`);
205+ }
206+ render() {
207+ return <div>{this.props.name}</div>;
208+ }
209+ }
210+
211+ class Parent extends React.Component {
212+ componentDidMount() {
213+ ops.push(`Parent:${this.props.step} componentDidMount`);
214+ }
215+ componentDidUpdate() {
216+ ops.push(`Parent:${this.props.step} componentDidUpdate`);
217+ }
218+ componentWillUnmount() {
219+ ops.push(`Parent:${this.props.step} componentWillUnmount`);
220+ }
221+ render() {
222+ const {step} = this.props;
223+ return [
224+ <Child name={`normal[0]:${step}`} />,
225+ ReactDOM.unstable_createPortal(
226+ <Child name={`portal1[0]:${step}`} />,
227+ portalContainer1
228+ ),
229+ <Child name={`normal[1]:${step}`} />,
230+ ReactDOM.unstable_createPortal(
231+ [
232+ <Child name={`portal2[0]:${step}`} />,
233+ <Child name={`portal2[1]:${step}`} />,
234+ ],
235+ portalContainer2
236+ ),
237+ ];
238+ }
239+ }
240+
241+ ReactDOM.render(<Parent step="a" />, container);
242+ expect(portalContainer1.innerHTML).toBe('<div>portal1[0]:a</div>');
243+ expect(portalContainer2.innerHTML).toBe('<div>portal2[0]:a</div><div>portal2[1]:a</div>');
244+ expect(container.innerHTML).toBe('<div>normal[0]:a</div><div>normal[1]:a</div>');
245+ expect(ops).toEqual([
246+ 'normal[0]:a componentDidMount',
247+ 'portal1[0]:a componentDidMount',
248+ 'normal[1]:a componentDidMount',
249+ 'portal2[0]:a componentDidMount',
250+ 'portal2[1]:a componentDidMount',
251+ 'Parent:a componentDidMount',
252+ ]);
253+
254+ ops.length = 0;
255+ ReactDOM.render(<Parent step="b" />, container);
256+ expect(portalContainer1.innerHTML).toBe('<div>portal1[0]:b</div>');
257+ expect(portalContainer2.innerHTML).toBe('<div>portal2[0]:b</div><div>portal2[1]:b</div>');
258+ expect(container.innerHTML).toBe('<div>normal[0]:b</div><div>normal[1]:b</div>');
259+ expect(ops).toEqual([
260+ 'normal[0]:b componentDidUpdate',
261+ 'portal1[0]:b componentDidUpdate',
262+ 'normal[1]:b componentDidUpdate',
263+ 'portal2[0]:b componentDidUpdate',
264+ 'portal2[1]:b componentDidUpdate',
265+ 'Parent:b componentDidUpdate',
266+ ]);
267+
268+ ops.length = 0;
269+ ReactDOM.unmountComponentAtNode(container);
270+ expect(portalContainer1.innerHTML).toBe('');
271+ expect(portalContainer2.innerHTML).toBe('');
272+ expect(container.innerHTML).toBe('');
273+ expect(ops).toEqual([
274+ 'Parent:b componentWillUnmount',
275+ 'normal[0]:b componentWillUnmount',
276+ 'portal1[0]:b componentWillUnmount',
277+ 'normal[1]:b componentWillUnmount',
278+ 'portal2[0]:b componentWillUnmount',
279+ 'portal2[1]:b componentWillUnmount',
280+ ]);
281+ });
282+
283+ it('should pass portal context when rendering subtree elsewhere', () => {
284+ var portalContainer = document.createElement('div');
285+
286+ class Component extends React.Component {
287+ static contextTypes = {
288+ foo: React.PropTypes.string.isRequired,
289+ };
290+
291+ render() {
292+ return <div>{this.context.foo}</div>;
293+ }
294+ }
295+
296+ class Parent extends React.Component {
297+ static childContextTypes = {
298+ foo: React.PropTypes.string.isRequired,
299+ };
300+
301+ getChildContext() {
302+ return {
303+ foo: 'bar',
304+ };
305+ }
306+
307+ render() {
308+ return ReactDOM.unstable_createPortal(
309+ <Component />,
310+ portalContainer
311+ );
312+ }
313+ }
314+
315+ ReactDOM.render(<Parent />, container);
316+ expect(container.innerHTML).toBe('');
317+ expect(portalContainer.innerHTML).toBe('<div>bar</div>');
318+ });
319+
320+ it('should update portal context if it changes due to setState', () => {
321+ var portalContainer = document.createElement('div');
322+
323+ class Component extends React.Component {
324+ static contextTypes = {
325+ foo: React.PropTypes.string.isRequired,
326+ getFoo: React.PropTypes.func.isRequired,
327+ };
328+
329+ render() {
330+ return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
331+ }
332+ }
333+
334+ class Parent extends React.Component {
335+ static childContextTypes = {
336+ foo: React.PropTypes.string.isRequired,
337+ getFoo: React.PropTypes.func.isRequired,
338+ };
339+
340+ state = {
341+ bar: 'initial',
342+ };
343+
344+ getChildContext() {
345+ return {
346+ foo: this.state.bar,
347+ getFoo: () => this.state.bar,
348+ };
349+ }
350+
351+ render() {
352+ return ReactDOM.unstable_createPortal(
353+ <Component />,
354+ portalContainer
355+ );
356+ }
357+ }
358+
359+ var instance = ReactDOM.render(<Parent />, container);
360+ expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
361+ expect(container.innerHTML).toBe('');
362+ instance.setState({bar: 'changed'});
363+ expect(portalContainer.innerHTML).toBe('<div>changed-changed</div>');
364+ expect(container.innerHTML).toBe('');
365+ });
366+
367+ it('should update portal context if it changes due to re-render', () => {
368+ var portalContainer = document.createElement('div');
369+
370+ class Component extends React.Component {
371+ static contextTypes = {
372+ foo: React.PropTypes.string.isRequired,
373+ getFoo: React.PropTypes.func.isRequired,
374+ };
375+
376+ render() {
377+ return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
378+ }
379+ }
380+
381+ class Parent extends React.Component {
382+ static childContextTypes = {
383+ foo: React.PropTypes.string.isRequired,
384+ getFoo: React.PropTypes.func.isRequired,
385+ };
386+
387+ getChildContext() {
388+ return {
389+ foo: this.props.bar,
390+ getFoo: () => this.props.bar,
391+ };
392+ }
393+
394+ render() {
395+ return ReactDOM.unstable_createPortal(
396+ <Component />,
397+ portalContainer
398+ );
399+ }
400+ }
401+
402+ ReactDOM.render(<Parent bar="initial" />, container);
403+ expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
404+ expect(container.innerHTML).toBe('');
405+ ReactDOM.render(<Parent bar="changed" />, container);
406+ expect(portalContainer.innerHTML).toBe('<div>changed-changed</div>');
407+ expect(container.innerHTML).toBe('');
408+ });
409+ }
189410});
0 commit comments