Skip to content

Commit

Permalink
Reinstate combobox onblur pill creation in #1353 and fix bug in initi…
Browse files Browse the repository at this point in the history
…al implementation (#1364)

* Don't call onCreateOption callback when input is empty.
  • Loading branch information
cjcenizal authored Dec 11, 2018
1 parent faf3811 commit ee9da6c
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 5 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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))
Expand All @@ -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)

Expand Down
30 changes: 26 additions & 4 deletions src/components/combo_box/combo_box.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down
57 changes: 57 additions & 0 deletions src/components/combo_box/combo_box.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<EuiComboBox
options={options}
selectedOptions={[options[2]]}
onCreateOption={onCreateOptionHandler}
/>
);

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(
<EuiComboBox
options={options}
selectedOptions={[options[2]]}
onCreateOption={onCreateOptionHandler}
/>
);

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();
Expand All @@ -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(
<EuiComboBox
options={options}
selectedOptions={[options[2]]}
onCreateOption={onCreateOptionHandler}
/>
);

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(
Expand Down

0 comments on commit ee9da6c

Please sign in to comment.