-
Notifications
You must be signed in to change notification settings - Fork 46.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ignore symbols and functions in select tag #13389
Changes from 1 commit
1d2755a
a6041eb
81ff1c9
48dcdef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -808,4 +808,268 @@ 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( | ||
<select onChange={noop} value={Symbol('foobar')}> | ||
<option value={Symbol('foobar')}>A Symbol!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).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( | ||
<select onChange={noop} value="monkey"> | ||
<option value={Symbol('foobar')}>A Symbol!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).toWarnDev('Invalid value for prop `value`'); | ||
|
||
expect(node.value).toBe('monkey'); | ||
|
||
node = ReactTestUtils.renderIntoDocument( | ||
<select onChange={noop} value={Symbol('foobar')}> | ||
<option value={Symbol('foobar')}>A Symbol!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
); | ||
|
||
expect(node.value).toBe(''); | ||
}); | ||
|
||
it('treats initial Symbol defaultValue as an empty string', () => { | ||
let node; | ||
|
||
expect( | ||
() => | ||
(node = ReactTestUtils.renderIntoDocument( | ||
<select defaultValue={Symbol('foobar')}> | ||
<option value={Symbol('foobar')}>A Symbol!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).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( | ||
<select defaultValue="monkey"> | ||
<option value={Symbol('foobar')}>A Symbol!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).toWarnDev('Invalid value for prop `value`'); | ||
|
||
expect(node.value).toBe('monkey'); | ||
|
||
node = ReactTestUtils.renderIntoDocument( | ||
<select defaultValue={Symbol('foobar')}> | ||
<option value={Symbol('foobar')}>A Symbol!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
); | ||
|
||
expect(node.value).toBe(''); | ||
}); | ||
}); | ||
|
||
describe('When given a function value', () => { | ||
let setUntrackedValue; | ||
function dispatchEventOnNode(node, type) { | ||
node.dispatchEvent(new Event(type, {bubbles: true, cancelable: true})); | ||
} | ||
|
||
beforeEach(() => { | ||
setUntrackedValue = Object.getOwnPropertyDescriptor( | ||
HTMLSelectElement.prototype, | ||
'value', | ||
).set; | ||
}); | ||
|
||
it('treats initial function value as an empty string', () => { | ||
let node; | ||
|
||
expect( | ||
() => | ||
(node = ReactTestUtils.renderIntoDocument( | ||
<select onChange={noop} value={() => {}}> | ||
<option value={() => {}}>A function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).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( | ||
<select defaultValue={() => {}}> | ||
<option value={() => {}}>A function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).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( | ||
<select onChange={noop} value="monkey"> | ||
<option value={() => {}}>A function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).toWarnDev('Invalid value for prop `value`'); | ||
|
||
expect(node.value).toBe('monkey'); | ||
|
||
node = ReactTestUtils.renderIntoDocument( | ||
<select onChange={noop} value={() => {}}> | ||
<option value={() => {}}>A function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
); | ||
|
||
expect(node.value).toBe(''); | ||
}); | ||
|
||
it('treats updated function defaultValue as an empty string', () => { | ||
let node; | ||
|
||
expect( | ||
() => | ||
(node = ReactTestUtils.renderIntoDocument( | ||
<select defaultValue="monkey"> | ||
<option value={() => {}}>A function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
)), | ||
).toWarnDev('Invalid value for prop `value`'); | ||
|
||
expect(node.value).toBe('monkey'); | ||
|
||
node = ReactTestUtils.renderIntoDocument( | ||
<select defaultValue={() => {}}> | ||
<option value={() => {}}>A function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select>, | ||
); | ||
|
||
expect(node.value).toBe(''); | ||
}); | ||
|
||
it('treats controlled function value as an empty string', () => { | ||
let selectNode; | ||
|
||
class Form extends React.Component { | ||
state = { | ||
value: 'giraffe', | ||
}; | ||
|
||
handleChange = e => this.setState({value: e.target.value}); | ||
|
||
render() { | ||
return ( | ||
<select | ||
onChange={this.handleChange} | ||
value={this.state.value} | ||
ref={x => (selectNode = x)}> | ||
<option value={() => {}}>A Function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select> | ||
); | ||
} | ||
} | ||
|
||
expect(() => ReactTestUtils.renderIntoDocument(<Form />)).toWarnDev( | ||
'Invalid value for prop `value`', | ||
); | ||
|
||
expect(selectNode.value).toBe('giraffe'); | ||
|
||
setUntrackedValue.call(selectNode, () => {}); | ||
dispatchEventOnNode(selectNode, 'change'); | ||
|
||
expect(selectNode.value).toBe(''); | ||
}); | ||
|
||
it('treats controlled function value as an empty string', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seemed to be a additional case that I found failing before this PR. I don't know if this is the best way to test it, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, this case is probably fine as is, since the browser defaults to using the element's children as a value if the attribute
|
||
let selectNode; | ||
|
||
class Form extends React.Component { | ||
state = { | ||
value: 'giraffe', | ||
}; | ||
|
||
handleChange = e => this.setState({value: e.target.value}); | ||
|
||
render() { | ||
return ( | ||
<select | ||
onChange={this.handleChange} | ||
value={this.state.value} | ||
ref={x => (selectNode = x)}> | ||
<option value={() => {}}>A Function!</option> | ||
<option value="monkey">A monkey!</option> | ||
<option value="giraffe">A giraffe!</option> | ||
</select> | ||
); | ||
} | ||
} | ||
|
||
expect(() => ReactTestUtils.renderIntoDocument(<Form />)).toWarnDev( | ||
'Invalid value for prop `value`', | ||
); | ||
|
||
expect(selectNode.value).toBe('giraffe'); | ||
|
||
setUntrackedValue.call(selectNode, () => {}); | ||
dispatchEventOnNode(selectNode, 'change'); | ||
|
||
expect(selectNode.value).toBe(''); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | void, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since |
||
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) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: but I think the implicit return with an assignment distracts from the test. Could you remove them, like: