@samitouri / QOS-React-2 / commits / 9fd760ce75

Add disable <textarea/> children flag (#17874)

Dominic Gannaway committed Jan 20, 2020 at 15:12 UTC 9fd760ce75fd76716888997852ca85394eeab49a
9 files changed +186 -88
packages/react-dom/src/__tests__/ReactDOMTextarea-test.js
+161 -75
@@ -19,6 +19,8 @@ describe('ReactDOMTextarea', () => {
19
20 let renderTextarea;
21
22 + const ReactFeatureFlags = require('shared/ReactFeatureFlags');
23 +
24 beforeEach(() => {
25 jest.resetModules();
26
@@ -287,23 +289,58 @@ describe('ReactDOMTextarea', () => {
289 }
290 });
291
290 - it('should treat children like `defaultValue`', () => {
291 - const container = document.createElement('div');
292 - let stub = <textarea>giraffe</textarea>;
293 - let node;
292 + if (ReactFeatureFlags.disableTextareaChildren) {
293 + it('should ignore children content', () => {
294 + const container = document.createElement('div');
295 + let stub = <textarea>giraffe</textarea>;
296 + let node;
297
295 - expect(() => {
296 - node = renderTextarea(stub, container);
297 - }).toErrorDev(
298 - 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
299 - );
298 + expect(() => {
299 + node = renderTextarea(stub, container);
300 + }).toErrorDev(
301 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
302 + );
303 + expect(node.value).toBe('');
304 + // Changing children should do nothing, it functions like `defaultValue`.
305 + stub = ReactDOM.render(<textarea>gorilla</textarea>, container);
306 + expect(node.value).toEqual('');
307 + });
308 + }
309
301 - expect(node.value).toBe('giraffe');
310 + if (ReactFeatureFlags.disableTextareaChildren) {
311 + it('should receive defaultValue and still ignore children content', () => {
312 + let node;
313
303 - // Changing children should do nothing, it functions like `defaultValue`.
304 - stub = ReactDOM.render(<textarea>gorilla</textarea>, container);
305 - expect(node.value).toEqual('giraffe');
306 - });
314 + expect(() => {
315 + node = renderTextarea(
316 + <textarea defaultValue="dragon">monkey</textarea>,
317 + );
318 + }).toErrorDev(
319 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
320 + );
321 + expect(node.value).toBe('dragon');
322 + });
323 + }
324 +
325 + if (!ReactFeatureFlags.disableTextareaChildren) {
326 + it('should treat children like `defaultValue`', () => {
327 + const container = document.createElement('div');
328 + let stub = <textarea>giraffe</textarea>;
329 + let node;
330 +
331 + expect(() => {
332 + node = renderTextarea(stub, container);
333 + }).toErrorDev(
334 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
335 + );
336 +
337 + expect(node.value).toBe('giraffe');
338 +
339 + // Changing children should do nothing, it functions like `defaultValue`.
340 + stub = ReactDOM.render(<textarea>gorilla</textarea>, container);
341 + expect(node.value).toEqual('giraffe');
342 + });
343 + }
344
345 it('should keep value when switching to uncontrolled element if not changed', () => {
346 const container = document.createElement('div');
@@ -342,71 +379,120 @@ describe('ReactDOMTextarea', () => {
379 expect(node.value).toEqual('puppies');
380 });
381
345 - it('should allow numbers as children', () => {
346 - let node;
347 - expect(() => {
348 - node = renderTextarea(<textarea>{17}</textarea>);
349 - }).toErrorDev(
350 - 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
351 - );
352 - expect(node.value).toBe('17');
353 - });
354 -
355 - it('should allow booleans as children', () => {
356 - let node;
357 - expect(() => {
358 - node = renderTextarea(<textarea>{false}</textarea>);
359 - }).toErrorDev(
360 - 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
361 - );
362 - expect(node.value).toBe('false');
363 - });
364 -
365 - it('should allow objects as children', () => {
366 - const obj = {
367 - toString: function() {
368 - return 'sharkswithlasers';
369 - },
370 - };
371 - let node;
372 - expect(() => {
373 - node = renderTextarea(<textarea>{obj}</textarea>);
374 - }).toErrorDev(
375 - 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
376 - );
377 - expect(node.value).toBe('sharkswithlasers');
378 - });
379 -
380 - it('should throw with multiple or invalid children', () => {
381 - expect(() => {
382 - expect(() =>
383 - ReactTestUtils.renderIntoDocument(
384 - <textarea>
385 - {'hello'}
386 - {'there'}
387 - </textarea>,
388 - ),
389 - ).toThrow('<textarea> can only have at most one child');
390 - }).toErrorDev(
391 - 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
392 - );
382 + if (ReactFeatureFlags.disableTextareaChildren) {
383 + it('should ignore numbers as children', () => {
384 + let node;
385 + expect(() => {
386 + node = renderTextarea(<textarea>{17}</textarea>);
387 + }).toErrorDev(
388 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
389 + );
390 + expect(node.value).toBe('');
391 + });
392 + }
393 +
394 + if (!ReactFeatureFlags.disableTextareaChildren) {
395 + it('should allow numbers as children', () => {
396 + let node;
397 + expect(() => {
398 + node = renderTextarea(<textarea>{17}</textarea>);
399 + }).toErrorDev(
400 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
401 + );
402 + expect(node.value).toBe('17');
403 + });
404 + }
405 +
406 + if (ReactFeatureFlags.disableTextareaChildren) {
407 + it('should ignore booleans as children', () => {
408 + let node;
409 + expect(() => {
410 + node = renderTextarea(<textarea>{false}</textarea>);
411 + }).toErrorDev(
412 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
413 + );
414 + expect(node.value).toBe('');
415 + });
416 + }
417 +
418 + if (!ReactFeatureFlags.disableTextareaChildren) {
419 + it('should allow booleans as children', () => {
420 + let node;
421 + expect(() => {
422 + node = renderTextarea(<textarea>{false}</textarea>);
423 + }).toErrorDev(
424 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
425 + );
426 + expect(node.value).toBe('false');
427 + });
428 + }
429 +
430 + if (ReactFeatureFlags.disableTextareaChildren) {
431 + it('should ignore objects as children', () => {
432 + const obj = {
433 + toString: function() {
434 + return 'sharkswithlasers';
435 + },
436 + };
437 + let node;
438 + expect(() => {
439 + node = renderTextarea(<textarea>{obj}</textarea>);
440 + }).toErrorDev(
441 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
442 + );
443 + expect(node.value).toBe('');
444 + });
445 + }
446 +
447 + if (!ReactFeatureFlags.disableTextareaChildren) {
448 + it('should allow objects as children', () => {
449 + const obj = {
450 + toString: function() {
451 + return 'sharkswithlasers';
452 + },
453 + };
454 + let node;
455 + expect(() => {
456 + node = renderTextarea(<textarea>{obj}</textarea>);
457 + }).toErrorDev(
458 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
459 + );
460 + expect(node.value).toBe('sharkswithlasers');
461 + });
462 + }
463
394 - let node;
395 - expect(() => {
396 - expect(
397 - () =>
398 - (node = renderTextarea(
464 + if (!ReactFeatureFlags.disableTextareaChildren) {
465 + it('should throw with multiple or invalid children', () => {
466 + expect(() => {
467 + expect(() =>
468 + ReactTestUtils.renderIntoDocument(
469 <textarea>
400 - <strong />
470 + {'hello'}
471 + {'there'}
472 </textarea>,
402 - )),
403 - ).not.toThrow();
404 - }).toErrorDev(
405 - 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
406 - );
473 + ),
474 + ).toThrow('<textarea> can only have at most one child');
475 + }).toErrorDev(
476 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
477 + );
478
408 - expect(node.value).toBe('[object Object]');
409 - });
479 + let node;
480 + expect(() => {
481 + expect(
482 + () =>
483 + (node = renderTextarea(
484 + <textarea>
485 + <strong />
486 + </textarea>,
487 + )),
488 + ).not.toThrow();
489 + }).toErrorDev(
490 + 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
491 + );
492 +
493 + expect(node.value).toBe('[object Object]');
494 + });
495 + }
496
497 it('should unmount', () => {
498 const container = document.createElement('div');
packages/react-dom/src/client/ReactDOMTextarea.js
+15 -13
@@ -14,6 +14,8 @@ import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCur
14 import {getToStringValue, toString} from './ToStringValue';
15 import type {ToStringValue} from './ToStringValue';
16
17 +import {disableTextareaChildren} from 'shared/ReactFeatureFlags';
18 +
19 let didWarnValDefaultVal = false;
20
21 type TextAreaWithWrapperState = HTMLTextAreaElement & {|
@@ -85,9 +87,7 @@ export function initWrapperState(element: Element, props: Object) {
87
88 // Only bother fetching default value if we're going to use it
89 if (initialValue == null) {
88 - let defaultValue = props.defaultValue;
89 - // TODO (yungsters): Remove support for children content in <textarea>.
90 - let children = props.children;
90 + let {children, defaultValue} = props;
91 if (children != null) {
92 if (__DEV__) {
93 console.error(
@@ -95,19 +95,21 @@ export function initWrapperState(element: Element, props: Object) {
95 'children on <textarea>.',
96 );
97 }
98 - invariant(
99 - defaultValue == null,
100 - 'If you supply `defaultValue` on a <textarea>, do not pass children.',
101 - );
102 - if (Array.isArray(children)) {
98 + if (!disableTextareaChildren) {
99 invariant(
104 - children.length <= 1,
105 - '<textarea> can only have at most one child.',
100 + defaultValue == null,
101 + 'If you supply `defaultValue` on a <textarea>, do not pass children.',
102 );
107 - children = children[0];
103 + if (Array.isArray(children)) {
104 + invariant(
105 + children.length <= 1,
106 + '<textarea> can only have at most one child.',
107 + );
108 + children = children[0];
109 + }
110 +
111 + defaultValue = children;
112 }
109 -
110 - defaultValue = children;
113 }
114 if (defaultValue == null) {
115 defaultValue = '';
packages/shared/ReactFeatureFlags.js
+3
@@ -105,3 +105,6 @@ export const disableLegacyContext = false;
105
106 // Disables React.createFactory
107 export const disableCreateFactory = false;
108 +
109 +// Disables children for <textarea> elements
110 +export const disableTextareaChildren = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -46,6 +46,7 @@ export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
46 export const enableTrainModelFix = false;
47 export const enableTrustedTypesIntegration = false;
48 export const disableCreateFactory = false;
49 +export const disableTextareaChildren = false;
50
51 // Only used in www builds.
52 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -41,6 +41,7 @@ export const enableTrainModelFix = false;
41 export const enableTrustedTypesIntegration = false;
42 export const enableNativeTargetAsInstance = false;
43 export const disableCreateFactory = false;
44 +export const disableTextareaChildren = false;
45
46 // Only used in www builds.
47 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -41,6 +41,7 @@ export const enableTrainModelFix = false;
41 export const enableTrustedTypesIntegration = false;
42 export const enableNativeTargetAsInstance = false;
43 export const disableCreateFactory = false;
44 +export const disableTextareaChildren = false;
45
46 // Only used in www builds.
47 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -41,6 +41,7 @@ export const enableTrainModelFix = false;
41 export const enableTrustedTypesIntegration = false;
42 export const enableNativeTargetAsInstance = false;
43 export const disableCreateFactory = false;
44 +export const disableTextareaChildren = false;
45
46 // Only used in www builds.
47 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -39,6 +39,7 @@ export const enableTrainModelFix = false;
39 export const enableTrustedTypesIntegration = false;
40 export const enableNativeTargetAsInstance = false;
41 export const disableCreateFactory = false;
42 +export const disableTextareaChildren = false;
43
44 // Only used in www builds.
45 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.www.js
+2
@@ -92,6 +92,8 @@ export const enableNativeTargetAsInstance = false;
92
93 export const disableCreateFactory = false;
94
95 +export const disableTextareaChildren = false;
96 +
97 // Flow magic to verify the exports of this file match the original version.
98 // eslint-disable-next-line no-unused-vars
99 type Check<_X, Y: _X, X: Y = _X> = null;