diff --git a/README.md b/README.md
index e78523da..5125c067 100644
--- a/README.md
+++ b/README.md
@@ -173,6 +173,12 @@ Default value: `false`
Whether or not to automatically select the highlighted item when the
`` loses focus.
+#### `selectOnTab: Boolean` (optional)
+Default value: `false`
+
+Whether or not to select the highlighted item when the
+Tab key is pressed (same as existing Enter or Click behavior).
+
#### `shouldItemRender: Function` (optional)
Arguments: `item: Any, value: String`
diff --git a/lib/Autocomplete.js b/lib/Autocomplete.js
index 1658c8bb..1efd01f9 100644
--- a/lib/Autocomplete.js
+++ b/lib/Autocomplete.js
@@ -142,6 +142,10 @@ class Autocomplete extends React.Component {
* `` loses focus.
*/
selectOnBlur: PropTypes.bool,
+ /**
+ * Whether or not to select the highlighted item on a 'Tab' keypress
+ */
+ selectOnTab: PropTypes.bool,
/**
* Arguments: `isOpen: Boolean`
*
@@ -187,6 +191,7 @@ class Autocomplete extends React.Component {
},
autoHighlight: true,
selectOnBlur: false,
+ selectOnTab: false,
onMenuVisibilityChange() {},
}
@@ -323,37 +328,7 @@ class Autocomplete extends React.Component {
Enter(event) {
// Key code 229 is used for selecting items from character selectors (Pinyin, Kana, etc)
if (event.keyCode !== 13) return
- // In case the user is currently hovering over the menu
- this.setIgnoreBlur(false)
- if (!this.isOpen()) {
- // menu is closed so there is no selection to accept -> do nothing
- return
- }
- else if (this.state.highlightedIndex == null) {
- // input has focus but no menu item is selected + enter is hit -> close the menu, highlight whatever's in input
- this.setState({
- isOpen: false
- }, () => {
- this.refs.input.select()
- })
- }
- else {
- // text entered + menu item has been highlighted + enter is hit -> update value to that of selected menu item, close the menu
- event.preventDefault()
- const item = this.getFilteredItems(this.props)[this.state.highlightedIndex]
- const value = this.props.getItemValue(item)
- this.setState({
- isOpen: false,
- highlightedIndex: null
- }, () => {
- //this.refs.input.focus() // TODO: file issue
- this.refs.input.setSelectionRange(
- value.length,
- value.length
- )
- this.props.onSelect(value, item)
- })
- }
+ this.handleKeyboardSelection(event)
},
Escape() {
@@ -365,12 +340,52 @@ class Autocomplete extends React.Component {
})
},
- Tab() {
- // In case the user is currently hovering over the menu
- this.setIgnoreBlur(false)
+ Tab(event) {
+ if (this.props.selectOnTab) {
+ this.handleKeyboardSelection(event)
+ }
},
}
+ handleKeyboardSelection(event) {
+ // Destructure key value from event object for possible later use
+ const { key } = event
+ // In case the user is currently hovering over the menu
+ this.setIgnoreBlur(false)
+ if (!this.isOpen()) {
+ // menu is closed so there is no selection to accept -> do nothing
+ return
+ }
+ else if (this.state.highlightedIndex == null) {
+ // input has focus but no menu item is selected + Enter/Tab is hit -> close the menu, highlight whatever's in input
+ this.setState({
+ isOpen: false
+ }, () => {
+ this.refs.input.select()
+ })
+ }
+ else {
+ // text entered + menu item has been highlighted + Enter/Tab is hit -> update value to that of selected menu item, close the menu
+ if (key === 'Enter') {
+ // Prevent default on Enter, but we *do* want it on Tab events so user selection goes to next field if possible
+ event.preventDefault()
+ }
+ const item = this.getFilteredItems(this.props)[this.state.highlightedIndex]
+ const value = this.props.getItemValue(item)
+ this.setState({
+ isOpen: false,
+ highlightedIndex: null
+ }, () => {
+ //this.refs.input.focus() // TODO: file issue
+ this.refs.input.setSelectionRange(
+ value.length,
+ value.length
+ )
+ this.props.onSelect(value, item)
+ })
+ }
+ }
+
getFilteredItems(props) {
let items = props.items
diff --git a/lib/__tests__/Autocomplete-test.js b/lib/__tests__/Autocomplete-test.js
index ed5b79fb..ac2a249c 100644
--- a/lib/__tests__/Autocomplete-test.js
+++ b/lib/__tests__/Autocomplete-test.js
@@ -596,6 +596,50 @@ describe('Autocomplete keyDown->Escape event handlers', () => {
})
+describe('Autocomplete kewDown->Tab event handlers', () => {
+ it('should invoke `onSelect` with the selected menu item and close the menu when selectOnTab is true', () => {
+ const autocompleteWrapper = mount(AutocompleteComponentJSX({
+ selectOnTab: true,
+ }))
+ const autocompleteInputWrapper = autocompleteWrapper.find('input')
+
+ let value = 'Ar'
+ let defaultPrevented = false
+ autocompleteWrapper.setState({ 'isOpen': true })
+ autocompleteInputWrapper.simulate('focus')
+ autocompleteWrapper.setProps({ value, onSelect(v) { value = v } })
+
+ // simulate keyUp of last key, triggering autocomplete suggestion + selection of the suggestion in the menu
+ autocompleteInputWrapper.simulate('keyUp', { key : 'r', keyCode: 82, which: 82 })
+
+ // Hit tab, updating state.value with the selected Autocomplete suggestion
+ autocompleteInputWrapper.simulate('keyDown', { key : 'Tab', keyCode: 9, which: 9, preventDefault() { defaultPrevented = true } })
+ expect(value).toEqual('Arizona')
+ expect(autocompleteWrapper.state('isOpen')).toBe(false)
+ expect(defaultPrevented).toBe(false)
+ })
+
+ it('should not do anything if selectOnTab is false', () => {
+ const autocompleteWrapper = mount(AutocompleteComponentJSX({
+ selectOnTab: false,
+ }))
+ const autocompleteInputWrapper = autocompleteWrapper.find('input')
+
+ let value = 'Ar'
+ autocompleteWrapper.setState({ 'isOpen': true })
+ autocompleteInputWrapper.simulate('focus')
+ autocompleteWrapper.setProps({ value, onSelect(v) { value = v } })
+
+ // simulate keyUp of last key, triggering autocomplete suggestion + selection of the suggestion in the menu
+ autocompleteInputWrapper.simulate('keyUp', { key : 'r', keyCode: 82, which: 82 })
+
+ // Pressing tab should not change the state of the component
+ autocompleteInputWrapper.simulate('keyDown', { key : 'Tab', keyCode: 9, which: 9 })
+ expect(value).toEqual('Ar')
+ expect(autocompleteWrapper.state('isOpen')).toBe(true)
+ })
+})
+
describe('Autocomplete keyDown', () => {
it('should not clear highlightedIndex for keys that don\'t modify `input.value`', () => {
const tree = mount(AutocompleteComponentJSX({ open: true }))