diff --git a/README.md b/README.md
index 236e89a495..a022326a9a 100644
--- a/README.md
+++ b/README.md
@@ -381,7 +381,7 @@ function onInputKeyDown(event) {
| `onInputKeyDown` | function | undefined | input keyDown handler; call `event.preventDefault()` to override default `Select` behaviour: `function(event) {}` |
| `onMenuScrollToBottom` | function | undefined | called when the menu is scrolled to the bottom |
| `onOpen` | function | undefined | handler for when the menu opens: `function () {}` |
-| `onSelectResetsInput` | boolean | true | whether the input value should be reset when options are selected, for `multi`
+| `onSelectResetsInput` | boolean | true | whether the input value should be reset when options are selected. Also input value will be set to empty if 'onSelectResetsInput=true' and Select will get new value that not equal previous value. |
| `onValueClick` | function | undefined | onClick handler for value labels: `function (value, event) {}` |
| `openOnClick` | boolean | true | open the options menu when the control is clicked (requires searchable = true) |
| `openOnFocus` | boolean | false | open the options menu when the control gets focus |
diff --git a/src/Select.js b/src/Select.js
index 022004763f..a7222eea5c 100644
--- a/src/Select.js
+++ b/src/Select.js
@@ -33,6 +33,26 @@ const stringOrNumber = PropTypes.oneOfType([
let instanceId = 1;
+const shouldShowValue = (state, props) => {
+ const { inputValue, isPseudoFocused, isFocused } = state;
+ const { onSelectResetsInput } = props;
+
+ if (!inputValue) return true;
+
+ if (!onSelectResetsInput){
+ return !(!isFocused && isPseudoFocused || isFocused && !isPseudoFocused);
+ }
+
+ return false;
+};
+
+const shouldShowPlaceholder = (state, props, isOpen) => {
+ const { inputValue, isPseudoFocused, isFocused } = state;
+ const { onSelectResetsInput } = props;
+
+ return !inputValue || !onSelectResetsInput && !isOpen && !isPseudoFocused && !isFocused;
+};
+
class Select extends React.Component {
constructor (props) {
super(props);
@@ -102,7 +122,7 @@ class Select extends React.Component {
this.setState({ required: false });
}
- if (this.state.inputValue && this.props.value !== nextProps.value && this.props.onSelectResetsInput) {
+ if (this.state.inputValue && this.props.value !== nextProps.value && nextProps.onSelectResetsInput) {
this.setState({ inputValue: this.handleInputValueChange('') });
}
}
@@ -347,7 +367,7 @@ class Select extends React.Component {
let toOpen = this.state.isOpen || this._openAfterFocus || this.props.openOnFocus;
toOpen = this._focusAfterClear ? false : toOpen; //if focus happens after clear values, don't open dropdown yet.
-
+
if (this.props.onFocus) {
this.props.onFocus(event);
}
@@ -771,7 +791,8 @@ class Select extends React.Component {
let renderLabel = this.props.valueRenderer || this.getOptionLabel;
let ValueComponent = this.props.valueComponent;
if (!valueArray.length) {
- return !this.state.inputValue ?
{this.props.placeholder}
: null;
+ const showPlaceholder = shouldShowPlaceholder(this.state, this.props, isOpen);
+ return showPlaceholder ? {this.props.placeholder}
: null;
}
let onClick = this.props.onValueClick ? this.handleValueClick : null;
if (this.props.multi) {
@@ -791,7 +812,7 @@ class Select extends React.Component {
);
});
- } else if (!this.state.inputValue) {
+ } else if (shouldShowValue(this.state, this.props)) {
if (isOpen) onClick = null;
return (
this.input = ref,
required: this.state.required,
- value: this.state.inputValue,
+ value,
};
if (this.props.inputRenderer) {
diff --git a/test/Select-test.js b/test/Select-test.js
index 874d1b1f5a..3b00677e4d 100644
--- a/test/Select-test.js
+++ b/test/Select-test.js
@@ -40,6 +40,7 @@ class PropsWrapper extends React.Component {
super(props);
this.state = props || {};
}
+
setPropsForChild(props) {
this.setState(props);
}
@@ -148,7 +149,7 @@ describe('Select', () => {
onChange={onChange}
onInputChange={onInputChange}
{...props}
- />
+ />
);
if (options.initialFocus !== false) {
findAndFocusInputControl();
@@ -177,7 +178,7 @@ describe('Select', () => {
onChange={onChange}
onInputChange={onInputChange}
{...props}
- />
+ />
);
instance = wrapper.getChild();
@@ -275,7 +276,7 @@ describe('Select', () => {
expect(ReactDOM.findDOMNode(selectInputElement).name, 'to equal', 'form-field-name');
});
- it('should show the options on mouse click', function () {
+ it('should show the options on mouse click', () => {
TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'), { button: 0 });
var node = ReactDOM.findDOMNode(instance);
expect(node, 'queried for', '.Select-option', 'to have length', 3);
@@ -348,14 +349,14 @@ describe('Select', () => {
});
- it('should display the options menu when tapped', function() {
+ it('should display the options menu when tapped', () => {
TestUtils.Simulate.touchStart(getSelectControl(instance));
TestUtils.Simulate.touchEnd(getSelectControl(instance));
var node = ReactDOM.findDOMNode(instance);
expect(node, 'queried for', '.Select-option', 'to have length', 3);
});
- it('should not display the options menu when touched and dragged', function() {
+ it('should not display the options menu when touched and dragged', () => {
TestUtils.Simulate.touchStart(getSelectControl(instance));
TestUtils.Simulate.touchMove(getSelectControl(instance));
TestUtils.Simulate.touchEnd(getSelectControl(instance));
@@ -721,7 +722,6 @@ describe('Select', () => {
});
it('selects the initial value', () => {
-
expect(instance, 'to contain',
Two
@@ -868,7 +868,7 @@ describe('Select', () => {
typeSearchText('1');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
+ 'to satisfy', [
expect.it('to have text', 'One'),
expect.it('to have text', 'Ten'),
expect.it('to have text', 'Twenty-one')
@@ -1016,18 +1016,18 @@ describe('Select', () => {
});
it('set the initial value of the hidden input control', () => {
- expect(ReactDOM.findDOMNode(wrapper).querySelector(FORM_VALUE_SELECTOR).value, 'to equal', 'true' );
+ expect(ReactDOM.findDOMNode(wrapper).querySelector(FORM_VALUE_SELECTOR).value, 'to equal', 'true');
});
it('updates the value when the value prop is set', () => {
wrapper.setPropsForChild({ value: false });
expect(ReactDOM.findDOMNode(instance), 'queried for first', DISPLAYED_SELECTION_SELECTOR,
- 'to have text', 'No');
+ 'to have text', 'No');
});
it('updates the value of the hidden input control after new value prop', () => {
wrapper.setPropsForChild({ value: false });
- expect(ReactDOM.findDOMNode(wrapper).querySelector(FORM_VALUE_SELECTOR).value, 'to equal', 'false' );
+ expect(ReactDOM.findDOMNode(wrapper).querySelector(FORM_VALUE_SELECTOR).value, 'to equal', 'false');
});
it('calls onChange with the new value as a boolean', () => {
@@ -1040,7 +1040,7 @@ describe('Select', () => {
it('supports setting the value via prop', () => {
wrapper.setPropsForChild({ value: false });
expect(ReactDOM.findDOMNode(instance), 'queried for first', DISPLAYED_SELECTION_SELECTOR,
- 'to have text', 'No');
+ 'to have text', 'No');
});
it('displays the X button for false value', () => {
@@ -1068,7 +1068,7 @@ describe('Select', () => {
it('selects the initial value', () => {
expect(instance, 'to contain',
-
+
Yes
No
);
@@ -1088,7 +1088,7 @@ describe('Select', () => {
});
expect(instance, 'to contain',
-
+
No
);
});
@@ -1100,7 +1100,7 @@ describe('Select', () => {
});
expect(instance, 'to contain',
-
+
Yes
);
});
@@ -1113,7 +1113,7 @@ describe('Select', () => {
});
expect(instance, 'to contain',
-
+
No
);
});
@@ -1153,18 +1153,18 @@ describe('Select', () => {
typeSearchText('fal');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
- expect.it('to have text', 'No'),
- ]);
+ 'to satisfy', [
+ expect.it('to have text', 'No'),
+ ]);
});
it('finds text at end', () => {
typeSearchText('se');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
- expect.it('to have text', 'No'),
- ]);
+ 'to satisfy', [
+ expect.it('to have text', 'No'),
+ ]);
});
});
@@ -1182,18 +1182,18 @@ describe('Select', () => {
typeSearchText('fa');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
- expect.it('to have text', 'No')
- ]);
+ 'to satisfy', [
+ expect.it('to have text', 'No')
+ ]);
});
it('does not match text at end', () => {
typeSearchText('se');
expect(ReactDOM.findDOMNode(instance), 'to contain elements matching',
- '.Select-noresults');
+ '.Select-noresults');
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching',
- '.Select-option');
+ '.Select-option');
});
});
@@ -1210,19 +1210,19 @@ describe('Select', () => {
typeSearchText('al');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
- expect.it('to have text', 'No'),
- ]);
+ 'to satisfy', [
+ expect.it('to have text', 'No'),
+ ]);
});
it('finds text at end', () => {
typeSearchText('e');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
- expect.it('to have text', 'Yes'),
- expect.it('to have text', 'No')
- ]);
+ 'to satisfy', [
+ expect.it('to have text', 'Yes'),
+ expect.it('to have text', 'No')
+ ]);
});
});
@@ -1240,18 +1240,18 @@ describe('Select', () => {
typeSearchText('tr');
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
- 'to satisfy', [
- expect.it('to have text', 'Yes')
- ]);
+ 'to satisfy', [
+ expect.it('to have text', 'Yes')
+ ]);
});
it('does not match text at end', () => {
typeSearchText('e');
expect(ReactDOM.findDOMNode(instance), 'to contain elements matching',
- '.Select-noresults');
+ '.Select-noresults');
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching',
- '.Select-option');
+ '.Select-option');
});
});
});
@@ -1298,7 +1298,7 @@ describe('Select', () => {
value: 'three'
});
- expect(ReactDOM.findDOMNode(wrapper).querySelector(FORM_VALUE_SELECTOR).value, 'to equal', 'three' );
+ expect(ReactDOM.findDOMNode(wrapper).querySelector(FORM_VALUE_SELECTOR).value, 'to equal', 'three');
});
it('display the raw value if the option is not available', () => {
@@ -1361,7 +1361,7 @@ describe('Select', () => {
expect(ReactDOM.findDOMNode(instance).querySelectorAll('.Select-option')[1],
'to have attributes', {
class: 'is-disabled'
- });
+ });
});
it('is not selectable by clicking', () => {
@@ -1488,7 +1488,7 @@ describe('Select', () => {
expect(onChange, 'was not called');
// And the menu is still open
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', DISPLAYED_SELECTION_SELECTOR);
- expect(ReactDOM.findDOMNode(instance), 'queried for' , '.Select-option',
+ expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
'to satisfy', [
expect.it('to have text', 'Two')
]);
@@ -1501,7 +1501,7 @@ describe('Select', () => {
expect(onChange, 'was not called');
// And the menu is still open
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', DISPLAYED_SELECTION_SELECTOR);
- expect(ReactDOM.findDOMNode(instance), 'queried for' , '.Select-option',
+ expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
'to satisfy', [
expect.it('to have text', 'Two')
]);
@@ -1514,7 +1514,7 @@ describe('Select', () => {
expect(onChange, 'was not called');
// And the menu is still open
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', DISPLAYED_SELECTION_SELECTOR);
- expect(ReactDOM.findDOMNode(instance), 'queried for' , '.Select-option',
+ expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
'to satisfy', [
expect.it('to have text', 'Two')
]);
@@ -1848,7 +1848,7 @@ describe('Select', () => {
it('filters the existing selections from the options', () => {
- setValueProp(['four','three']);
+ setValueProp(['four', 'three']);
typeSearchText('o');
@@ -1861,7 +1861,7 @@ describe('Select', () => {
it('removes the last selected option with backspace', () => {
- setValueProp(['four','three']);
+ setValueProp(['four', 'three']);
onChange.reset(); // Ignore previous onChange calls
pressBackspace();
expect(onChange, 'was called with', [{ label: 'Four', value: 'four' }]);
@@ -1887,7 +1887,7 @@ describe('Select', () => {
it('removes the last selected option with delete', () => {
- setValueProp(['four','three']);
+ setValueProp(['four', 'three']);
onChange.reset(); // Ignore previous onChange calls
pressDelete();
expect(onChange, 'was called with', [{ label: 'Four', value: 'four' }]);
@@ -2129,63 +2129,103 @@ describe('Select', () => {
});
- describe('with multi=true different onSelectResetsInput', () => {
- it('should have retained inputValue after accepting selection with onSelectResetsInput=false', () => {
- options = [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- { value: 'three', label: 'Three' },
- { value: 'four', label: 'Four' }
- ];
+ describe('onSelectResetsInput', () => {
+ options = [
+ { value: 'one', label: 'One' },
+ { value: 'two', label: 'Two' },
+ { value: 'three', label: 'Three' },
+ { value: 'four', label: 'Four' }
+ ];
+
+ describe('with single select', () => {
+ it('should have retained inputValue after accepting selection with onSelectResetsInput=false', () => {
+ // Render an instance of the component
+ wrapper = createControlWithWrapper({
+ value: '',
+ options: options,
+ onSelectResetsInput: false,
+ onCloseResetsInput: false,
+ onBlurResetsInput: false,
+ });
- // Render an instance of the component
- wrapper = createControlWithWrapper({
- value: '',
- options: options,
- multi: true,
- closeOnSelect: false,
- removeSelected: false,
- onSelectResetsInput: false
- });
+ clickArrowToOpen();
+ typeSearchText('tw');
+ pressEnterToAccept();
+ setValueProp('two'); // trigger componentWillReceiveProps
- instance.setState({
- isFocused: true
+ expect(instance.state.inputValue, 'to equal', 'tw');
+ expect(instance, 'to contain', Two
);
+ expect(instance, 'to contain', );
+
+ instance.setState({
+ isFocused: false,
+ isPseudoFocused: false,
+ });
+
+ expect(instance, 'to contain', );
+ expect(instance, 'to contain', Two
);
+
+ instance.setState({
+ isFocused: true,
+ });
+
+ expect(instance, 'to contain', );
+ expect(instance, 'not to contain', Two
);
});
- clickArrowToOpen();
- typeSearchText('two');
- pressEnterToAccept();
- setValueProp('two'); // trigger componentWillReceiveProps
+ it('should have reset the inputValue after accepting selection when onSelectResetsInput= true or not set', () => {
+ // Render an instance of the component
+ wrapper = createControlWithWrapper({
+ value: '',
+ options: options,
+ });
+
+ clickArrowToOpen();
+ typeSearchText('tw');
+ expect(instance.state.inputValue, 'to equal', 'tw');
+ pressEnterToAccept();
+ setValueProp('two'); // trigger componentWillReceiveProps
- expect(instance.state.inputValue, 'to equal', 'two');
+ expect(instance.state.inputValue, 'to equal', '');
+ expect(instance, 'to contain', Two
);
+ expect(instance, 'to contain', );
+ });
});
- it('should have reset the inputValue after accepting selection when onSelectResetsInput= true or not set', () => {
- options = [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- { value: 'three', label: 'Three' },
- { value: 'four', label: 'Four' }
- ];
+ describe('with multi select', () => {
+ it('should have retained inputValue after accepting selection with onSelectResetsInput=false', () => {
+ // Render an instance of the component
+ wrapper = createControlWithWrapper({
+ value: '',
+ options: options,
+ multi: true,
+ onSelectResetsInput: false
+ });
- // Render an instance of the component
- wrapper = createControlWithWrapper({
- value: '',
- options: options,
- multi: true,
- closeOnSelect: false,
- removeSelected: false
- });
+ clickArrowToOpen();
+ typeSearchText('two');
+ pressEnterToAccept();
+ setValueProp('two'); // trigger componentWillReceiveProps
- instance.setState({
- isFocused: true
+ expect(instance.state.inputValue, 'to equal', 'two');
});
- clickArrowToOpen();
- typeSearchText('two');
- pressEnterToAccept();
+ it('should have reset the inputValue after accepting selection when onSelectResetsInput= true or not set', () => {
+ // Render an instance of the component
+ wrapper = createControlWithWrapper({
+ value: '',
+ options: options,
+ multi: true,
+ });
+
+ clickArrowToOpen();
+ typeSearchText('two');
+ expect(instance.state.inputValue, 'to equal', 'two');
+ pressEnterToAccept();
+ setValueProp('two'); // trigger componentWillReceiveProps
- expect(instance.state.inputValue, 'to equal', '');
+ expect(instance.state.inputValue, 'to equal', '');
+ });
});
});
@@ -2717,16 +2757,16 @@ describe('Select', () => {
instance = ReactDOM.render(, node);
});
- it('should set the isFocused state to false if disabled=true', function(){
+ it('should set the isFocused state to false if disabled=true', () => {
- expect(instance.state.isFocused, 'to equal', false);
- findAndFocusInputControl();
- expect(instance.state.isFocused, 'to equal', true);
- ReactDOM.render(, node);
- expect(instance.state.isFocused, 'to equal', false);
+ expect(instance.state.isFocused, 'to equal', false);
+ findAndFocusInputControl();
+ expect(instance.state.isFocused, 'to equal', true);
+ ReactDOM.render(