JSPM

  • Created
  • Published
  • Downloads 305765
  • Score
    100M100P100Q216941F
  • License MIT

WAI-ARIA compliant React autosuggest component

Package Exports

  • react-autosuggest

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-autosuggest) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Build Status NPM Version NPM Downloads

React Autosuggest

WAI-ARIA compliant React autosuggest component.

Demo

Playground

Codepen

Features

The following are not part of react-autosuggest, but can be easily implemented:

  • Delayed requests management (if request comes back after user types another letter, it will be ignored). Example
  • In-memory caching (suggestions for a given input are retrieved only once). Example

Installation

npm install react-autosuggest --save

Basic Usage

This example doesn't use flux architecture. If you would like to connect Autosuggest to your redux app, check out the redux examples.

import Autosuggest from 'react-autosuggest';

const languages = [{
  name: 'C',
  year: 1972
}, {
  name: 'Elm',
  year: 2012
}, {
  name: 'Javascript',
  year: 1995
}, {
  name: 'Python',
  year: 1991
}];

function getMatchingLanguages(value) {
  const escapedValue = escapeRegexCharacters(value.trim()); // See: https://github.com/moroshko/react-autosuggest/blob/master/demo/src/components/utils/utils.js#L2-L4
  const regex = new RegExp('^' + escapedValue, 'i');

  return languages.filter(language => regex.test(language.name));
}

function getSuggestionValue(suggestion) { // when suggestion selected, this function tells
  return suggestion.name;                 // what should be the value of the input
}

function renderSuggestion(suggestion) {
  return (
    <span>{suggestion.name}</span>
  );
}

class Example extends React.Component {
  constructor() {
    super();

    this.state = {
      value: '',
      suggestions: getMatchingLanguages('')
    };

    this.onChange = this.onChange.bind(this);
    this.onSuggestionSelected = this.onSuggestionSelected.bind(this);
  }

  onChange(event, { newValue, method }) {
    if (method === 'type') {
      this.setState({
        value: newValue,
        suggestions: getMatchingLanguages(newValue)
      });
    } else {
      this.setState({
        value: newValue
      });
    }
  }

  // When suggestion is selected, we need to update `suggestions` so that,
  // if user presses Up or Down to reveal suggestions,
  // they would see the updated list.
  onSuggestionSelected(event, { suggestionValue }) {
    this.setState({
      suggestions: getMatchingLanguages(suggestionValue)
    });
  }

  render() {
    const { value, suggestions } = this.state;
    const inputProps = {
      placeholder: 'Type a programming language',
      value,
      onChange: this.onChange
    };

    return (
      <Autosuggest suggestions={suggestions}
                   getSuggestionValue={getSuggestionValue}
                   renderSuggestion={renderSuggestion}
                   inputProps={inputProps}
                   onSuggestionSelected={this.onSuggestionSelected} />
    );
  }
}

Props

suggestions (required)

Arbitrary array of suggestions to display.

For plain list of suggestions, you could have:

const suggestions = [{
  text: 'Apple'
}, {
  text: 'Banana'
}, {
  text: 'Cherry'
}, {
  text: 'Grapefruit'
}, {
  text: 'Lemon'
}];

You could also have multiple sections, in which case suggestions would be an array of sections. 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'
  }]
}];

Note:

  • It's totally up to you what shape suggestions take!
  • The initial value of suggestions should match the initial value of inputProps.value. This is to make sure that, if input has a non-empty initial value, and it's focused, the right suggestions are displayed.

getSuggestionValue (required)

When user navigates the suggestions using the Up and Down keys, the input should display 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:

  • suggestion - The suggestion in question

It should return a string. For example:

function getSuggestionValue(suggestion) {
  return suggestion.text;
}

renderSuggestion (required)

Use your imagination to define how suggestions are rendered.

renderSuggestion has the following signature:

function renderSuggestion(suggestion, { value, valueBeforeUpDown })

where:

  • suggestion - The suggestion to render
  • value - The current value of the input
  • valueBeforeUpDown - The value of the input prior to Up/Down interactions. If user didn't interact with Up/Down yet, it will be null. It is useful if you want to highlight input's value in the suggestion (a.k.a the match), for example.

It should return a ReactElement. For example:

function renderSuggestion(suggestion) {
  return (
    <span>{suggestion.text}</span>
  );
}

inputProps (required)

Autosuggest is a controlled component. Therefore, you should pass at least a value and an onChange callback to the input field. You can pass additional props as well. For example:

const inputProps = {
  value: inputValue,   // `inputValue` usually comes from application state
  onChange: onChange   // `onChange` will be called when input value changes
  type: 'search',
  placeholder: 'Enter city or postcode'
};

onChange has the following signature:

function onChange(event, { newValue, method })

where:

  • newValue - the new value of the input field
  • method - string describing how the change occurred. The possible values are:
    • 'down' - user pressed Down
    • 'up' - user pressed Up
    • 'escape' - user pressed Esc
    • 'click' - user clicked 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 field, etc.)

