diff --git a/lib/Autocomplete.js b/lib/Autocomplete.js
index df15edd4..82427e7f 100644
--- a/lib/Autocomplete.js
+++ b/lib/Autocomplete.js
@@ -10,6 +10,7 @@ let Autocomplete = React.createClass({
value: React.PropTypes.any,
onChange: React.PropTypes.func,
onSelect: React.PropTypes.func,
+ selectOnTab: React.PropTypes.bool.isRequired,
shouldItemRender: React.PropTypes.func,
sortItems: React.PropTypes.func,
getItemValue: React.PropTypes.func.isRequired,
@@ -34,10 +35,11 @@ let Autocomplete = React.createClass({
},
inputProps: {},
onChange () {},
- onSelect (value, item) {},
+ onSelect (value, item, selectionMethod) {},
renderMenu (items, value, style) {
return
},
+ selectOnTab: true,
shouldItemRender () { return true },
menuStyle: {
borderRadius: '3px',
@@ -164,35 +166,7 @@ let Autocomplete = React.createClass({
},
Enter (event) {
- if (this.state.isOpen === false) {
- // 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()
- var item = this.getFilteredItems()[this.state.highlightedIndex]
- var 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 (event) {
@@ -200,6 +174,49 @@ let Autocomplete = React.createClass({
highlightedIndex: null,
isOpen: false
})
+ },
+
+ Tab (event) {
+ if (this.props.selectOnTab) {
+ this.handleKeyboardSelection(event);
+ }
+ }
+ },
+
+ handleKeyboardSelection (event) {
+ var key = event.key;
+ if (this.state.isOpen === false) {
+ // 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
+ if (key === 'Enter') {
+ // If enter was pressed, we want to prevent the default event handler from executing.
+ // However, if tab was pressed, we *do* want the default handler to kick in.
+ event.preventDefault()
+ }
+ var item = this.getFilteredItems()[this.state.highlightedIndex]
+ var 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, key)
+ })
}
},
@@ -262,7 +279,7 @@ let Autocomplete = React.createClass({
isOpen: false,
highlightedIndex: null
}, () => {
- this.props.onSelect(value, item)
+ this.props.onSelect(value, item, 'click')
this.refs.input.focus()
})
},
@@ -373,4 +390,3 @@ let Autocomplete = React.createClass({
})
module.exports = Autocomplete
-
diff --git a/lib/__tests__/Autocomplete-test.js b/lib/__tests__/Autocomplete-test.js
index ac67f2c8..eb6910c6 100644
--- a/lib/__tests__/Autocomplete-test.js
+++ b/lib/__tests__/Autocomplete-test.js
@@ -274,18 +274,18 @@ describe('Autocomplete kewDown->Enter event handlers', () => {
});
it('should invoke `onSelect` with the selected menu item and close the menu', () => {
- let value = 'Ar';
+ const onSelect = jest.fn();
let defaultPrevented = false;
autocompleteWrapper.setState({'isOpen': true});
autocompleteInputWrapper.simulate('focus');
- autocompleteWrapper.setProps({ value, onSelect(v) { value = v; } });
-
+ autocompleteWrapper.setProps({ value: 'Ar', onSelect });
+
// simulate keyUp of last key, triggering autocomplete suggestion + selection of the suggestion in the menu
- autocompleteInputWrapper.simulate('keyUp', { key : 'r', keyCode: 82, which: 82 });
+ autocompleteInputWrapper.simulate('keyUp', { key : 'r', keyCode: 82, which: 82 });
// Hit enter, updating state.value with the selected Autocomplete suggestion
autocompleteInputWrapper.simulate('keyDown', { key : 'Enter', keyCode: 13, which: 13, preventDefault() { defaultPrevented = true; } });
- expect(value).toEqual('Arizona');
+ expect(onSelect).toBeCalledWith('Arizona', {abbr: 'AZ', name: 'Arizona'}, 'Enter');
expect(autocompleteWrapper.state('isOpen')).toBe(false);
expect(defaultPrevented).toBe(true);
@@ -293,6 +293,49 @@ describe('Autocomplete kewDown->Enter event handlers', () => {
});
+describe('Autocomplete kewDown->Tab event handlers', () => {
+ it('should invoke `onSelect` with the selected menu item and close the menu', () => {
+ const onSelect = jest.fn();
+ const autocompleteWrapper = mount(AutocompleteComponentJSX({}));
+ const autocompleteInputWrapper = autocompleteWrapper.find('input');
+
+ let defaultPrevented = false;
+ autocompleteWrapper.setState({'isOpen': true});
+ autocompleteInputWrapper.simulate('focus');
+ autocompleteWrapper.setProps({ value: 'Ar', onSelect });
+
+ // 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(onSelect).toBeCalledWith('Arizona', {abbr: 'AZ', name: 'Arizona'}, 'Tab');
+ expect(autocompleteWrapper.state('isOpen')).toBe(false);
+ expect(defaultPrevented).toBe(false);
+ });
+
+ it('should not do anything if selectOnTab is false', () => {
+ const onSelect = jest.fn();
+ const autocompleteWrapper = mount(AutocompleteComponentJSX({
+ selectOnTab: false,
+ }));
+ const autocompleteInputWrapper = autocompleteWrapper.find('input');
+
+ autocompleteWrapper.setState({'isOpen': true});
+ autocompleteInputWrapper.simulate('focus');
+ autocompleteWrapper.setProps({ value: 'Ar', onSelect });
+
+ // 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(onSelect).not.toBeCalled();
+ expect(autocompleteWrapper.state('isOpen')).toBe(true);
+ });
+});
+
describe('Autocomplete kewDown->Escape event handlers', () => {
var autocompleteWrapper = mount(AutocompleteComponentJSX({}));
@@ -316,20 +359,17 @@ describe('Autocomplete click event handlers', () => {
var autocompleteInputWrapper = autocompleteWrapper.find('input');
it('should update input value from selected menu item and close the menu', () => {
- let value = 'Ar';
- autocompleteWrapper.setProps({
- value,
- onSelect(v) { value = v; },
- });
+ const onSelect = jest.fn();
+ autocompleteWrapper.setProps({ value: 'Ar', onSelect});
autocompleteWrapper.setState({ isOpen: true });
- autocompleteInputWrapper.simulate('change', { target: { value } });
-
+ autocompleteInputWrapper.simulate('change', { target: { value: 'Ar' } });
+
// simulate keyUp of last key, triggering autocomplete suggestion + selection of the suggestion in the menu
- autocompleteInputWrapper.simulate('keyUp', { key : 'r', keyCode: 82, which: 82 });
+ autocompleteInputWrapper.simulate('keyUp', { key : 'r', keyCode: 82, which: 82 });
// Click inside input, updating state.value with the selected Autocomplete suggestion
autocompleteInputWrapper.simulate('click');
- expect(value).toEqual('Arizona');
+ expect(onSelect).toBeCalledWith('Arizona', {abbr: 'AZ', name: 'Arizona'}, 'click');
expect(autocompleteWrapper.state('isOpen')).toBe(false);
});