mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Fix form reset for uncontrolled select elements (#11057)
* Set defaultSelected on option element from select's defaultValue * Add form reset fixture for selects * Pass explicit value for setDefaultSelected
This commit is contained in:
committed by
Dan Abramov
parent
b2101cb328
commit
5c6cb597f9
@@ -7,11 +7,23 @@ const ReactDOM = window.ReactDOM;
|
||||
class SelectFixture extends React.Component {
|
||||
state = {value: ''};
|
||||
_nestedDOMNode = null;
|
||||
_singleFormDOMNode = null;
|
||||
_multipleFormDOMNode = null;
|
||||
|
||||
onChange = event => {
|
||||
this.setState({value: event.target.value});
|
||||
};
|
||||
|
||||
resetSingleOptionForm = event => {
|
||||
event.preventDefault();
|
||||
this._singleFormDOMNode.reset();
|
||||
};
|
||||
|
||||
resetMultipleOptionForm = event => {
|
||||
event.preventDefault();
|
||||
this._multipleFormDOMNode.reset();
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._renderNestedSelect();
|
||||
}
|
||||
@@ -100,6 +112,49 @@ class SelectFixture extends React.Component {
|
||||
</select>
|
||||
</div>
|
||||
</TestCase>
|
||||
|
||||
<TestCase title="A single select being reset">
|
||||
<TestCase.Steps>
|
||||
<li>Open the select</li>
|
||||
<li>Select "baz" or "foo"</li>
|
||||
<li>Click the "Reset" button</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
The select should be reset to the inital value, "bar"
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<div className="test-fixture">
|
||||
<form ref={n => (this._singleFormDOMNode = n)}>
|
||||
<select defaultValue="bar">
|
||||
<option value="foo">foo</option>
|
||||
<option value="bar">bar</option>
|
||||
<option value="baz">baz</option>
|
||||
</select>
|
||||
<button onClick={this.resetSingleOptionForm}>Reset</button>
|
||||
</form>
|
||||
</div>
|
||||
</TestCase>
|
||||
|
||||
<TestCase title="A multiple select being reset">
|
||||
<TestCase.Steps>
|
||||
<li>Select any combination of options</li>
|
||||
<li>Click the "Reset" button</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
The select should be reset to the initial values "foo" and "baz"
|
||||
</TestCase.ExpectedResult>
|
||||
|
||||
<div className="test-fixture">
|
||||
<form ref={n => (this._multipleFormDOMNode = n)}>
|
||||
<select multiple defaultValue={['foo', 'baz']}>
|
||||
<option value="foo">foo</option>
|
||||
<option value="bar">bar</option>
|
||||
<option value="baz">baz</option>
|
||||
</select>
|
||||
<button onClick={this.resetMultipleOptionForm}>Reset</button>
|
||||
</form>
|
||||
</div>
|
||||
</TestCase>
|
||||
</FixtureSet>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ function updateOptions(
|
||||
node: HTMLSelectElement,
|
||||
multiple: boolean,
|
||||
propValue: any,
|
||||
setDefaultSelected: boolean,
|
||||
) {
|
||||
type IndexableHTMLOptionsCollection = HTMLOptionsCollection & {
|
||||
[key: number]: HTMLOptionElement,
|
||||
@@ -94,6 +95,9 @@ function updateOptions(
|
||||
if (options[i].selected !== selected) {
|
||||
options[i].selected = selected;
|
||||
}
|
||||
if (selected && setDefaultSelected) {
|
||||
options[i].defaultSelected = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Do not set `select.value` as exact behavior isn't consistent across all
|
||||
@@ -103,6 +107,9 @@ function updateOptions(
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].value === selectedValue) {
|
||||
options[i].selected = true;
|
||||
if (setDefaultSelected) {
|
||||
options[i].defaultSelected = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (defaultSelected === null && !options[i].disabled) {
|
||||
@@ -173,9 +180,9 @@ var ReactDOMSelect = {
|
||||
node.multiple = !!props.multiple;
|
||||
var value = props.value;
|
||||
if (value != null) {
|
||||
updateOptions(node, !!props.multiple, value);
|
||||
updateOptions(node, !!props.multiple, value, false);
|
||||
} else if (props.defaultValue != null) {
|
||||
updateOptions(node, !!props.multiple, props.defaultValue);
|
||||
updateOptions(node, !!props.multiple, props.defaultValue, true);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,14 +197,14 @@ var ReactDOMSelect = {
|
||||
|
||||
var value = props.value;
|
||||
if (value != null) {
|
||||
updateOptions(node, !!props.multiple, value);
|
||||
updateOptions(node, !!props.multiple, value, false);
|
||||
} else if (wasMultiple !== !!props.multiple) {
|
||||
// For simplicity, reapply `defaultValue` if `multiple` is toggled.
|
||||
if (props.defaultValue != null) {
|
||||
updateOptions(node, !!props.multiple, props.defaultValue);
|
||||
updateOptions(node, !!props.multiple, props.defaultValue, true);
|
||||
} else {
|
||||
// Revert the select back to its default unselected state.
|
||||
updateOptions(node, !!props.multiple, props.multiple ? [] : '');
|
||||
updateOptions(node, !!props.multiple, props.multiple ? [] : '', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -207,7 +214,7 @@ var ReactDOMSelect = {
|
||||
var value = props.value;
|
||||
|
||||
if (value != null) {
|
||||
updateOptions(node, !!props.multiple, value);
|
||||
updateOptions(node, !!props.multiple, value, false);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user