Skip to content

Commit

Permalink
EuiComboBox with no custom option: bug fix, cleaned up example, added…
Browse files Browse the repository at this point in the history
… comment (#1796)

* Update documentation around EuiComboBox's invalid non-custom option, fixed a related UX bug

* changelog

* PR feedback

* Updated example to clear error state on component blur

* changelog

* More messaging hooks for EuiComboBox unsaved value

* Also trigger the combobox invalid visual state when the dropdown array is clicked
  • Loading branch information
chandlerprall authored Apr 26, 2019
1 parent 87f944c commit d6a299e
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Fixed `euiBreakpoint()` warning to give accurate feedback ([#1874](https://github.com/elastic/eui/pull/1874))
- Fixed type definitions around `EuiI18n`'s `default` prop to better support use cases ([#1861](https://github.com/elastic/eui/pull/1861))
- Localized `EuiTablePagination`'s row count selection ([#1883](https://github.com/elastic/eui/pull/1883))
- Fixed EuiComboBox's internal tracking of its focus state ([#1796](https://github.com/elastic/eui/pull/1796))
- Fixed `EuiComboBox` with `singleSelection` and `onAddCustomOption` reopening the options list after adding a custom option ([#1882](https://github.com/elastic/eui/pull/1882))
- Fixed `EuiComboBox` reopening the options list in Firefox when closing via the dropdown arrow button ([#1885](https://github.com/elastic/eui/pull/1885))

Expand Down
37 changes: 30 additions & 7 deletions src-docs/src/views/combo_box/disallow_custom_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Component } from 'react';

import {
EuiComboBox,
EuiFormRow,
} from '../../../../src/components';

export default class extends Component {
Expand Down Expand Up @@ -32,24 +33,46 @@ export default class extends Component {
}];

this.state = {
error: undefined,
selectedOptions: [this.options[2], this.options[4]],
};
}

onChange = (selectedOptions) => {
this.setState({
error: undefined,
selectedOptions,
});
};
}

onSearchChange = (value, hasMatchingOptions) => {
this.setState({
error: value.length === 0 || hasMatchingOptions ? undefined : `"${value}" is not a valid option`,
});
}

onBlur = () => {
const { value } = this.inputRef;
this.setState({
error: value.length === 0 ? undefined : `"${value}" is not a valid option`,
});
}

setInputRef = ref => this.inputRef = ref;

render() {
return (
<EuiComboBox
placeholder="Select from a list of options"
options={this.options}
selectedOptions={this.state.selectedOptions}
onChange={this.onChange}
/>
<EuiFormRow error={this.state.error} isInvalid={this.state.error !== undefined}>
<EuiComboBox
placeholder="Select from a list of options"
options={this.options}
selectedOptions={this.state.selectedOptions}
inputRef={this.setInputRef}
onChange={this.onChange}
onSearchChange={this.onSearchChange}
onBlur={this.onBlur}
/>
</EuiFormRow>
);
}
}
7 changes: 0 additions & 7 deletions src/components/combo_box/__snapshots__/combo_box.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ exports[`props full width is rendered 1`] = `
inputRef={[Function]}
isListOpen={false}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
Expand Down Expand Up @@ -131,7 +130,6 @@ exports[`props isClearable=false disallows user from clearing input when no opti
inputRef={[Function]}
isListOpen={false}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onCloseListClick={[Function]}
Expand Down Expand Up @@ -166,7 +164,6 @@ exports[`props isClearable=false disallows user from clearing input when options
inputRef={[Function]}
isListOpen={false}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onCloseListClick={[Function]}
Expand Down Expand Up @@ -211,7 +208,6 @@ exports[`props isDisabled is rendered 1`] = `
isDisabled={true}
isListOpen={false}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onCloseListClick={[Function]}
Expand Down Expand Up @@ -342,7 +338,6 @@ exports[`props selectedOptions are rendered 1`] = `
inputRef={[Function]}
isListOpen={false}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
Expand Down Expand Up @@ -387,7 +382,6 @@ exports[`props singleSelection is rendered 1`] = `
inputRef={[Function]}
isListOpen={false}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
Expand Down Expand Up @@ -429,7 +423,6 @@ exports[`props singleSelection selects existing option when opened 1`] = `
inputRef={[Function]}
isListOpen={true}
noIcon={false}
onBlur={[Function]}
onChange={[Function]}
onClear={[Function]}
onClick={[Function]}
Expand Down
14 changes: 7 additions & 7 deletions src/components/combo_box/combo_box.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ export class EuiComboBox extends Component {
if (this.props.onBlur) {
this.props.onBlur();
}
this.setState({ hasFocus: false });

// 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.
Expand All @@ -323,10 +324,6 @@ export class EuiComboBox extends Component {
}
}

onComboBoxBlur = () => {
this.setState({ hasFocus: false });
}

onKeyDown = (e) => {
switch (e.keyCode) {
case comboBoxKeyCodes.UP:
Expand Down Expand Up @@ -457,7 +454,8 @@ export class EuiComboBox extends Component {

onSearchChange = (searchValue) => {
if (this.props.onSearchChange) {
this.props.onSearchChange(searchValue);
const hasMatchingOptions = this.state.matchingOptions.length > 0;
this.props.onSearchChange(searchValue, hasMatchingOptions);
}

this.setState(
Expand Down Expand Up @@ -616,7 +614,10 @@ export class EuiComboBox extends Component {
} = this.props;
const { hasFocus, searchValue, isListOpen, listPosition, width, activeOptionIndex } = this.state;

const markAsInvalid = isInvalid || (hasFocus === false && searchValue);
// Visually indicate the combobox is in an invalid state if it has lost focus but there is text entered in the input.
// When custom options are disabled and the user leaves the combo box after entering text that does not match any
// options, this tells the user that they've entered invalid input.
const markAsInvalid = isInvalid || ((hasFocus === false || isListOpen === false) && searchValue);

const classes = classNames('euiComboBox', className, {
'euiComboBox-isOpen': isListOpen,
Expand Down Expand Up @@ -679,7 +680,6 @@ export class EuiComboBox extends Component {
placeholder={placeholder}
selectedOptions={selectedOptions}
onRemoveOption={this.onRemoveOption}
onBlur={this.onComboBoxBlur}
onClick={this.onComboBoxClick}
onChange={this.onSearchChange}
onFocus={this.onComboBoxFocus}
Expand Down

0 comments on commit d6a299e

Please sign in to comment.