mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix passing symbols and functions to textarea (#13362)
* refactor: move getSafeValue to separate file * fix(?): ReactDOMFiberTextarea sanitization for symbols and functions * tests: add TODOs for warnings * fix: restore accidentally removed test * fix: remove redundant logic for initialValue * refactor: integrate SafeValue typings into textarea * fix: restore stringified newValue for equality check * fix: remove getSafeValue from hostProps * refactor: SafeValue -> ToStringValue * refactor: update TODO comment in test file * refactor: no need to convert children to ToStringValue
This commit is contained in:
committed by
Nathan Hunzaker
parent
5550ed4a8f
commit
d04d03e470
@@ -20,6 +20,8 @@ describe('ReactDOMTextarea', () => {
|
||||
let renderTextarea;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
@@ -423,4 +425,126 @@ describe('ReactDOMTextarea', () => {
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
ReactDOM.render(<textarea value={undefined} />, container);
|
||||
});
|
||||
|
||||
describe('When given a Symbol value', () => {
|
||||
it('treats initial Symbol value as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<textarea value={Symbol('foobar')} onChange={() => {}} />,
|
||||
container,
|
||||
),
|
||||
).toWarnDev('Invalid value for prop `value`');
|
||||
const node = container.firstChild;
|
||||
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats initial Symbol children as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<textarea onChange={() => {}}>{Symbol('foo')}</textarea>,
|
||||
container,
|
||||
),
|
||||
).toWarnDev('Use the `defaultValue` or `value` props');
|
||||
const node = container.firstChild;
|
||||
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats updated Symbol value as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<textarea value="foo" onChange={() => {}} />, container);
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<textarea value={Symbol('foo')} onChange={() => {}} />,
|
||||
container,
|
||||
),
|
||||
).toWarnDev('Invalid value for prop `value`');
|
||||
const node = container.firstChild;
|
||||
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats initial Symbol defaultValue as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<textarea defaultValue={Symbol('foobar')} />, container);
|
||||
const node = container.firstChild;
|
||||
|
||||
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats updated Symbol defaultValue as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<textarea defaultValue="foo" />, container);
|
||||
ReactDOM.render(<textarea defaultValue={Symbol('foobar')} />, container);
|
||||
const node = container.firstChild;
|
||||
|
||||
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
|
||||
expect(node.value).toBe('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When given a function value', () => {
|
||||
it('treats initial function value as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<textarea value={() => {}} onChange={() => {}} />,
|
||||
container,
|
||||
),
|
||||
).toWarnDev('Invalid value for prop `value`');
|
||||
const node = container.firstChild;
|
||||
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats initial function children as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<textarea onChange={() => {}}>{() => {}}</textarea>,
|
||||
container,
|
||||
),
|
||||
).toWarnDev('Use the `defaultValue` or `value` props');
|
||||
const node = container.firstChild;
|
||||
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats updated function value as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<textarea value="foo" onChange={() => {}} />, container);
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<textarea value={() => {}} onChange={() => {}} />,
|
||||
container,
|
||||
),
|
||||
).toWarnDev('Invalid value for prop `value`');
|
||||
const node = container.firstChild;
|
||||
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats initial function defaultValue as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<textarea defaultValue={() => {}} />, container);
|
||||
const node = container.firstChild;
|
||||
|
||||
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
|
||||
expect(node.value).toBe('');
|
||||
});
|
||||
|
||||
it('treats updated function defaultValue as an empty string', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<textarea defaultValue="foo" />, container);
|
||||
ReactDOM.render(<textarea defaultValue={() => {}} />, container);
|
||||
const node = container.firstChild;
|
||||
|
||||
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
|
||||
expect(node.value).toBe('foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+9
-8
@@ -12,12 +12,14 @@ import warning from 'shared/warning';
|
||||
|
||||
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
|
||||
import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCurrentFiber';
|
||||
import {getToStringValue, toString} from './ToStringValue';
|
||||
import type {ToStringValue} from './ToStringValue';
|
||||
|
||||
let didWarnValDefaultVal = false;
|
||||
|
||||
type TextAreaWithWrapperState = HTMLTextAreaElement & {
|
||||
_wrapperState: {
|
||||
initialValue: string,
|
||||
initialValue: ToStringValue,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -54,7 +56,7 @@ export function getHostProps(element: Element, props: Object) {
|
||||
...props,
|
||||
value: undefined,
|
||||
defaultValue: undefined,
|
||||
children: '' + node._wrapperState.initialValue,
|
||||
children: toString(node._wrapperState.initialValue),
|
||||
};
|
||||
|
||||
return hostProps;
|
||||
@@ -110,7 +112,7 @@ export function initWrapperState(element: Element, props: Object) {
|
||||
children = children[0];
|
||||
}
|
||||
|
||||
defaultValue = '' + children;
|
||||
defaultValue = children;
|
||||
}
|
||||
if (defaultValue == null) {
|
||||
defaultValue = '';
|
||||
@@ -119,18 +121,17 @@ export function initWrapperState(element: Element, props: Object) {
|
||||
}
|
||||
|
||||
node._wrapperState = {
|
||||
initialValue: '' + initialValue,
|
||||
initialValue: getToStringValue(initialValue),
|
||||
};
|
||||
}
|
||||
|
||||
export function updateWrapper(element: Element, props: Object) {
|
||||
const node = ((element: any): TextAreaWithWrapperState);
|
||||
const value = props.value;
|
||||
const value = getToStringValue(props.value);
|
||||
if (value != null) {
|
||||
// Cast `value` to a string to ensure the value is set correctly. While
|
||||
// browsers typically do this as necessary, jsdom doesn't.
|
||||
const newValue = '' + value;
|
||||
|
||||
const newValue = toString(value);
|
||||
// To avoid side effects (such as losing text selection), only set value if changed
|
||||
if (newValue !== node.value) {
|
||||
node.value = newValue;
|
||||
@@ -140,7 +141,7 @@ export function updateWrapper(element: Element, props: Object) {
|
||||
}
|
||||
}
|
||||
if (props.defaultValue != null) {
|
||||
node.defaultValue = props.defaultValue;
|
||||
node.defaultValue = toString(getToStringValue(props.defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user