Skip to content

Commit

Permalink
Add alwaysRenderSuggestions prop (#155)
Browse files Browse the repository at this point in the history
closes #133
  • Loading branch information
thibaudcolas authored and moroshko committed Aug 5, 2016
1 parent 3309cd3 commit 6ff95ae
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 13 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class Example extends React.Component {
* [`renderSuggestion`](#renderSuggestionProp)
* [`inputProps`](#inputPropsProp)
* [`shouldRenderSuggestions`](#shouldRenderSuggestionsProp)
* [`alwaysRenderSuggestions`](#alwaysRenderSuggestionsProp)
* [`multiSection`](#multiSectionProp)
* [`renderSectionTitle`](#renderSectionTitleProp)
* [`getSectionSuggestions`](#getSectionSuggestionsProp)
Expand Down Expand Up @@ -331,6 +332,15 @@ function shouldRenderSuggestions(value) {
}
```

When `shouldRenderSuggestions` returns true, suggestions will be rendered only when the input field is focused. If you would like to render suggestions regardless of whether the input field is focused or not, set `alwaysRenderSuggestions={true}` (`shouldRenderSuggestions` is ignored in this case).

<a name="alwaysRenderSuggestionsProp"></a>
#### alwaysRenderSuggestions (optional)

By default, suggestions are rendered only if the input field is focused.

If you'd like to _always_ display suggestions, set `alwaysRenderSuggestions={true}`.

<a name="multiSectionProp"></a>
#### multiSection (optional)

Expand Down
2 changes: 2 additions & 0 deletions demo/src/components/App/components/Examples/Examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import Basic from 'Basic/Basic';
import MultipleSections from 'MultipleSections/MultipleSections';
import CustomRender from 'CustomRender/CustomRender';
import AlwaysOpen from 'AlwaysOpen/AlwaysOpen';

export default function Examples() {
return (
Expand All @@ -14,6 +15,7 @@ export default function Examples() {
<Basic />
<MultipleSections />
<CustomRender />
<AlwaysOpen />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import styles from './AlwaysOpen.less';

import React, { Component } from 'react';
import isMobile from 'ismobilejs';
import Autosuggest from 'AutosuggestContainer';
import languages from './languages';
import { escapeRegexCharacters } from 'utils/utils';

const focusInputOnSuggestionClick = !isMobile.any;

function getSuggestions(value) {
const escapedValue = escapeRegexCharacters(value.trim());

if (escapedValue === '') {
return languages;
}

const regex = new RegExp('^' + escapedValue, 'i');

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

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

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

export default class AlwaysOpen extends Component {
constructor() {
super();

this.state = {
isOpen: false,
value: '',
suggestions: getSuggestions('')
};

this.onChange = this.onChange.bind(this);
this.onSuggestionsUpdateRequested = this.onSuggestionsUpdateRequested.bind(this);
this.onSuggestionSelected = this.onSuggestionSelected.bind(this);
this.onOpenModal = this.onToggleModal.bind(this, true);
this.onCloseModal = this.onToggleModal.bind(this, false);
this.onClickOverlay = this.onClickOverlay.bind(this);
}

onToggleModal(isOpen = !this.state.isOpen) {
this.setState({
isOpen: isOpen
});

if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}
}

onClickOverlay(e) {
const isOverlay = e.target.getAttribute('data-overlay');

if (isOverlay) {
this.onCloseModal();
e.preventDefault();
}
}

onChange(event, { newValue }) {
this.setState({
value: newValue
});
}

onSuggestionsUpdateRequested({ value }) {
this.setState({
suggestions: getSuggestions(value)
});
}

onSuggestionSelected(e, { suggestionValue }) {
this.setState({
value: suggestionValue,
suggestions: getSuggestions(suggestionValue)
});

this.onCloseModal();
}

render() {
const { isOpen, value, suggestions } = this.state;
const inputProps = {
placeholder: 'Type \'c\'',
value,
onChange: this.onChange
};

return (
<div id="basic-example" className={styles.container}>
<div className={styles.textContainer}>
<div className={styles.title}>
Always open
</div>
<div className={styles.description}>
Propose default suggestions even if the user hasn’t typed yet
</div>
</div>
<div className={styles.autosuggest}>
<a href="javascript:void(0)" onClick={this.onOpenModal} className={styles.button}>View suggestions</a>
{isOpen ? (
<div className={styles.overlay} data-overlay={true} onClick={this.onClickOverlay}>
<div className={styles.modal}>
<Autosuggest suggestions={suggestions}
onSuggestionsUpdateRequested={this.onSuggestionsUpdateRequested}
onSuggestionSelected={this.onSuggestionSelected}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
alwaysRenderSuggestions={true}
focusInputOnSuggestionClick={focusInputOnSuggestionClick}
id="alwaysopen-example"
theme={{
container: 'react-autosuggest__container',
containerOpen: 'react-autosuggest__container--open',
input: 'react-autosuggest__input',
suggestionsContainer: `react-autosuggest__suggestions-container ${styles.suggestionsContainer}`,
suggestion: 'react-autosuggest__suggestion',
suggestionFocused: 'react-autosuggest__suggestion--focused',
sectionContainer: 'react-autosuggest__section-container',
sectionTitle: 'react-autosuggest__section-title',
sectionSuggestionsContainer: 'react-autosuggest__section-suggestions-container'
}} />
</div>
</div>
) : null}
</div>
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@import '~variables';

.button {
color: #209FD3;
text-decoration: none;
}

.suggestionsContainer {
position: static;
max-height: 300px;
overflow-y: scroll;
}

.overlay {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 100;
background-color: rgba(0, 0, 0, 0.7);
}

.modal {
position: absolute;
top: 10vh;
left: 50%;
transform: translateX(-50%);
z-index: 101;
}

.container {
display: flex;
justify-content: space-between;
width: 34 * @columns;
margin: (11 * @rows) (0.5 * @column) 0;

@media @examples-vertical {
flex-direction: column;
width: 14 * @columns;
margin-top: 9 * @rows;
}

@media @small {
margin-top: 7 * @rows;
}
}

.textContainer {
display: flex;
flex-direction: column;
width: 15 * @columns;
}

.title {
font-size: 30px;
font-weight: 400;
line-height: 5 * @rows;

@media @small {
font-size: 25px;
}
}

.description {
margin-top: @row;
font-size: 20px;
}

.codepenLink {
margin-top: @row;
font-size: 20px;
color: #209FD3;
}

.autosuggest {
margin-top: 6 * @rows;

@media @examples-vertical {
margin-top: 3 * @rows;
margin-left: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export default [
{
name: 'C',
year: 1972
},
{
name: 'C#',
year: 2000
},
{
name: 'C++',
year: 1983
},
{
name: 'Clojure',
year: 2007
},
{
name: 'Elm',
year: 2012
},
{
name: 'Go',
year: 2009
},
{
name: 'Haskell',
year: 1990
},
{
name: 'Java',
year: 1995
},
{
name: 'Javascript',
year: 1995
},
{
name: 'Perl',
year: 1987
},
{
name: 'PHP',
year: 1995
},
{
name: 'Python',
year: 1991
},
{
name: 'Ruby',
year: 1995
},
{
name: 'Scala',
year: 2003
}
];
15 changes: 9 additions & 6 deletions src/Autosuggest.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Autosuggest extends Component {
renderSuggestion: PropTypes.func.isRequired,
inputProps: PropTypes.object.isRequired,
shouldRenderSuggestions: PropTypes.func.isRequired,
alwaysRenderSuggestions: PropTypes.bool.isRequired,
onSuggestionSelected: PropTypes.func.isRequired,
multiSection: PropTypes.bool.isRequired,
renderSectionTitle: PropTypes.func.isRequired,
Expand Down Expand Up @@ -60,14 +61,16 @@ class Autosuggest extends Component {

componentWillReceiveProps(nextProps) {
if (nextProps.suggestions !== this.props.suggestions) {
const { suggestions, inputProps, shouldRenderSuggestions,
isCollapsed, revealSuggestions, lastAction } = nextProps;
const {
suggestions, inputProps, shouldRenderSuggestions, isCollapsed,
revealSuggestions, lastAction, alwaysRenderSuggestions
} = nextProps;
const { value } = inputProps;

if (suggestions.length > 0 && shouldRenderSuggestions(value)) {
if (alwaysRenderSuggestions || suggestions.length > 0 && shouldRenderSuggestions(value)) {
this.maybeFocusFirstSuggestion();

if (isCollapsed && lastAction !== 'click' && lastAction !== 'enter') {
if (alwaysRenderSuggestions || isCollapsed && lastAction !== 'click' && lastAction !== 'enter') {
revealSuggestions();
}
}
Expand Down Expand Up @@ -225,10 +228,10 @@ class Autosuggest extends Component {
theme, isFocused, isCollapsed, focusedSectionIndex, focusedSuggestionIndex,
valueBeforeUpDown, inputFocused, inputBlurred, inputChanged,
updateFocusedSuggestion, revealSuggestions, closeSuggestions,
getSuggestionValue
getSuggestionValue, alwaysRenderSuggestions
} = this.props;
const { value, onBlur, onFocus, onKeyDown } = inputProps;
const isOpen = isFocused && !isCollapsed && this.willRenderSuggestions();
const isOpen = alwaysRenderSuggestions || isFocused && !isCollapsed && this.willRenderSuggestions();
const items = (isOpen ? suggestions : []);
const autowhateverInputProps = {
...inputProps,
Expand Down

0 comments on commit 6ff95ae

Please sign in to comment.