Skip to content

Instantly share code, notes, and snippets.

@CarsonF
Last active December 20, 2016 05:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CarsonF/d6796ed6d5b7a94f40caac03d29a5994 to your computer and use it in GitHub Desktop.
Save CarsonF/d6796ed6d5b7a94f40caac03d29a5994 to your computer and use it in GitHub Desktop.
Material UI AutoComplete Forced Selection
import React from 'react';
import ReactDOM from 'react-dom';
import { FormattedMessage } from 'react-intl';
import { AutoComplete as MuiAutoComplete } from 'material-ui';
class AutoComplete extends MuiAutoComplete {
/**
* Added callback param
*/
close(cb?: () => any) {
this.setState({
open: false,
anchorEl: null
}, cb);
if (this.props.onClose) {
this.props.onClose();
}
}
/**
* After calling close, dispatch onClosedAndBlurred (for validation)
*/
handleBlur = (event) => {
if (this.state.focusTextField && this.timerTouchTapCloseId === null) {
this.timerBlurClose = setTimeout(() => {
this.close(() => this.props.onClose(true));
}, 0);
}
if (this.props.onBlur) {
this.props.onBlur(event);
}
};
/**
* Change enter to be the same as down arrow.
* AKA force selection instead of closing box.
*/
handleKeyDown = (event) => {
if (this.props.onKeyDown) this.props.onKeyDown(event);
switch (event.which) {
case 27: // escape
this.close();
break;
case 13: // enter
case 40: // down
event.preventDefault();
this.setState({
open: true,
focusTextField: false,
anchorEl: ReactDOM.findDOMNode(this.refs['searchTextField']),
});
break;
}
};
/**
* Refocus text field to encourage dropdown selection.
*/
handleEscKeyDown = () => {
this.close();
this.focus();
}
}
export class ForcedSelectionAutoComplete extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
data: [],
searchText: '',
textIsValidSelection: true,
blurredWithoutSelection: false,
};
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.searchText && nextProps.searchText !== this.props.searchText) {
this.setState({searchText: nextProps.searchText});
}
}
render() {
let errorText;
if (this.state.errorText) {
errorText = this.state.errorText;
} else if (this.state.blurredWithoutSelection && !this.state.textIsValidSelection) {
errorText = <FormattedMessage id='dropdown.make_choice' defaultMessage='Select an item from the dropdown' />;
}
// Created this way because definition is crap.
// - dataSourceConfig is missing
// - text props are strings instead of nodes
// - dataSource and onNewRequest don't have to be the definitions given (due to dataSourceConfig).
return React.createElement(AutoComplete, Object.assign({
dataSource: this.state.data,
filter: AutoComplete.noFilter,
onUpdateInput: this.handleUpdateInput,
onNewRequest: this.handleNewRequest,
onFocus: this.handleFocus,
onClose: this.handleClose,
openOnFocus: true,
errorText
}, this.props, {
searchText: this.state.searchText,
}));
}
protected handleClose = (blurred = false) => {
if (blurred) {
this.handleCloseAndBlurred();
}
};
handleCloseAndBlurred = () => {
this.setState({
blurredWithoutSelection: true,
});
if (this.state.searchText.length == 0) {
this.props.onNewSelection && this.props.onNewSelection(null);
}
};
handleFocus = (e) => {
this.setState({
blurredWithoutSelection: false,
});
if (this.props.onFocus) {
this.props.onFocus(e);
}
};
handleUpdateInput = (value: string) => {
this.setState({
searchText: value,
errorText: null,
textIsValidSelection: value.length == 0,
});
if (value.length < 1) {
return;
}
// do logic, set state
};
handleNewRequest = (chosenItem, index) => {
this.setState({
textIsValidSelection: true,
});
// do logic
if (this.props.onNewSelection) {
this.props.onNewSelection(chosenItem, index);
}
};
}
@CarsonF
Copy link
Author

CarsonF commented Dec 20, 2016

Features:

  • Empty search text is valid
  • Once something is typed
    • enter and down arrows focus the dropdown
    • escape refocuses the search field
    • clicking away produces error text
  • Selecting an item from the list calls onNewSelection prop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment