diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0818f913767..10be84a2a0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
## [`master`](https://github.com/elastic/eui/tree/master)
+- Reinstate ([#1353](https://github.com/elastic/eui/pull/1353)) `onBlur` action on `EuiComboBox` ([#1364](https://github.com/elastic/eui/pull/1364))
+
+**Bug fixes**
+
+- Fixed `onCreateOption` callback of `EuiComboBox` so it isn't called when the input is empty ([#1364](https://github.com/elastic/eui/pull/1364))
- Added `anchorClassName` prop to `EuiPopover` ([#1367](https://github.com/elastic/eui/pull/1367))
- Added support for `fullWidth` on `EuiSuperSelect` ([#1367](https://github.com/elastic/eui/pull/1367))
- Applied new scrollbar customization for Firefox ([#1367](https://github.com/elastic/eui/pull/1367))
@@ -16,7 +21,7 @@
**Bug fixes**
- `react-datepicker` set milliseconds to zero when selecting time ([#1361](https://github.com/elastic/eui/pull/1361))
-- Revert ([#1353](https://github.com/elastic/eui/pull/1353)) `onBlur` action on `euiComboBox`. It caused regressions on Kibana. ([#1363](https://github.com/elastic/eui/pull/1363))
+- Revert ([#1353](https://github.com/elastic/eui/pull/1353)) `onBlur` action on `EuiComboBox`. It caused regressions on Kibana. ([#1363](https://github.com/elastic/eui/pull/1363))
## [`5.5.1`](https://github.com/elastic/eui/tree/v5.5.1)
diff --git a/src/components/combo_box/combo_box.js b/src/components/combo_box/combo_box.js
index c81d2f3170b..3989c9c557c 100644
--- a/src/components/combo_box/combo_box.js
+++ b/src/components/combo_box/combo_box.js
@@ -204,22 +204,38 @@ export class EuiComboBox extends Component {
};
addCustomOption = () => {
+ const {
+ options,
+ selectedOptions,
+ onCreateOption,
+ } = this.props;
+
+ const {
+ searchValue,
+ matchingOptions,
+ } = this.state;
+
if (this.doesSearchMatchOnlyOption()) {
- this.onAddOption(this.state.matchingOptions[0]);
+ this.onAddOption(matchingOptions[0]);
+ return;
+ }
+
+ if (!onCreateOption) {
return;
}
- if (!this.props.onCreateOption) {
+ // Don't bother trying to create an option if the user hasn't typed anything.
+ if (!searchValue) {
return;
}
// Don't create the value if it's already been selected.
- if (getSelectedOptionForSearchValue(this.state.searchValue, this.props.selectedOptions)) {
+ if (getSelectedOptionForSearchValue(searchValue, selectedOptions)) {
return;
}
// Add new custom pill if this is custom input, even if it partially matches an option..
- const isOptionCreated = this.props.onCreateOption(this.state.searchValue, flattenOptionGroups(this.props.options));
+ const isOptionCreated = this.props.onCreateOption(searchValue, flattenOptionGroups(options));
// Expect the consumer to be explicit in rejecting a custom option.
if (isOptionCreated === false) {
@@ -258,6 +274,12 @@ export class EuiComboBox extends Component {
this.closeList();
}
+ // If the user tabs away or changes focus to another element, take whatever input they've
+ // typed and convert it into a pill, to prevent the combo box from looking like a text input.
+ if (!this.hasActiveOption() && !focusedInInput) {
+ this.addCustomOption();
+ }
+
if (this.props.onBlur) {
this.props.onBlur(e);
}
diff --git a/src/components/combo_box/combo_box.test.js b/src/components/combo_box/combo_box.test.js
index da2a636adf5..fceb9d55682 100644
--- a/src/components/combo_box/combo_box.test.js
+++ b/src/components/combo_box/combo_box.test.js
@@ -135,6 +135,44 @@ describe('props', () => {
});
describe('behavior', () => {
+ describe('hitting "Enter"', () => {
+ test(`calls the onCreateOption callback when there is input`, () => {
+ const onCreateOptionHandler = sinon.spy();
+
+ const component = mount(
+
+ );
+
+ component.setState({ searchValue: 'foo' });
+ const searchInput = findTestSubject(component, 'comboBoxSearchInput');
+ searchInput.simulate('focus');
+ searchInput.simulate('keyDown', { keyCode: comboBoxKeyCodes.ENTER });
+ sinon.assert.calledOnce(onCreateOptionHandler);
+ sinon.assert.calledWith(onCreateOptionHandler, 'foo');
+ });
+
+ test(`doesn't the onCreateOption callback when there is no input`, () => {
+ const onCreateOptionHandler = sinon.spy();
+
+ const component = mount(
+
+ );
+
+ const searchInput = findTestSubject(component, 'comboBoxSearchInput');
+ searchInput.simulate('focus');
+ searchInput.simulate('keyDown', { keyCode: comboBoxKeyCodes.ENTER });
+ sinon.assert.notCalled(onCreateOptionHandler);
+ });
+ });
+
describe('tabbing', () => {
test(`off the search input closes the options list if the user isn't navigating the options`, () => {
const onKeyDownWrapper = jest.fn();
@@ -160,6 +198,25 @@ describe('behavior', () => {
expect(onKeyDownWrapper.mock.calls.length).toBe(1);
});
+ test(`off the search input calls onCreateOption`, () => {
+ const onCreateOptionHandler = sinon.spy();
+
+ const component = mount(
+
+ );
+
+ component.setState({ searchValue: 'foo' });
+ const searchInput = findTestSubject(component, 'comboBoxSearchInput');
+ searchInput.simulate('focus');
+ searchInput.simulate('blur');
+ sinon.assert.calledOnce(onCreateOptionHandler);
+ sinon.assert.calledWith(onCreateOptionHandler, 'foo');
+ });
+
test('off the search input does nothing if the user is navigating the options', () => {
const onKeyDownWrapper = jest.fn();
const component = mount(