this.menu = ref} role="listbox" className="Select-menu" id={this._instancePrefix + '-list'}
style={this.props.menuStyle}
onScroll={this.handleMenuScroll}
onMouseDown={this.handleMouseDownOnMenu}>
@@ -1074,8 +1105,8 @@ const Select = React.createClass({
render () {
let valueArray = this.getValueArray(this.props.value);
- let options = this.filterOptions(this.props.options || [], this.props.multi ? valueArray : null);
- this._visibleOptions = this.flattenOptions(options);
+ this._visibleOptions = this.filterFlatOptions(this.props.multi ? valueArray : null);
+ let options = this.unflattenOptions(this._visibleOptions);
let isOpen = typeof this.props.isOpen === 'boolean' ? this.props.isOpen : this.state.isOpen;
if (this.props.multi && !options.length && valueArray.length && !this.state.inputValue) isOpen = false;
const focusedOptionIndex = this.getFocusableOptionIndex(valueArray[0]);
@@ -1113,11 +1144,11 @@ const Select = React.createClass({
}
return (
-
this.wrapper = ref}
className={className}
style={this.props.wrapperStyle}>
{this.renderHiddenField(valueArray)}
-
this.control = ref}
className="Select-control"
style={this.props.style}
onKeyDown={this.handleKeyDown}
diff --git a/src/utils/defaultFilterOptions.js b/src/utils/defaultFilterOptions.js
new file mode 100644
index 00000000..8a5a783a
--- /dev/null
+++ b/src/utils/defaultFilterOptions.js
@@ -0,0 +1,38 @@
+import stripDiacritics from './stripDiacritics';
+
+function filterOptions (options, filterValue, excludeOptions, props) {
+ if (props.ignoreAccents) {
+ filterValue = stripDiacritics(filterValue);
+ }
+
+ if (props.ignoreCase) {
+ filterValue = filterValue.toLowerCase();
+ }
+
+ if (excludeOptions) excludeOptions = excludeOptions.map(i => i[props.valueKey]);
+
+ return options.filter(option => {
+ if (excludeOptions && excludeOptions.indexOf(option[props.valueKey]) > -1) return false;
+ if (props.filterOption) return props.filterOption.call(this, option, filterValue);
+ if (!filterValue) return true;
+ var valueTest = String(option[props.valueKey]);
+ var labelTest = String(option[props.labelKey]);
+ if (props.ignoreAccents) {
+ if (props.matchProp !== 'label') valueTest = stripDiacritics(valueTest);
+ if (props.matchProp !== 'value') labelTest = stripDiacritics(labelTest);
+ }
+ if (props.ignoreCase) {
+ if (props.matchProp !== 'label') valueTest = valueTest.toLowerCase();
+ if (props.matchProp !== 'value') labelTest = labelTest.toLowerCase();
+ }
+ return props.matchPos === 'start' ? (
+ (props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue) ||
+ (props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue)
+ ) : (
+ (props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0) ||
+ (props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0)
+ );
+ });
+}
+
+module.exports = filterOptions;
diff --git a/src/utils/defaultMenuRenderer.js b/src/utils/defaultMenuRenderer.js
new file mode 100644
index 00000000..c5a3833b
--- /dev/null
+++ b/src/utils/defaultMenuRenderer.js
@@ -0,0 +1,80 @@
+import classNames from 'classnames';
+import React from 'react';
+
+function isGroup (option) {
+ return option && Array.isArray(option.options);
+}
+
+function menuRenderer ({
+ focusedOption,
+ instancePrefix,
+ labelKey,
+ onFocus,
+ onOptionRef,
+ onSelect,
+ optionClassName,
+ optionComponent,
+ optionGroupComponent,
+ optionRenderer,
+ options,
+ valueArray,
+ valueKey,
+}) {
+ let OptionGroup = optionGroupComponent;
+ let Option = optionComponent;
+ let renderLabel = optionRenderer || this.getOptionLabel;
+
+ const renderOptions = (optionsSubset) => {
+ return optionsSubset.map((option, i) => {
+ if (isGroup(option)) {
+ let optionGroupClass = classNames({
+ 'Select-option-group': true,
+ });
+
+ return (
+
+ {renderOptions(option.options)}
+
+ );
+ } else {
+ let isSelected = valueArray && valueArray.indexOf(option) > -1;
+ let isFocused = option === focusedOption;
+ let optionRef = isFocused ? 'focused' : null;
+ let optionClass = classNames(optionClassName, {
+ 'Select-option': true,
+ 'is-selected': isSelected,
+ 'is-focused': isFocused,
+ 'is-disabled': option.disabled,
+ });
+
+ return (
+ { onOptionRef(ref, isFocused); }}
+ >
+ {renderLabel(option, i)}
+
+ );
+ }
+ });
+ };
+
+ return renderOptions(options);
+}
+
+module.exports = menuRenderer;
diff --git a/test/Creatable-test.js b/test/Creatable-test.js
new file mode 100644
index 00000000..aca1ffde
--- /dev/null
+++ b/test/Creatable-test.js
@@ -0,0 +1,185 @@
+'use strict';
+/* global describe, it, beforeEach */
+/* eslint react/jsx-boolean-value: 0 */
+
+// Copied from Async-test verbatim; may need to be reevaluated later.
+var jsdomHelper = require('../testHelpers/jsdomHelper');
+jsdomHelper();
+var unexpected = require('unexpected');
+var unexpectedDom = require('unexpected-dom');
+var unexpectedReact = require('unexpected-react');
+var expect = unexpected
+ .clone()
+ .installPlugin(unexpectedDom)
+ .installPlugin(unexpectedReact);
+
+var React = require('react');
+var ReactDOM = require('react-dom');
+var TestUtils = require('react-addons-test-utils');
+var Select = require('../src/Select');
+
+describe('Creatable', () => {
+ let creatableInstance, creatableNode, filterInputNode, innserSelectInstance, renderer;
+
+ beforeEach(() => renderer = TestUtils.createRenderer());
+
+ const defaultOptions = [
+ { value: 'one', label: 'One' },
+ { value: 'two', label: '222' },
+ { value: 'three', label: 'Three' },
+ { value: 'four', label: 'AbcDef' }
+ ];
+
+ function createControl (props = {}) {
+ props.options = props.options || defaultOptions;
+ creatableInstance = TestUtils.renderIntoDocument(
+
+ );
+ creatableNode = ReactDOM.findDOMNode(creatableInstance);
+ innserSelectInstance = creatableInstance.select;
+ findAndFocusInputControl();
+ };
+
+ function findAndFocusInputControl () {
+ filterInputNode = creatableNode.querySelector('input');
+ if (filterInputNode) {
+ TestUtils.Simulate.focus(filterInputNode);
+ }
+ };
+
+ function typeSearchText (text) {
+ TestUtils.Simulate.change(filterInputNode, { target: { value: text } });
+ };
+
+ it('should render a decorated Select (with passed through properties)', () => {
+ createControl({
+ inputProps: {
+ className: 'foo'
+ }
+ });
+ expect(creatableNode.querySelector('.Select-input'), 'to have attributes', {
+ class: ['foo']
+ });
+ });
+
+ it('should add a placeholder "create..." prompt when filter text is entered that does not match any existing options', () => {
+ createControl();
+ typeSearchText('foo');
+ expect(creatableNode.querySelector('.Select-create-option-placeholder'), 'to have text', Select.Creatable.promptTextCreator('foo'));
+ });
+
+ it('should not show a "create..." prompt if current filter text is an exact match for an existing option', () => {
+ createControl({
+ isOptionUnique: () => false
+ });
+ typeSearchText('existing');
+ expect(creatableNode.querySelector('.Select-menu-outer').textContent, 'not to equal', Select.Creatable.promptTextCreator('existing'));
+ });
+
+ it('should not show a "create..." prompt if current filter text is not a valid option (as determined by :isValidNewOption prop)', () => {
+ createControl({
+ isValidNewOption: () => false
+ });
+ typeSearchText('invalid');
+ expect(creatableNode.querySelector('.Select-menu-outer').textContent, 'not to equal', Select.Creatable.promptTextCreator('invalid'));
+ });
+
+ it('should create (and auto-select) a new option when placeholder option is clicked', () => {
+ let selectedOption;
+ const options = [];
+ createControl({
+ onChange: (option) => selectedOption = option,
+ options
+ });
+ typeSearchText('foo');
+ TestUtils.Simulate.mouseDown(creatableNode.querySelector('.Select-create-option-placeholder'));
+ expect(options, 'to have length', 1);
+ expect(options[0].label, 'to equal', 'foo');
+ expect(selectedOption, 'to be', options[0]);
+ });
+
+ it('should create (and auto-select) a new option when ENTER is pressed while placeholder option is selected', () => {
+ let selectedOption;
+ const options = [];
+ createControl({
+ onChange: (option) => selectedOption = option,
+ options,
+ shouldKeyDownEventCreateNewOption: () => true
+ });
+ typeSearchText('foo');
+ TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 13 });
+ expect(options, 'to have length', 1);
+ expect(options[0].label, 'to equal', 'foo');
+ expect(selectedOption, 'to be', options[0]);
+ });
+
+ it('should not create a new option if the placeholder option is not selected but should select the focused option', () => {
+ const options = [{ label: 'One', value: 1 }];
+ createControl({
+ options,
+ shouldKeyDownEventCreateNewOption: ({ keyCode }) => keyCode === 13
+ });
+ typeSearchText('on'); // ['Create option "on"', 'One']
+ TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 40, key: 'ArrowDown' }); // Select 'One'
+ TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 13 });
+ expect(options, 'to have length', 1);
+ });
+
+ it('default :isOptionUnique function should do a simple equality check for value and label', () => {
+ const options = [
+ newOption('foo', 1),
+ newOption('bar', 2),
+ newOption('baz', 3)
+ ];
+
+ function newOption (label, value) {
+ return { label, value };
+ };
+
+ function test (option) {
+ return Select.Creatable.isOptionUnique({
+ labelKey: 'label',
+ option,
+ options,
+ valueKey: 'value'
+ });
+ };
+
+ expect(test(newOption('foo', 0)), 'to be', false);
+ expect(test(newOption('qux', 1)), 'to be', false);
+ expect(test(newOption('qux', 4)), 'to be', true);
+ expect(test(newOption('Foo', 11)), 'to be', true);
+ });
+
+ it('default :isValidNewOption function should just ensure a non-empty string is provided', () => {
+ function test (label) {
+ return Select.Creatable.isValidNewOption({ label });
+ };
+
+ expect(test(''), 'to be', false);
+ expect(test('a'), 'to be', true);
+ expect(test(' '), 'to be', true);
+ });
+
+ it('default :newOptionCreator function should create an option with a :label and :value equal to the label string', () => {
+ const option = Select.Creatable.newOptionCreator({
+ label: 'foo',
+ labelKey: 'label',
+ valueKey: 'value'
+ });
+ expect(option.className, 'to equal', 'Select-create-option-placeholder');
+ expect(option.label, 'to equal', 'foo');
+ expect(option.value, 'to equal', 'foo');
+ });
+
+ it('default :shouldKeyDownEventCreateNewOption function should accept TAB, ENTER, and comma keys', () => {
+ function test (keyCode) {
+ return Select.Creatable.shouldKeyDownEventCreateNewOption({ keyCode });
+ };
+
+ expect(test(9), 'to be', true);
+ expect(test(13), 'to be', true);
+ expect(test(188), 'to be', true);
+ expect(test(1), 'to be', false);
+ });
+});
diff --git a/test/Select-test.js b/test/Select-test.js
index 3fa366ad..53b9230f 100644
--- a/test/Select-test.js
+++ b/test/Select-test.js
@@ -46,12 +46,12 @@ class PropsWrapper extends React.Component {
}
getChild() {
- return this.refs.child;
+ return this.child;
}
render() {
var Component = this.props.childComponent; // eslint-disable-line react/prop-types
- return ;
+ return this.child = ref} />;
}
}
@@ -114,12 +114,12 @@ describe('Select', () => {
var clickArrowToOpen = () => {
var selectArrow = ReactDOM.findDOMNode(instance).querySelector('.Select-arrow');
- TestUtils.Simulate.mouseDown(selectArrow);
+ TestUtils.Simulate.mouseDown(selectArrow, { button: 0 });
};
var findAndFocusInputControl = () => {
// Focus on the input, such that mouse events are accepted
- var searchInstance = ReactDOM.findDOMNode(instance.refs.input);
+ var searchInstance = ReactDOM.findDOMNode(instance.input);
searchInputNode = null;
if (searchInstance) {
searchInputNode = searchInstance.querySelector('input');
@@ -202,7 +202,7 @@ describe('Select', () => {
{ value: 'ten', label: 'ten' }
];
- describe('with simple options', () => {
+ describe('has refs', () => {
beforeEach(() => {
options = [
{ value: 'one', label: 'One' },
@@ -215,9 +215,53 @@ describe('Select', () => {
value: 'one',
options: options,
simpleValue: true,
+ joinValues: true,
});
});
+ it('input', () => {
+ expect(instance.input, 'not to equal', undefined);
+ });
+
+ it('value', () => {
+ typeSearchText('o');
+ expect(instance.value, 'not to equal', undefined);
+ });
+
+ it('menuContainer', () => {
+ clickArrowToOpen();
+ expect(instance.menuContainer, 'not to equal', undefined);
+ });
+
+ it('menu', () => {
+ clickArrowToOpen();
+ expect(instance.menu, 'not to equal', undefined);
+ });
+
+ it('wrapper', () => {
+ expect(instance.wrapper, 'not to equal', undefined);
+ });
+
+ it('control', () => {
+ expect(instance.control, 'not to equal', undefined);
+ });
+ });
+
+ describe('with simple options', () => {
+ beforeEach(() => {
+ options = [
+ { value: 'one', label: 'One' },
+ { value: 'two', label: 'Two' },
+ { value: 'three', label: 'Three' }
+ ];
+
+ instance = createControl({
+ name: 'form-field-name',
+ value: 'one',
+ options: options,
+ simpleValue: true,
+ });
+ });
it('should assign the given name', () => {
var selectInputElement = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'input')[0];
@@ -225,13 +269,13 @@ describe('Select', () => {
});
it('should show the options on mouse click', function () {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'));
+ 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);
});
it('should display the labels on mouse click', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'), { button: 0 });
var node = ReactDOM.findDOMNode(instance);
expect(node, 'queried for', '.Select-option:nth-child(1)', 'to have items satisfying', 'to have text', 'One');
expect(node, 'queried for', '.Select-option:nth-child(2)', 'to have items satisfying', 'to have text', 'Two');
@@ -313,7 +357,7 @@ describe('Select', () => {
});
it('should focus the first value on mouse click', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'), { button: 0 });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
'to have text', 'One');
@@ -321,7 +365,7 @@ describe('Select', () => {
it('should move the focused value to the second value when down pressed', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 40, key: 'ArrowDown' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
@@ -330,7 +374,7 @@ describe('Select', () => {
it('should move the focused value to the second value when down pressed', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 40, key: 'ArrowDown' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 40, key: 'ArrowDown' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
@@ -340,7 +384,7 @@ describe('Select', () => {
it('should loop round to top item when down is pressed on the last item', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 40, key: 'ArrowDown' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 40, key: 'ArrowDown' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 40, key: 'ArrowDown' });
@@ -351,7 +395,7 @@ describe('Select', () => {
it('should loop round to bottom item when up is pressed on the first item', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 38, key: 'ArrowUp' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
@@ -360,7 +404,7 @@ describe('Select', () => {
it('should move the focused value to the second item when up pressed twice', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 38, key: 'ArrowUp' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 38, key: 'ArrowUp' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
@@ -370,7 +414,7 @@ describe('Select', () => {
it('should move the focused value to the end when pressing end', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 35, key: 'End' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
@@ -389,7 +433,7 @@ describe('Select', () => {
it('should move the focused value to the end if page down is pressed and number of items is less than page size', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 34, key: 'PageDown' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
@@ -406,7 +450,7 @@ describe('Select', () => {
});
var selectControl = getSelectControl(longerListInstance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 34, key: 'PageDown' });
expect(ReactDOM.findDOMNode(longerListInstance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
@@ -424,7 +468,7 @@ describe('Select', () => {
});
var selectControl = getSelectControl(longerListInstance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 34, key: 'PageDown' });
expect(ReactDOM.findDOMNode(longerListInstance), 'queried for', '.Select-option.is-focused',
'to have items satisfying',
@@ -433,7 +477,7 @@ describe('Select', () => {
it('should move the focused value to the start if page up is pressed and number of items is less than page size', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 34, key: 'PageDown' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 33, key: 'PageUp' });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option.is-focused',
@@ -451,7 +495,7 @@ describe('Select', () => {
});
var selectControl = getSelectControl(longerListInstance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 35, key: 'End' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 33, key: 'PageUp' });
expect(ReactDOM.findDOMNode(longerListInstance), 'queried for', '.Select-option.is-focused',
@@ -470,7 +514,7 @@ describe('Select', () => {
});
var selectControl = getSelectControl(longerListInstance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 35, key: 'End' });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 33, key: 'PageUp' });
expect(ReactDOM.findDOMNode(longerListInstance), 'queried for', '.Select-option.is-focused',
@@ -481,7 +525,7 @@ describe('Select', () => {
it('should clear the selection on escape', () => {
var selectControl = getSelectControl(instance);
- TestUtils.Simulate.mouseDown(selectControl);
+ TestUtils.Simulate.mouseDown(selectControl, { button: 0 });
TestUtils.Simulate.keyDown(selectControl, { keyCode: 27, key: 'Escape' });
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', '.Select-option');
@@ -508,16 +552,16 @@ describe('Select', () => {
it('should close the options one the second click on the arrow', () => {
var selectArrow = ReactDOM.findDOMNode(instance).querySelector('.Select-arrow');
- TestUtils.Simulate.mouseDown(selectArrow);
+ TestUtils.Simulate.mouseDown(selectArrow, { button: 0 });
expect(ReactDOM.findDOMNode(instance).querySelectorAll('.Select-option'), 'to have length', 3);
- TestUtils.Simulate.mouseDown(selectArrow);
+ TestUtils.Simulate.mouseDown(selectArrow, { button: 0 });
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', '.Select-option');
});
it('should ignore a right mouse click on the arrow', () => {
var selectArrow = ReactDOM.findDOMNode(instance).querySelector('.Select-arrow');
- TestUtils.Simulate.mouseDown(selectArrow, { type: 'mousedown', button: 1 });
+ TestUtils.Simulate.mouseDown(selectArrow, { button: 1 });
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', '.Select-option');
});
@@ -1081,7 +1125,24 @@ describe('Select', () => {
expect(ReactDOM.findDOMNode(instance), 'queried for', DISPLAYED_SELECTION_SELECTOR,
'to have items satisfying', 'to have text', 'New item in Group 1A');
+ });
+ it('matches searches for ungrouped items', () => {
+ typeSearchText('omega');
+ expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
+ 'to have items satisfying', 'to have text', 'Omega');
+ });
+
+ it('matches searches for shallowly grouped items', () => {
+ typeSearchText('alpha');
+ expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
+ 'to have items satisfying', 'to have text', 'Alpha');
+ });
+
+ it('matches searches for deeply grouped items', () => {
+ typeSearchText('beta');
+ expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
+ 'to have items satisfying', 'to have text', 'Beta');
});
});
@@ -1702,6 +1763,30 @@ describe('Select', () => {
});
+ describe('with multi=true and clearable=true', () => {
+ beforeEach(() => {
+
+ options = [
+ { value: 0, label: 'Zero' },
+ { value: 1, label: 'One' }
+ ];
+
+ wrapper = createControlWithWrapper({
+ value: [0],
+ options: options,
+ multi: true,
+ clearable: true
+ });
+
+ });
+
+ it('calls onChange with an empty list when cleared', () => {
+
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-clear'), { button: 0 });
+ expect(onChange, 'was called with', []);
+ });
+ });
+
describe('with multi=true and searchable=false', () => {
beforeEach(() => {
@@ -1930,7 +2015,7 @@ describe('Select', () => {
describe('on clicking `clear`', () => {
beforeEach(() => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-clear'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-clear'), { button: 0 });
});
it('calls onChange with empty', () => {
@@ -1981,7 +2066,7 @@ describe('Select', () => {
});
it('calls onChange with a custom resetValue', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-clear'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-clear'), { button: 0 });
expect(onChange, 'was called with', 'reset');
});
});
@@ -2255,7 +2340,7 @@ describe('Select', () => {
it('should close the opened menu if disabled=true', function(){
findAndFocusInputControl();
- TestUtils.Simulate.mouseDown(getSelectControl(instance));
+ TestUtils.Simulate.mouseDown(getSelectControl(instance), { button: 0 });
expect(node, 'queried for', '.Select-option', 'to have length', 4);
ReactDOM.render( , node);
expect(node, 'to contain no elements matching', '.Select-option');
@@ -2357,7 +2442,7 @@ describe('Select', () => {
it('uses the returned options', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-arrow'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-arrow'), { button: 0 });
var options = ReactDOM.findDOMNode(instance).querySelectorAll('.Select-option');
expect(options[0], 'to have text', 'Return One');
@@ -2443,7 +2528,7 @@ describe('Select', () => {
it('adds the className on to the auto-size input', () => {
- expect(ReactDOM.findDOMNode(instance.refs.input),
+ expect(ReactDOM.findDOMNode(instance.input),
'to have attributes', {
class: ['extra-class-name', 'Select-input']
});
@@ -2691,29 +2776,49 @@ describe('Select', () => {
expect(onBlur, 'was called once');
});
+ /*
+ TODO: This test doesn't work now that we're checking
+ this.menu === document.activeElement in the method. Needs and review to work
+ out whether it's possible to mock this.
+
it( 'should focus on the input when the menu is active', () => {
instance = createControl({
options: defaultOptions
});
clickArrowToOpen();
- instance.refs.menu.focus();
+ instance.menu.focus();
- var inputFocus = sinon.spy( instance.refs.input, 'focus' );
+ var inputFocus = sinon.spy( instance.input, 'focus' );
instance.handleInputBlur();
- expect( instance.refs.input.focus, 'was called once' );
+ expect( instance.input.focus, 'was called once' );
} );
+ */
it( 'should not focus the input when menu is not active', () => {
instance = createControl({
options: defaultOptions
});
- var inputFocus = sinon.spy( instance.refs.input, 'focus' );
+ var inputFocus = sinon.spy( instance.input, 'focus' );
+ instance.handleInputBlur();
+
+ expect( instance.input.focus, 'was not called' );
+ } );
+
+ it( 'should set onBlurredState', () => {
+ instance = createControl({
+ options: defaultOptions
+ });
+
+ var inputFocus = sinon.spy( instance.input, 'focus' );
instance.handleInputBlur();
- expect( instance.refs.input.focus, 'was not called' );
+ expect( instance.state.isFocused, 'to be false');
+ expect( instance.state.isOpen, 'to be false');
+ expect( instance.state.isPseudoFocused, 'to be false');
+
} );
});
@@ -2807,13 +2912,13 @@ describe('Select', () => {
it('calls the function when clicking on a label', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('a.Select-value-label'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('a.Select-value-label'), { button: 0 });
expect(onValueClick, 'was called once');
});
it('calls the function with the value', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelectorAll('a.Select-value-label')[0]);
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelectorAll('a.Select-value-label')[0], { button: 0 });
expect(onValueClick, 'was called with', { value: 'two', label: '222' });
});
});
@@ -2833,7 +2938,7 @@ describe('Select', () => {
});
it('is called when the options are displayed', () => {
- TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'));
+ TestUtils.Simulate.mouseDown(ReactDOM.findDOMNode(instance).querySelector('.Select-control'), { button: 0 });
expect(eventHandler, 'was called once');
});
});
@@ -2862,6 +2967,40 @@ describe('Select', () => {
});
});
+ describe('with onCloseResetsInput=true', () => {
+ beforeEach(() => {
+ instance = createControl({
+ options: defaultOptions,
+ onCloseResetsInput: true
+ });
+ typeSearchText('test');
+ });
+
+ it('should clear the search input after calling onBlur', () => {
+ const domNode = ReactDOM.findDOMNode(instance);
+ TestUtils.Simulate.mouseDown(domNode.querySelector('.Select-control'));
+ TestUtils.Simulate.keyDown(domNode.querySelector('input'), { keyCode: 27, key: 'Escape' });
+ expect(ReactDOM.findDOMNode(instance).querySelector('input').value, 'to equal', '');
+ });
+ });
+
+ describe('with onCloseResetsInput=false', () => {
+ beforeEach(() => {
+ instance = createControl({
+ options: defaultOptions,
+ onCloseResetsInput: false
+ });
+ typeSearchText('test');
+ });
+
+ it('should clear the search input after calling onClose', () => {
+ const domNode = ReactDOM.findDOMNode(instance);
+ TestUtils.Simulate.mouseDown(domNode.querySelector('.Select-control'));
+ TestUtils.Simulate.keyDown(domNode.querySelector('input'), { keyCode: 27, key: 'Escape' });
+ expect(ReactDOM.findDOMNode(instance).querySelector('input').value, 'to equal', 'test');
+ });
+ });
+
describe('optionRenderer', () => {
var optionRenderer;
@@ -2885,7 +3024,7 @@ describe('Select', () => {
it('renders the options using the optionRenderer', () => {
var selectArrow = ReactDOM.findDOMNode(instance).querySelector('.Select-arrow');
- TestUtils.Simulate.mouseDown(selectArrow);
+ TestUtils.Simulate.mouseDown(selectArrow, { button: 0 });
var options = ReactDOM.findDOMNode(instance).querySelectorAll('.Select-option');
expect(options[0].querySelector('span'), 'to have attributes', {
@@ -2900,7 +3039,7 @@ describe('Select', () => {
it('calls the renderer exactly once for each option', () => {
var selectArrow = ReactDOM.findDOMNode(instance).querySelector('.Select-arrow');
- TestUtils.Simulate.mouseDown(selectArrow);
+ TestUtils.Simulate.mouseDown(selectArrow, { button: 0 });
expect(optionRenderer, 'was called times', 4);
});
});
@@ -3418,24 +3557,29 @@ describe('Select', () => {
});
});
- describe('clicking outside', () => {
+ describe('outside event', () => {
beforeEach(() => {
-
instance = createControl({
options: defaultOptions
});
- });
-
- it('closes the menu', () => {
-
- TestUtils.Simulate.mouseDown(getSelectControl(instance));
+ TestUtils.Simulate.mouseDown(getSelectControl(instance), { button: 0 });
expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-option',
'to have length', 4);
+ });
+ it('click closes the menu', () => {
TestUtils.Simulate.blur(searchInputNode);
expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', '.Select-option');
});
+
+ it('touch closes the menu', () => {
+ const evt = document.createEvent('Event');
+ evt.initEvent('touchstart', true, true);
+ document.querySelector('body').dispatchEvent(evt);
+ expect(ReactDOM.findDOMNode(instance), 'to contain no elements matching', '.Select-option');
+ });
+
});
describe('with autosize=false', () => {
@@ -3446,7 +3590,7 @@ describe('Select', () => {
});
it('creates a plain input instead of an autosizable input', () => {
- const inputNode = ReactDOM.findDOMNode(instance.refs.input);
+ const inputNode = ReactDOM.findDOMNode(instance.input);
expect(inputNode.tagName, 'to equal', 'INPUT');
});
});
diff --git a/test/Value-test.js b/test/Value-test.js
index 48ff182b..e5b145a2 100644
--- a/test/Value-test.js
+++ b/test/Value-test.js
@@ -83,7 +83,7 @@ describe('Value component', function() {
});
it('calls a custom callback when the anchor is clicked', function() {
- TestUtils.Simulate.mouseDown(valueLabel);
+ TestUtils.Simulate.mouseDown(valueLabel, { button: 0 });
expect(props.onClick, 'was called');
});