diff --git a/src/Select.js b/src/Select.js index 0ef2c4e406..4711df8e1c 100644 --- a/src/Select.js +++ b/src/Select.js @@ -381,7 +381,7 @@ var Select = React.createClass({ }, handleKeyDown: function(event) { - if (this.state.disabled) return; + if (this.props.disabled) return; switch (event.keyCode) { @@ -407,7 +407,7 @@ var Select = React.createClass({ case 27: // escape if (this.state.isOpen) { this.resetValue(); - } else { + } else if (this.props.clearable) { this.clearValue(event); } break; @@ -754,13 +754,13 @@ var Select = React.createClass({ var input; var inputProps = { ref: 'input', - className: 'Select-input', + className: 'Select-input ' + (this.props.inputProps.className || ''), tabIndex: this.props.tabIndex || 0, onFocus: this.handleInputFocus, onBlur: this.handleInputBlur }; for (var key in this.props.inputProps) { - if (this.props.inputProps.hasOwnProperty(key)) { + if (this.props.inputProps.hasOwnProperty(key) && key !== 'className') { inputProps[key] = this.props.inputProps[key]; } } diff --git a/test/Select-test.js b/test/Select-test.js index 796b8e95c3..62b679f355 100644 --- a/test/Select-test.js +++ b/test/Select-test.js @@ -18,12 +18,99 @@ var TestUtils = React.addons.TestUtils; var Select = require('../src/Select'); +// The displayed text of the currently selected item, when items collapsed +var DISPLAYED_SELECTION_SELECTOR = '.Select-placeholder'; +var FORM_VALUE = '.Select > input'; + +class PropsWrapper extends React.Component { + + constructor(props) { + super(props); + this.state = props || {}; + } + + setPropsForChild(props) { + this.setState(props); + } + + getChild() { + return this.refs.child; + } + + render() { + var Component = this.props.childComponent; + return (); + } +} describe('Select', function() { + var options, instance, onChange; + var searchInputNode; + + function pressEnterToAccept() { + TestUtils.Simulate.keyDown(searchInputNode, { keyCode: 13, key: 'Enter' }); + } + + function pressTabToAccept() { + TestUtils.Simulate.keyDown(searchInputNode, { keyCode: 9, key: 'Tab' }); + } + + function pressEscape() { + TestUtils.Simulate.keyDown(searchInputNode, { keyCode: 27, key: 'Escape' }); + } + + function pressBackspace() { + TestUtils.Simulate.keyDown(searchInputNode, { keyCode: 8, key: 'Backspace' }); + } + + function typeSearchText(text) { + TestUtils.Simulate.change(searchInputNode, { target: { value: text } }); + } + + function getSelectControl(instance) { + return React.findDOMNode(instance).querySelector('.Select-control'); + } + + function clickDocument() { + var clickEvent = document.createEvent('MouseEvents'); + clickEvent.initEvent('click', true, true); + document.dispatchEvent(clickEvent); + } + + var createControl = function(props) { + + + onChange = sinon.spy(); + // Render an instance of the component + instance = TestUtils.renderIntoDocument( + + ); + + // Focus on the input, such that mouse events are accepted + searchInputNode = instance.getInputNode().getDOMNode().querySelector('input'); + TestUtils.Simulate.focus(searchInputNode); + }); + + + it('calls the asyncOptions initially with autoload=true', function () { + + expect(asyncOptions, 'was called with', ''); + }); + + it('calls the asyncOptions again when the input changes', function () { + + typeSearchText('ab'); + + expect(asyncOptions, 'was called twice'); + expect(asyncOptions, 'was called with', 'ab'); + }); + + it('shows the returned options after asyncOptions calls back', function () { + + typeSearchText('te'); + + var optionList = React.findDOMNode(instance).querySelectorAll('.Select-menu .Select-option'); + expect(optionList, 'to have length', 3); + expect(optionList[0], 'to have text', 'TEST one'); + expect(optionList[1], 'to have text', 'TEST two'); + expect(optionList[2], 'to have text', 'TELL three'); + }); + + it('uses the options cache when the same text is entered again', function () { + + + typeSearchText('te'); + typeSearchText('tes'); + + expect(asyncOptions, 'was called times', 3); + + typeSearchText('te'); + + expect(asyncOptions, 'was called times', 3); + + }); + + it('displays the correct options from the cache after the input is changed back to a previous value', function () { + + typeSearchText('te'); + typeSearchText('tes'); + typeSearchText('te'); + // Double check the options list is still correct + var optionList = React.findDOMNode(instance).querySelectorAll('.Select-menu .Select-option'); + expect(optionList, 'to have length', 3); + expect(optionList[0], 'to have text', 'TEST one'); + expect(optionList[1], 'to have text', 'TEST two'); + expect(optionList[2], 'to have text', 'TELL three'); + }); + + it('re-filters an existing options list if complete:true is provided', function () { + + asyncOptions.withArgs('te').callsArgWith(1, null, { + options: [ + { value: 'test', label: 'TEST one' }, + { value: 'test2', label: 'TEST two' }, + { value: 'tell', label: 'TELL three' } + ], + complete: true + }); + + typeSearchText('te'); + expect(asyncOptions, 'was called times', 2); + typeSearchText('tel'); + expect(asyncOptions, 'was called times', 2); + var optionList = React.findDOMNode(instance).querySelectorAll('.Select-menu .Select-option'); + expect(optionList, 'to have length', 1); + expect(optionList[0], 'to have text', 'TELL three'); + }); + + it('rethrows the error if err is set in the callback', function () { + + asyncOptions.withArgs('tes').callsArgWith(1, new Error('Something\'s wrong jim'), { + options: [ + { value: 'test', label: 'TEST one' }, + { value: 'test2', label: 'TEST two' } + ] + }); + + + expect(function () { + typeSearchText('tes'); + }, 'to throw exception', new Error('Something\'s wrong jim')); + }); + + + }); + + describe('with autoload=false', function () { + + beforeEach(function () { + + // Render an instance of the component + instance = TestUtils.renderIntoDocument( + + ); + + // Focus on the input, such that mouse events are accepted + searchInputNode = instance.getInputNode().getDOMNode().querySelector('input'); + TestUtils.Simulate.focus(searchInputNode); + + }); + + it('selects a single option on enter', function () { + + typeSearchText('fo'); + pressEnterToAccept(); + expect(onChange, 'was called with', 'four', [{ label: 'Four', value: 'four' }]); + }); + + it('selects a second option', function () { + + typeSearchText('fo'); + pressEnterToAccept(); + typeSearchText('th'); + onChange.reset(); // Ignore previous onChange calls + pressEnterToAccept(); + expect(onChange, 'was called with', 'four,three', + [{ label: 'Four', value: 'four' }, { label: 'Three', value: 'three' }]); + }); + + it('displays both selected options', function () { + + typeSearchText('fo'); + pressEnterToAccept(); + typeSearchText('th'); + pressEnterToAccept(); + expect(React.findDOMNode(instance).querySelectorAll('.Select-item-label')[0], + 'to have text', 'Four'); + expect(React.findDOMNode(instance).querySelectorAll('.Select-item-label')[1], + 'to have text', 'Three'); }); + it('removes the last selected option with backspace', function () { + + typeSearchText('fo'); + pressEnterToAccept(); + typeSearchText('th'); + pressEnterToAccept(); + onChange.reset(); // Ignore previous onChange calls + pressBackspace(); + expect(onChange, 'was called with', 'four', [{ label: 'Four', value: 'four' }]); + }); + + it('removes an item when clicking on the X', function () { + + typeSearchText('fo'); + pressEnterToAccept(); + typeSearchText('th'); + pressEnterToAccept(); + typeSearchText('tw'); + pressEnterToAccept(); + onChange.reset(); // Ignore previous onChange calls + + var threeDeleteButton = React.findDOMNode(instance).querySelectorAll('.Select-item-icon')[1]; + TestUtils.Simulate.click(threeDeleteButton); + + expect(onChange, 'was called with', 'four,two', [ + { label: 'Four', value: 'four' }, + { label: 'Two', value: 'two' } + ]); + }); + + it('uses the selected text as an item when comma is pressed', function () { + + typeSearchText('fo'); + pressEnterToAccept(); + typeSearchText('new item'); + onChange.reset(); + + TestUtils.Simulate.keyDown(searchInputNode, { keyCode: 188, key: ',' }); + expect(onChange, 'was called with', 'four,new item', [ + { value: 'four', label: 'Four' }, + { value: 'new item', label: 'new item' } + ]); + + }); + + }); + + describe('with props', function () { + + + describe('className', function () { + + it('assigns the className to the outer-most element', function () { + + var instance = createControl({ className: 'test-class' }); + expect(React.findDOMNode(instance), 'to have attributes', { + class: 'test-class' + }); + }); + }); + + describe('clearable=true', function () { + + beforeEach(function () { + + var instance = createControl({ + clearable: true, + options: defaultOptions, + value: 'three' + }); + + expect(React.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR, + 'to have items satisfying', 'to have text', 'Three'); + + }); + + describe('on pressing escape', function () { + + beforeEach(function () { + + pressEscape(); + }); + + it('calls onChange with empty', function () { + + expect(onChange, 'was called with', ''); + }); + + it('resets the display value', function () { + + expect(React.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR, + 'to have items satisfying', 'to have text', 'Select...'); + }); + + it('resets the control value', function () { + + expect(React.findDOMNode(instance).querySelector('input').value, 'to equal', ''); + }); + }); + + describe('on clicking `clear`', function () { + + beforeEach(function () { + TestUtils.Simulate.click(React.findDOMNode(instance).querySelector('.Select-clear')); + }); + + it('calls onChange with empty', function () { + + expect(onChange, 'was called with', ''); + }); + + it('resets the display value', function () { + + expect(React.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR, + 'to have items satisfying', 'to have text', 'Select...'); + }); + + it('resets the control value', function () { + + expect(React.findDOMNode(instance).querySelector('input').value, 'to equal', ''); + }); + }); + }); + + describe('clearable=false', function () { + + beforeEach(function () { + + var instance = createControl({ + clearable: false, + options: defaultOptions, + value: 'three' + }); + + expect(React.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR, + 'to have items satisfying', 'to have text', 'Three'); + + }); + + it('does not render a clear button', function () { + + expect(React.findDOMNode(instance).querySelectorAll('.Select-clear'), 'to have length', 0); + }); + + describe('on escape', function () { + beforeEach(function () { + + pressEscape(); + }); + + it('does not call onChange', function () { + + expect(onChange, 'was not called'); + }); + + it('does not reset the display value', function () { + + expect(React.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR, + 'to have items satisfying', 'to have text', 'Three'); + }); + + it('does not reset the control value', function () { + + expect(React.findDOMNode(instance).querySelector('input').value, 'to equal', 'three'); + }); + + }); + + describe('when open', function () { + + beforeEach(function () { + + typeSearchText('abc'); + }); + + describe('on escape', function () { + + beforeEach(function () { + pressEscape(); + }); + + it('closes the menu', function () { + + expect(React.findDOMNode(instance).querySelectorAll('.Select-menu'), 'to have length', 0); + }); + + it('resets the control value to the original', function () { + + expect(React.findDOMNode(instance).querySelector('input').value, 'to equal', 'three'); + }); + + it('renders the original display label', function () { + + expect(React.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR, + 'to have items satisfying', 'to have text', 'Three'); + }); + }); + }); + }); + + describe('clearAllText', function () { + + beforeEach(function () { + + instance = createControl({ + multi: true, + clearable: true, + value: 'three', + clearAllText: 'Remove All Items Test Title', + clearValueText: 'Remove Value Test Title', // Should be ignored, multi=true + options: defaultOptions + }); + }); + + it('uses the prop as the title for clear', function () { + + expect(React.findDOMNode(instance).querySelector('.Select-clear'), 'to have attributes', { + title: 'Remove All Items Test Title' + }); + }); + }); + + describe('clearValueText', function () { + + beforeEach(function () { + + instance = createControl({ + multi: false, + clearable: true, + value: 'three', + clearAllText: 'Remove All Items Test Title', // Should be ignored, multi=false + clearValueText: 'Remove Value Test Title', + options: defaultOptions + }); + }); + + it('uses the prop as the title for clear', function () { + + expect(React.findDOMNode(instance).querySelector('.Select-clear'), 'to have attributes', { + title: 'Remove Value Test Title' + }); + }); + }); + + describe('delimiter', function () { + + describe('is ;', function () { + + beforeEach(function () { + + instance = createControl({ + multi: true, + value: 'four;three', + delimiter: ';', + options: defaultOptions + }); + }); + + it('interprets the initial options correctly', function () { + + var values = React.findDOMNode(instance).querySelectorAll('.Select-item'); + + expect(values[0], 'queried for', '.Select-item-label', 'to have items satisfying', + 'to have text', 'AbcDef'); + expect(values[1], 'queried for', '.Select-item-label', 'to have items satisfying', + 'to have text', 'Three'); + expect(values, 'to have length', 2); + }); + + it('adds an additional option with the correct delimiter', function () { + + typeSearchText('one'); + pressEnterToAccept(); + expect(onChange, 'was called with', 'four;three;one', [ + { value: 'four', label: 'AbcDef' }, + { value: 'three', label: 'Three' }, + { value: 'one', label: 'One' } + ]); + }); + }); + + describe('is a multi-character string (`==XXX==`)', function () { + + beforeEach(function () { + + instance = createControl({ + multi: true, + value: 'four==XXX==three', + delimiter: '==XXX==', + options: defaultOptions + }); + }); + + it('interprets the initial options correctly', function () { + + var values = React.findDOMNode(instance).querySelectorAll('.Select-item'); + + expect(values[0], 'queried for', '.Select-item-label', 'to have items satisfying', + 'to have text', 'AbcDef'); + expect(values[1], 'queried for', '.Select-item-label', 'to have items satisfying', + 'to have text', 'Three'); + expect(values, 'to have length', 2); + }); + + it('adds an additional option with the correct delimiter', function () { + + typeSearchText('one'); + pressEnterToAccept(); + expect(onChange, 'was called with', 'four==XXX==three==XXX==one', [ + { value: 'four', label: 'AbcDef' }, + { value: 'three', label: 'Three' }, + { value: 'one', label: 'One' } + ]); + }); + }); + }); + + describe('disabled=true', function () { + + beforeEach(function () { + + instance = createControl({ + options: defaultOptions, + value: 'three', + disabled: true, + searchable: true + }); + }); + + it('does not render an input search control', function () { + + expect(searchInputNode, 'to be null'); + }); + + it('does not react to keyDown', function () { + + TestUtils.Simulate.keyDown(getSelectControl(instance), { keyCode: 40, key: 'ArrowDown' }); + expect(React.findDOMNode(instance).querySelectorAll('.Select-option'), 'to have length', 0); + }); + + it('does not respond to mouseDown', function () { + + TestUtils.Simulate.mouseDown(getSelectControl(instance)); + expect(React.findDOMNode(instance).querySelectorAll('.Select-option'), 'to have length', 0); + }); + + it('does not respond to mouseDown on the arrow', function () { + + TestUtils.Simulate.mouseDown(getSelectControl(instance).querySelector('.Select-arrow')); + expect(React.findDOMNode(instance).querySelectorAll('.Select-option'), 'to have length', 0); + }); + + it('renders the given value', function () { + + expect(React.findDOMNode(instance).querySelector(DISPLAYED_SELECTION_SELECTOR), 'to have text', 'Three'); + }); + }); + + describe('custom filterOption function', function () { + + // Custom function returns true only for value "four" + var filterOption = function (option) { + if (option.value === 'four') { + return true; + } + + return false; + }; + var spyFilterOption; + + beforeEach(function () { + + spyFilterOption = sinon.spy(filterOption); + + instance = createControl({ + options: defaultOptions, + filterOption: spyFilterOption + }); + }); + + it('calls the filter with each option', function () { + + expect(spyFilterOption, 'was called times', 4); + expect(spyFilterOption, 'was called with', defaultOptions[0], ''); + expect(spyFilterOption, 'was called with', defaultOptions[1], ''); + expect(spyFilterOption, 'was called with', defaultOptions[2], ''); + expect(spyFilterOption, 'was called with', defaultOptions[3], ''); + }); + + describe('when entering text', function () { + + beforeEach(function () { + + spyFilterOption.reset(); + typeSearchText('xyz'); + }); + + it('calls the filterOption function for each option', function () { + + expect(spyFilterOption, 'was called times', 4); + expect(spyFilterOption, 'was called with', defaultOptions[0], 'xyz'); + expect(spyFilterOption, 'was called with', defaultOptions[1], 'xyz'); + expect(spyFilterOption, 'was called with', defaultOptions[2], 'xyz'); + expect(spyFilterOption, 'was called with', defaultOptions[3], 'xyz'); + }); + + it('only shows the filtered option', function () { + + expect(React.findDOMNode(instance).querySelectorAll('.Select-option'), + 'to have length', 1); + + expect(React.findDOMNode(instance).querySelectorAll('.Select-option'), + 'to have items satisfying', + 'to have text', 'AbcDef'); + }); + }); + }); + + describe('custom filterOptions function', function () { + + var spyFilterOptions; + + beforeEach(function () { + + spyFilterOptions = sinon.stub(); + spyFilterOptions.returns([ + { label: 'Return One', value: 'one' }, + { label: 'Return Two', value: 'two' } + ]); + + instance = createControl({ + options: defaultOptions, + filterOptions: spyFilterOptions, + searchable: true + }); + }); + + it('calls the filterOptions function initially', function () { + + expect(spyFilterOptions, 'was called'); + }); + + it('calls the filterOptions function initially with the initial options', function () { + + expect(spyFilterOptions, 'was called with', defaultOptions, ''); + }); + + it('uses the returned options', function () { + + TestUtils.Simulate.mouseDown(React.findDOMNode(instance).querySelector('.Select-arrow')); + + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options[0], 'to have text', 'Return One'); + expect(options[1], 'to have text', 'Return Two'); + expect(options, 'to have length', 2); + }); + + it('calls the filterOptions function on text change', function () { + + typeSearchText('xyz'); + expect(spyFilterOptions, 'was called with', defaultOptions, 'xyz'); + }); + + it('uses new options after text change', function () { + + spyFilterOptions.returns([ + { value: 'abc', label: 'AAbbcc' }, + { value: 'def', label: 'DDeeff' } + ]); + typeSearchText('xyz'); + + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options[0], 'to have text', 'AAbbcc'); + expect(options[1], 'to have text', 'DDeeff'); + expect(options, 'to have length', 2); + }); + }); + + describe('ignoreCase=false', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + ignoreCase: false, + options: defaultOptions + }); + }); + + it('does not find options in a different case', function () { + + typeSearchText('def'); + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options, 'to have length', 0); + }); + + it('finds options in the same case', function () { + + typeSearchText('Def'); + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options[0], 'to have text', 'AbcDef'); + expect(options, 'to have length', 1); + }); + }); + + describe('inputProps', function () { + + + beforeEach(function () { + + instance = createControl({ + searchable: true, + inputProps: { + inputClassName: 'extra-input-class', + className: 'extra-class-name', + id: 'search-input-id' + }, + options: defaultOptions + }); + }); + + it('passes id through to the search input box', function () { + expect(searchInputNode, 'to have attributes', { + id: 'search-input-id' + }); + }); + + it('passes the inputClassName to the search input box', function () { + + expect(searchInputNode, 'to have attributes', { + class: 'extra-input-class' + }); + }); + + it('adds the className on to the auto-size input', function () { + + expect(React.findDOMNode(instance.getInputNode()), + 'to have attributes', { + class: ['extra-class-name', 'Select-input'] + }); + }); + + describe('and not searchable', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: false, + inputProps: { + inputClassName: 'extra-input-class', + className: 'extra-class-name', + id: 'search-input-id' + }, + options: defaultOptions + }); + }); + + it('sets the className and id on the placeholder for the input', function () { + + expect(React.findDOMNode(instance).querySelector('.extra-class-name'), + 'to have attributes', { + id: 'search-input-id' + }); + }); + }); + + describe('and disabled', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + disabled: true, + inputProps: { + inputClassName: 'extra-input-class', + className: 'extra-class-name', + id: 'search-input-id' + }, + options: defaultOptions + }); + }); + + it('doesn\'t pass the inputProps through', function () { + + expect(React.findDOMNode(instance).querySelectorAll('.extra-class-name'), 'to have length', 0); + expect(React.findDOMNode(instance).querySelectorAll('#search-input-id'), 'to have length', 0); + }); + }); + }); + + describe('matchPos=start', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + matchPos: 'start', + options: defaultOptions + }); + }); + + it('searches only at the start', function () { + + typeSearchText('o'); + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options[0], 'to have text', 'One'); + expect(options, 'to have length', 1); + }); + }); + + describe('matchProp=value', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + matchProp: 'value', + options: [ + { value: 'aaa', label: '111' }, + { value: 'bbb', label: '222' }, + { value: 'ccc', label: 'Three' }, + { value: 'four', label: 'Abcaaa' } + ] + }); + }); + + it('searches only the value', function () { + + typeSearchText('aa'); // Matches value "three", and label "AbcDef" + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options, 'to have length', 1); + expect(options[0], 'to have text', '111'); + }); + }); + + describe('matchProp=label', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + matchProp: 'label', + options: [ + { value: 'aaa', label: 'bbb' }, + { value: 'bbb', label: '222' }, + { value: 'ccc', label: 'Three' }, + { value: 'four', label: 'Abcaaa' } + ] + }); + }); + + it('searches only the value', function () { + + typeSearchText('bb'); // Matches value "three", and label "AbcDef" + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options, 'to have length', 1); + expect(options[0], 'to have text', 'bbb'); + }); + }); + + describe('matchPos=start and matchProp=value', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + matchProp: 'value', + matchPos: 'start', + options: [ + { value: 'aaa', label: '111' }, + { value: 'bbb', label: '222' }, + { value: 'cccaa', label: 'Three' }, + { value: 'four', label: 'aaAbca' } + ] + }); + }); + + it('searches only the value', function () { + + typeSearchText('aa'); // Matches value "three", and label "AbcDef" + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options, 'to have length', 1); + expect(options[0], 'to have text', '111'); + }); + }); + + describe('matchPos=start and matchProp=label', function () { + + beforeEach(function () { + + instance = createControl({ + searchable: true, + matchProp: 'label', + matchPos: 'start', + options: [ + { value: 'aaa', label: 'bbb' }, + { value: 'bbb', label: '222' }, + { value: 'cccbbb', label: 'Three' }, + { value: 'four', label: 'Abcbbb' } + ] + }); + }); + + it('searches only the label', function () { + + typeSearchText('bb'); // Matches value "three", and label "AbcDef" + var options = React.findDOMNode(instance).querySelectorAll('.Select-option'); + expect(options, 'to have length', 1); + expect(options[0], 'to have text', 'bbb'); + }); + }); + + describe('onBlur', function () { + + var onBlur; + + it('calls the onBlur prop when blurring the input', function () { + + onBlur = sinon.spy(); + + instance = createControl({ + options: defaultOptions, + onBlur: onBlur + }); + + TestUtils.Simulate.blur(searchInputNode); + expect(onBlur, 'was called once'); + }); + }); + + describe('onFocus', function () { + + var onFocus; + + beforeEach(function () { + + onFocus = sinon.spy(); + + instance = createControl({ + options: defaultOptions, + onFocus: onFocus + }); + }); + + it('calls the onFocus prop when focusing the control', function () { + + expect(onFocus, 'was called once'); + }); + }); + }); + + describe('clicking outside', function () { + + beforeEach(function () { + + instance = createControl({ + options: defaultOptions + }); + }); + + it('closes the menu', function () { + + TestUtils.Simulate.mouseDown(getSelectControl(instance)); + expect(React.findDOMNode(instance), 'queried for', '.Select-option', + 'to have length', 4); + + clickDocument(); + expect(React.findDOMNode(instance).querySelectorAll('.Select-option'), + 'to have length', 0); + }); }); }); diff --git a/wallaby.js b/wallaby.js index 0a8cd8b128..d83b6f19dd 100644 --- a/wallaby.js +++ b/wallaby.js @@ -9,7 +9,8 @@ module.exports = function (wallaby) { tests: ['test/*-test.js' ], env: { - type: 'node' + type: 'node', + runner: 'node' }, preprocessors: {