diff --git a/HISTORY.md b/HISTORY.md
index f6fe584445..9432bf05a3 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,8 @@
## Unreleased
+* added; new `closeOnSelect` prop (defaults to `true`) that controls whether the menu is closed when an option is selected, thanks to [Michael Elgar](https://github.com/melgar) for the original idea
+* changed; by default, the menu for multi-selects now closes when an option is selected
* fixed; `Async` component always called `onChange` even when it wasn't provided
* fixed; input lag for the `Async` component when results are returned from cache
diff --git a/README.md b/README.md
index 416d2d4e1a..2b73d9dcf8 100644
--- a/README.md
+++ b/README.md
@@ -360,7 +360,7 @@ function onInputKeyDown(event) {
| clearAllText | string | 'Clear all' | title for the "clear" control when `multi` is true |
| clearRenderer | func | undefined | Renders a custom clear to be shown in the right-hand side of the select when clearable true: `clearRenderer()` |
| clearValueText | string | 'Clear value' | title for the "clear" control |
-| resetValue | any | null | value to use when you clear the control |
+| closeOnSelect | bool | true | whether to close the menu when a value is selected
| deleteRemoves | bool | true | whether pressing delete key removes the last item when there is no input value |
| delimiter | string | ',' | delimiter to use to join multiple values |
| disabled | bool | false | whether the Select is disabled or not |
@@ -396,6 +396,7 @@ function onInputKeyDown(event) {
| options | array | undefined | array of options |
| placeholder | string\|node | 'Select ...' | field placeholder, displayed when there's no value |
| required | bool | false | applies HTML5 required attribute when needed |
+| resetValue | any | null | value to set when the control is cleared |
| scrollMenuIntoView | bool | true | whether the viewport will shift to display the entire menu when engaged |
| searchable | bool | true | whether to enable searching feature or not |
| searchPromptText | string\|node | 'Type to search' | label to prompt for search input |
diff --git a/examples/src/components/Multiselect.js b/examples/src/components/Multiselect.js
index b6ced49178..23cbea74e9 100644
--- a/examples/src/components/Multiselect.js
+++ b/examples/src/components/Multiselect.js
@@ -25,7 +25,7 @@ var MultiSelectField = createClass({
return {
disabled: false,
crazy: false,
- options: FLAVOURS,
+ stayOpen: false,
value: [],
};
},
@@ -33,31 +33,41 @@ var MultiSelectField = createClass({
console.log('You\'ve selected:', value);
this.setState({ value });
},
- toggleDisabled (e) {
- this.setState({ disabled: e.target.checked });
- },
- toggleChocolate (e) {
- let crazy = e.target.checked;
+ toggleCheckbox (e) {
this.setState({
- crazy: crazy,
- options: crazy ? WHY_WOULD_YOU : FLAVOURS,
+ [e.target.name]: e.target.checked,
});
},
render () {
+ const { crazy, disabled, stayOpen, value } = this.state;
+ const options = crazy ? WHY_WOULD_YOU : FLAVOURS;
return (
);
diff --git a/examples/src/components/States.js b/examples/src/components/States.js
index 4405705f61..8c642a3b9e 100644
--- a/examples/src/components/States.js
+++ b/examples/src/components/States.js
@@ -31,13 +31,13 @@ var StatesField = createClass({
console.log('Country changed to ' + newCountry);
this.setState({
country: newCountry,
- selectValue: null
+ selectValue: null,
});
},
updateValue (newValue) {
console.log('State changed to ' + newValue);
this.setState({
- selectValue: newValue
+ selectValue: newValue,
});
},
focusStateSelect () {
diff --git a/src/Select.js b/src/Select.js
index 06c5681228..c8475c0497 100644
--- a/src/Select.js
+++ b/src/Select.js
@@ -17,44 +17,46 @@ import defaultClearRenderer from './utils/defaultClearRenderer';
import Option from './Option';
import Value from './Value';
-const stringifyValue = (value) => typeof value === 'string' ? value : value !== null && JSON.stringify(value) || '';
+const stringifyValue = value =>
+ typeof value === 'string'
+ ? value
+ : (value !== null && JSON.stringify(value)) || '';
const stringOrNode = PropTypes.oneOfType([
PropTypes.string,
- PropTypes.node
+ PropTypes.node,
]);
let instanceId = 1;
class Select extends React.Component {
- constructor(props) {
+ constructor (props) {
super(props);
-
- [
- 'clearValue',
- 'focusOption',
- 'handleInputBlur',
- 'handleInputChange',
- 'handleInputFocus',
- 'handleInputValueChange',
- 'handleKeyDown',
- 'handleMenuScroll',
- 'handleMouseDown',
- 'handleMouseDownOnArrow',
- 'handleMouseDownOnMenu',
- 'handleRequired',
- 'handleTouchOutside',
- 'handleTouchMove',
- 'handleTouchStart',
- 'handleTouchEnd',
- 'handleTouchEndClearValue',
- 'handleValueClick',
- 'getOptionLabel',
- 'onOptionRef',
- 'removeValue',
- 'selectValue'
- ].forEach((fn) => this[fn] = this[fn].bind(this));
+ [
+ 'clearValue',
+ 'focusOption',
+ 'handleInputBlur',
+ 'handleInputChange',
+ 'handleInputFocus',
+ 'handleInputValueChange',
+ 'handleKeyDown',
+ 'handleMenuScroll',
+ 'handleMouseDown',
+ 'handleMouseDownOnArrow',
+ 'handleMouseDownOnMenu',
+ 'handleRequired',
+ 'handleTouchOutside',
+ 'handleTouchMove',
+ 'handleTouchStart',
+ 'handleTouchEnd',
+ 'handleTouchEndClearValue',
+ 'handleValueClick',
+ 'getOptionLabel',
+ 'onOptionRef',
+ 'removeValue',
+ 'selectValue',
+ ].forEach((fn) => this[fn] = this[fn].bind(this));
this.state = {
inputValue: '',
@@ -492,7 +494,7 @@ class Select extends React.Component {
}
setValue (value) {
- if (this.props.autoBlur){
+ if (this.props.autoBlur) {
this.blurInput();
}
if (!this.props.onChange) return;
@@ -507,20 +509,24 @@ class Select extends React.Component {
}
selectValue (value) {
- //NOTE: update value in the callback to make sure the input value is empty so that there are no styling issues (Chrome had issue otherwise)
- this.hasScrolledToOption = false;
+ // NOTE: we actually add/set the value in a callback to make sure the
+ // input value is empty to avoid styling issues in Chrome
+ if (this.props.closeOnSelect) {
+ this.hasScrolledToOption = false;
+ }
if (this.props.multi) {
const updatedValue = this.props.onSelectResetsInput ? '' : this.state.inputValue;
this.setState({
+ focusedIndex: null,
inputValue: this.handleInputValueChange(updatedValue),
- focusedIndex: null
+ isOpen: !this.props.closeOnSelect,
}, () => {
this.addValue(value);
});
} else {
this.setState({
- isOpen: false,
inputValue: this.handleInputValueChange(''),
+ isOpen: !this.props.closeOnSelect,
isPseudoFocused: this.state.isFocused,
}, () => {
this.setValue(value);
@@ -805,7 +811,6 @@ class Select extends React.Component {
}
renderClear () {
-
if (!this.props.clearable || this.props.value === undefined || this.props.value === null || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return;
const clear = this.props.clearRenderer();
@@ -1040,7 +1045,7 @@ Select.propTypes = {
'aria-describedby': PropTypes.string, // HTML ID(s) of element(s) that should be used to describe this input (for assistive tech)
'aria-label': PropTypes.string, // Aria label (for assistive tech)
'aria-labelledby': PropTypes.string, // HTML ID of an element that should be used as the label (for assistive tech)
- addLabelText: PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input
+ addLabelText: PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input
arrowRenderer: PropTypes.func, // Create drop-down caret element
autoBlur: PropTypes.bool, // automatically blur the component when an option is selected
autofocus: PropTypes.bool, // autofocus the component on mount
@@ -1052,6 +1057,7 @@ Select.propTypes = {
clearRenderer: PropTypes.func, // create clearable x element
clearValueText: stringOrNode, // title for the "clear" control
clearable: PropTypes.bool, // should it be possible to reset value
+ closeOnSelect: React.PropTypes.bool, // whether to close the menu when a value is selected
deleteRemoves: PropTypes.bool, // whether backspace removes an item if there is no text input
delimiter: PropTypes.string, // delimiter to use to join multiple values for the hidden field value
disabled: PropTypes.bool, // whether the Select is disabled or not
@@ -1120,6 +1126,7 @@ Select.defaultProps = {
clearAllText: 'Clear all',
clearRenderer: defaultClearRenderer,
clearValueText: 'Clear value',
+ closeOnSelect: true,
deleteRemoves: true,
delimiter: ',',
disabled: false,