This is an Internet Explorer 11 compatible version of moroshko's react-autosuggest. This version solves the issue experienced in ie11 in which a user's input is not registered in async applications (particularly those featuring a debounce stage).
We apply the fix given by postlight in this thread. We selectively apply this fix only to IE11 instances of the application. This allows us to continue to use the tests from moroshko, with no changes necessary.
Check out the Homepage and the Codepen examples.
- WAI-ARIA compliant, with support for ARIA attributes and keyboard interactions
- Mobile friendly
- Plugs in nicely to Flux and Redux applications
- Full control over suggestions rendering
- Suggestions can be presented as plain list or multiple sections
- Suggestions can be retrieved asynchronously
- Focus the first suggestion in the list if you wish
- Supports styling using CSS Modules, Radium, Inline styles, global CSS, and more
- You decide when to show suggestions (e.g. when user types 2 or more characters)
- Always render suggestions (useful for mobile and modals)
- Pass through arbitrary props to the input element (e.g. placeholder, type, onChange, onBlur, or any other), or take full control on the rendering of the input (useful for integration with other libraries)
- Thoroughly tested
yarn add react-autosuggest
or
npm install react-autosuggest --save
import Autosuggest from 'react-autosuggest';
// Imagine you have a list of languages that you'd like to autosuggest.
const languages = [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
},
...
];
// Teach Autosuggest how to calculate suggestions for any given input value.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : languages.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
};
// When suggestion is clicked, Autosuggest needs to populate the input element
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion.name;
// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
<div>
{suggestion.name}
</div>
);
class Example extends React.Component {
constructor() {
super();
// Autosuggest is a controlled component.
// This means that you need to provide an input value
// and an onChange handler that updates this value (see below).
// Suggestions also need to be provided to the Autosuggest,
// and they are initially empty because the Autosuggest is closed.
this.state = {
value: '',
suggestions: []
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
// Autosuggest will call this function every time you need to update suggestions.
// You already implemented this logic above, so just use it.
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
// Autosuggest will call this function every time you need to clear suggestions.
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render() {
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input element.
const inputProps = {
placeholder: 'Type a programming language',
value,
onChange: this.onChange
};
// Finally, render it!
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
);
}
}
Prop | Type | Required | Description |
---|---|---|---|
suggestions |
Array | âś“ | These are the suggestions that will be displayed. Items can take an arbitrary shape. |
onSuggestionsFetchRequested |
Function | âś“ | Will be called every time you need to recalculate suggestions . |
onSuggestionsClearRequested |
Function | âś“* | Will be called every time you need to set suggestions to [] . |
getSuggestionValue |
Function | âś“ | Implement it to teach Autosuggest what should be the input value when suggestion is clicked. |
renderSuggestion |
Function | âś“ | Use your imagination to define how suggestions are rendered. |
inputProps |
Object | âś“ | Pass through arbitrary props to the input element. It must contain at least value and onChange . |
onSuggestionSelected |
Function | Will be called every time suggestion is selected via mouse or keyboard. | |
shouldRenderSuggestions |
Function | When the input element is focused, Autosuggest will consult this function when to render suggestions. Use it, for example, if you want to display suggestions when input value is at least 2 characters long. | |
alwaysRenderSuggestions |
Boolean | Set it to true if you'd like to render suggestions even when the input element is not focused. |
|
focusFirstSuggestion |
Boolean | Set it to true if you'd like Autosuggest to automatically highlight the first suggestion. |
|
focusInputOnSuggestionClick |
Boolean | Set it to false if you don't want Autosuggest to keep the input element focused when suggestions are clicked/tapped. |
|
multiSection |
Boolean | Set it to true if you'd like to display suggestions in multiple sections (with optional titles). |
|
renderSectionTitle |
Function | âś“ when multiSection={true} |
Use your imagination to define how section titles are rendered. |
getSectionSuggestions |
Function | âś“ when multiSection={true} |
Implement it to teach Autosuggest where to find the suggestions for every section. |
renderInputComponent |
Function | Use it only if you need to customize the rendering of the input element. | |
renderSuggestionsContainer |
Function | Use it if you want to customize things inside the suggestions container beyond rendering the suggestions themselves. | |
theme |
Object | Use your imagination to style the Autosuggest. | |
id |
String | Use it only if you have multiple Autosuggest components on a page. |
Array of suggestions to display. The only requirement is that suggestions
is an array. Items in this array can take an arbitrary shape.
For a plain list of suggestions, every item in suggestions
should be a single suggestion. It's up to you what shape every suggestion takes. For example:
const suggestions = [
{
text: 'Apple'
},
{
text: 'Banana'
},
{
text: 'Cherry'
},
{
text: 'Grapefruit'
},
{
text: 'Lemon'
}
];
To display multiple sections, every item in suggestions
should be a single section. Again, it's up to you what shape every section takes. For example:
const suggestions = [
{
title: 'A',
suggestions: [
{
id: '100',
text: 'Apple'
},
{
id: '101',
text: 'Apricot'
}
]
},
{
title: 'B',
suggestions: [
{
id: '102',
text: 'Banana'
}
]
},
{
title: 'C',
suggestions: [
{
id: '103',
text: 'Cherry'
}
]
}
];
This function will be called every time you need to update suggestions
. It has the following signature:
function onSuggestionsFetchRequested({ value })
where value
is current value of the input.
This function will be called every time you need to clear suggestions
.
All you have to do in this function is to set suggestions
to []
.
Note: When alwaysRenderSuggestions={true}
, you don't have to implement this function.
When user navigates the suggestions using the Up and Down keys, the input value should be set according to the highlighted suggestion. You design how suggestion is modelled. Therefore, it's your responsibility to tell Autosuggest how to map suggestions to input values.
This function gets the suggestion in question, and it should return a string. For example:
function getSuggestionValue(suggestion) {
return suggestion.text;
}
Use your imagination to define how suggestions are rendered.
The signature is:
function renderSuggestion(suggestion, { query })
where:
suggestion
- The suggestion to renderquery
- Used to highlight the matching string. As user types in the input element,query
will be equal to the trimmed value of the input. Then, if user interacts using the Up or Down keys, the input element will get the value of the highlighted suggestion, butquery
will remain to be equal to the trimmed value of the input element prior to the Up and Down interactions.
It should return a string or a ReactElement
. For example:
function renderSuggestion(suggestion) {
return (
<span>{suggestion.text}</span>
);
}
Important: renderSuggestion
must be a pure function (we optimize rendering performance based on this assumption).
Autosuggest is a controlled component. Therefore, you should pass at least a value
and an onChange
callback to the input element. You can pass any other props as well. For example:
const inputProps = {
value, // usually comes from the application state
onChange, // called every time the input value changes
onBlur, // called when the input element loses focus, e.g. when user presses Tab
type: 'search',
placeholder: 'Enter city or postcode'
};
The signature is:
function onChange(event, { newValue, method })
where:
newValue
- the new value of the input elementmethod
- string describing how the change has occurred. The possible values are:'down'
- user pressed Down'up'
- user pressed Up'escape'
- user pressed Escape'enter'
- user pressed Enter'click'
- user clicked (or tapped) on suggestion'type'
- none of the methods above (usually means that user typed something, but can also be that they pressed Backspace, pasted something into the input element, etc.)
The signature is:
function onBlur(event, { focusedSuggestion })
where:
focusedSuggestion
- the suggestion that was highlighted just before the input element lost focus, ornull
if there was no highlighted suggestion.
This function is called when suggestion is selected. It has the following signature:
function onSuggestionSelected(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method })
where:
suggestion
- the selected suggestionsuggestionValue
- the value of the selected suggestion (equivalent togetSuggestionValue(suggestion)
)suggestionIndex
- the index of the selected suggestion in thesuggestions
arraysectionIndex
- when rendering multiple sections, this will be the section index (insuggestions
) of the selected suggestion. Otherwise, it will benull
.method
- string describing how user selected the suggestion. The possible values are:'click'
- user clicked (or tapped) on the suggestion'enter'
- user selected the suggestion using Enter
By default, suggestions are rendered when the input element isn't blank. Feel free to override this behaviour.
This function gets the current value of the input, and it should return a boolean.
For example, to display suggestions only when input value is at least 3 characters long, do:
function shouldRenderSuggestions(value) {
return value.trim().length > 2;
}
When shouldRenderSuggestions
returns true
, suggestions will be rendered only when the input element is focused.
If you would like to render suggestions regardless of whether the input element is focused or not, set alwaysRenderSuggestions={true}
(shouldRenderSuggestions
is ignored in this case).
Set alwaysRenderSuggestions={true}
if you'd like to always render the suggestions.
Important: Make sure to set the initial value of suggestions
to match the initial value of inputProps.value
.
When focusFirstSuggestion={true}
, Autosuggest will automatically highlight the first suggestion. Defaults to false
.
By default, focusInputOnSuggestionClick={true}
, which means that, every time suggestion is clicked (or tapped), the input element keeps the focus.
On mobile devices, when the input element is focused, the native keyboard appears. You'll probably want to lose the focus when suggestion is tapped in order to hide the keyboard.
You can do something like this:
<Autosuggest focusInputOnSuggestionClick={!isMobile} ... />
where isMobile
is a boolean describing whether Autosuggest operates on a mobile device or not. You can use kaimallea/isMobile, for example, to determine that.
By default, Autosuggest renders a plain list of suggestions.
If you'd like to have multiple sections (with optional titles), set multiSection={true}
.
When rendering multiple sections, you need to tell Autosuggest how to render a section title.
This function gets the section to render (an item in the suggestions array), and it should return a string or a ReactElement
. For example:
function renderSectionTitle(section) {
return (
<strong>{section.title}</strong>
);
}
If renderSectionTitle
returns null
or undefined
, section title is not rendered.
When rendering multiple sections, you need to tell Autosuggest where to find the suggestions for a given section.
This function gets the section to render (an item in the suggestions array), and it should return an array of suggestions to render in the given section. For example:
function getSectionSuggestions(section) {
return section.suggestions;
}
Note: Sections with no suggestions are not rendered.
You shouldn't specify renderInputComponent
unless you want to customize the rendering of the input element.
To keep Autosuggest accessible, renderInputComponent
should:
- render an input element
- pass through all the provided
inputProps
to the input element
Example:
const renderInputComponent = inputProps => (
<div>
<input {...inputProps} />
<div>custom stuff</div>
</div>
);
Note: When using renderInputComponent
, you still need to specify the usual inputProps
. Autosuggest will merge the inputProps
that you provide with other props that are needed for accessibility (e.g. 'aria-activedescendant'
), and will pass the merged inputProps
to renderInputComponent
.
You shouldn't specify renderSuggestionsContainer
unless you want to customize the content or behaviour of the suggestions container beyond rendering the suggestions themselves. For example, you might want to add a custom text before/after the suggestions list, or customize the scrolling behaviour of the suggestions container.
The signature is:
function renderSuggestionsContainer(props)
You should pass all the props
to the topmost element that is returned from renderSuggestionsContainer
with the following exceptions:
children
- these are the suggestions themselves. It's up to you where to render them.ref
- whenrenderSuggestionsContainer
returns a composite component (e.g.<IsolatedScroll ... />
as opposed to a DOM node like<div ... />
), you should callref
with the topmost element that the composite component renders.
Example:
function renderSuggestionsContainer({ children, ...rest }) {
return (
<div {...rest}>
<p>
Some text
</p>
{children}
</div>
);
}
import IsolatedScroll from 'react-isolated-scroll';
function renderSuggestionsContainer({ ref, ...rest }) {
const callRef = isolatedScroll => {
if (isolatedScroll !== null) {
ref(isolatedScroll.component);
}
};
return (
<IsolatedScroll {...rest} ref={callRef} />
);
}
Autosuggest comes with no styles.
It uses react-themeable to allow you to style your Autosuggest component using CSS Modules, Radium, React Style, JSS, Inline styles, or even global CSS.
For example, to style the Autosuggest using CSS Modules, do:
/* theme.css */
.container { ... }
.input { ... }
.suggestionsContainer { ... }
.suggestion { ... }
.suggestionFocused { ... }
...
import theme from 'theme.css';
<Autosuggest theme={theme} ... />
When not specified, theme
defaults to:
{
container: 'react-autosuggest__container',
containerOpen: 'react-autosuggest__container--open',
input: 'react-autosuggest__input',
suggestionsContainer: 'react-autosuggest__suggestions-container',
suggestionsList: 'react-autosuggest__suggestions-list',
suggestion: 'react-autosuggest__suggestion',
suggestionFocused: 'react-autosuggest__suggestion--focused',
sectionContainer: 'react-autosuggest__section-container',
sectionTitle: 'react-autosuggest__section-title'
}
The following picture illustrates how theme
keys correspond to Autosuggest DOM structure:
The only reason id
exists, is to set ARIA attributes (they require a unique id).
When rendering a single Autosuggest, don't set the id
(it will be set to '1'
, by default).
When rendering multiple Autosuggest components on a page, make sure to give them unique id
s. For example:
<Autosuggest id="source" ... />
<Autosuggest id="destination" ... />
npm install
npm start
Now, open http://localhost:3000/demo/dist/index.html
and start hacking!