shouldRenderSuggestions (optional)

By default, suggestions are rendered when input field isn't blank. Feel free to override this behaviour.

This function gets:

  • value - The current value of the input

It should return a boolean.

For example, to display suggestions only when input is at least 3 characters long, do:

function shouldRenderSuggestions(value) {
  return value.trim().length > 2;
}

multiSection (optional)

By default, Autosuggest renders a plain list of suggestions.

If you'd like to have multiple sections (with optional titles), set multiSection={true}.

renderSectionTitle (optional)

When rendering multiple sections, you need to tell Autosuggest how to render a section title.

This function gets:

  • section - The section to render (this would be an item in the suggestions array

It should return a ReactElement. For example:

function renderSectionTitle(section) {
  return (
    <strong>{section.title}</strong>
  );
}

getSectionSuggestions (optional)

When rendering multiple sections, you need to tell Autosuggest where to find the suggestions for a given section.

This function gets:

  • section - The section to render (this would be an item in the suggestions array

It should return an array of suggestions to render in the given section. For example:

function getSectionSuggestions(section) {
  return section.suggestions;
}

onSuggestionSelected (optional)

This function is called when suggestion is selected. It has the following signature:

function onSuggestionSelected(event, { suggestion, suggestionValue, method })

where:

  • suggestion - the selected suggestion
  • suggestionValue - the value of the selected suggestion (equivalent to getSuggestionValue(suggestion))
  • method - string describing how user selected the suggestion. The possible values are:
    • 'click' - user clicked on the suggestion
    • 'enter' - user selected the suggestion using Enter

theme (optional)

Autosuggest comes with no styles, and it supports react-themeable.

This means you can use CSS Modules, Radium, React Style, JSS, inline styles, or even global CSS to style your Autosugest component.

For example, to style the Autosuggest using CSS Modules, do:

/* theme.css */

.container { ... }
.input { ... }
.suggestions-container { ... }
.suggestion { ... }
.suggestion--focused { ... }
...
import theme from 'theme.css';
<Autosuggest theme={theme} ... />

When not specified, theme defaults to:

{
  'container':                     'react-autosuggest__container',
  'container--open':               'react-autosuggest__container--open',
  'input':                         'react-autosuggest__input',
  'suggestions-container':         'react-autosuggest__suggestions-container',
  'suggestion':                    'react-autosuggest__suggestion',
  'suggestion--focused':           'react-autosuggest__suggestion--focused',
  'section-container':             'react-autosuggest__section-container',
  'section-title':                 'react-autosuggest__section-title',
  'section-suggestions-container': 'react-autosuggest__section-suggestions-container'
}

An example of styling an Autosuggest using CSS Modules can be found here.

The following diagrams illustrate how theme is structured.

Plain list
+--| container |-----------------------+
|                                      |
|  +--| input |---------------------+  |
|  |                                |  |
|  +--------------------------------+  |
|                                      |
|  +--| suggestions-container |-----+  |
|  |                                |  |
|  |  +--| suggestion |----------+  |  |
|  |  |                          |  |  |
|  |  +--------------------------+  |  |
|  |                                |  |
|  +--------------------------------+  |
|                                      |
+--------------------------------------+
Multiple sections
+--| container |----------------------------------------+
|                                                       |
|  +--| input |--------------------------------------+  |
|  |                                                 |  |
|  +-------------------------------------------------+  |
|                                                       |
|  +--| suggestions-container |----------------------+  |
|  |                                                 |  |
|  |  +--| section-container |--------------------+  |  |
|  |  |                                           |  |  |
|  |  |  +--| section-title |------------------+  |  |  |
|  |  |  |                                     |  |  |  |
|  |  |  +-------------------------------------+  |  |  |
|  |  |                                           |  |  |
|  |  |  +--| section-suggestions-container |--+  |  |  |
|  |  |  |                                     |  |  |  |
|  |  |  |  +--| suggestion |---------------+  |  |  |  |
|  |  |  |  |                               |  |  |  |  |
|  |  |  |  +-------------------------------+  |  |  |  |
|  |  |  |                                     |  |  |  |
|  |  |  +-------------------------------------+  |  |  |
|  |  |                                           |  |  |
|  |  +-------------------------------------------+  |  |
|  |                                                 |  |
|  |  +--| section-container |--------------------+  |  |
|  |  |                                           |  |  |
|  |  |  ...                                      |  |  |
|  |  |                                           |  |  |
|  |  +-------------------------------------------+  |  |
|  +-------------------------------------------------+  |
|                                                       |
+-------------------------------------------------------+

Development

npm install
npm start

Now, open http://localhost:3000/demo/dist/index.html and start hacking!

Running Tests

npm test

License

MIT


TODO:

  • Examples:
    • No results
    • Navigate on selection
  • Write upgrade guide
  • Release 3.0
    • Update deps
    • Publish to npm
    • Publish to gh-pages
  • Add support for scrollbar