diff --git a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js index 4bb68cc92bec7..20dc76053880f 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js @@ -808,4 +808,176 @@ describe('ReactDOMSelect', () => { expect(node.options[1].selected).toBe(false); // b expect(node.options[2].selected).toBe(false); // c }); + + describe('When given a Symbol value', () => { + it('treats initial Symbol value as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe(''); + }); + + it('treats updated Symbol value as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe('monkey'); + + node = ReactTestUtils.renderIntoDocument( + , + ); + + expect(node.value).toBe(''); + }); + + it('treats initial Symbol defaultValue as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe(''); + }); + + it('treats updated Symbol defaultValue as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe('monkey'); + + node = ReactTestUtils.renderIntoDocument( + , + ); + + expect(node.value).toBe(''); + }); + }); + + describe('When given a function value', () => { + it('treats initial function value as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe(''); + }); + + it('treats initial function defaultValue as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe(''); + }); + + it('treats updated function value as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe('monkey'); + + node = ReactTestUtils.renderIntoDocument( + , + ); + + expect(node.value).toBe(''); + }); + + it('treats updated function defaultValue as an empty string', () => { + let node; + + expect(() => { + node = ReactTestUtils.renderIntoDocument( + , + ); + }).toWarnDev('Invalid value for prop `value`'); + + expect(node.value).toBe('monkey'); + + node = ReactTestUtils.renderIntoDocument( + , + ); + + expect(node.value).toBe(''); + }); + }); }); diff --git a/packages/react-dom/src/client/ReactDOMFiberOption.js b/packages/react-dom/src/client/ReactDOMFiberOption.js index 5c2c561594ff9..9d905ceaaa018 100644 --- a/packages/react-dom/src/client/ReactDOMFiberOption.js +++ b/packages/react-dom/src/client/ReactDOMFiberOption.js @@ -10,6 +10,7 @@ import React from 'react'; import warning from 'shared/warning'; import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting'; +import {getToStringValue, toString} from './ToStringValue'; let didWarnSelectedSetOnOption = false; @@ -73,7 +74,7 @@ export function validateProps(element: Element, props: Object) { export function postMountWrapper(element: Element, props: Object) { // value="" should make a value attribute (#6219) if (props.value != null) { - element.setAttribute('value', props.value); + element.setAttribute('value', toString(getToStringValue(props.value))); } } diff --git a/packages/react-dom/src/client/ReactDOMFiberSelect.js b/packages/react-dom/src/client/ReactDOMFiberSelect.js index dfe71a6ae999c..1c51a02376b18 100644 --- a/packages/react-dom/src/client/ReactDOMFiberSelect.js +++ b/packages/react-dom/src/client/ReactDOMFiberSelect.js @@ -12,6 +12,8 @@ import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCur import warning from 'shared/warning'; import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes'; +import {getToStringValue, toString} from './ToStringValue'; +import type {ToStringValue} from './ToStringValue'; let didWarnValueDefaultValue; @@ -21,7 +23,7 @@ if (__DEV__) { type SelectWithWrapperState = HTMLSelectElement & { _wrapperState: { - initialValue: ?string, + initialValue: ?ToStringValue, wasMultiple: boolean, }, }; @@ -98,7 +100,7 @@ function updateOptions( } else { // Do not set `select.value` as exact behavior isn't consistent across all // browsers for all cases. - let selectedValue = '' + (propValue: string); + let selectedValue = toString(getToStringValue((propValue: any))); let defaultSelected = null; for (let i = 0; i < options.length; i++) { if (options[i].value === selectedValue) {