summaryrefslogtreecommitdiffstats
path: root/web/react/components/suggestion
diff options
context:
space:
mode:
Diffstat (limited to 'web/react/components/suggestion')
-rw-r--r--web/react/components/suggestion/at_mention_provider.jsx118
-rw-r--r--web/react/components/suggestion/command_provider.jsx43
-rw-r--r--web/react/components/suggestion/emoticon_provider.jsx91
-rw-r--r--web/react/components/suggestion/search_channel_provider.jsx69
-rw-r--r--web/react/components/suggestion/search_suggestion_list.jsx97
-rw-r--r--web/react/components/suggestion/search_user_provider.jsx62
-rw-r--r--web/react/components/suggestion/suggestion_box.jsx163
-rw-r--r--web/react/components/suggestion/suggestion_list.jsx125
8 files changed, 0 insertions, 768 deletions
diff --git a/web/react/components/suggestion/at_mention_provider.jsx b/web/react/components/suggestion/at_mention_provider.jsx
deleted file mode 100644
index b53b351d9..000000000
--- a/web/react/components/suggestion/at_mention_provider.jsx
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-const MaxUserSuggestions = 40;
-
-class AtMentionSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let username;
- let description;
- let icon;
- if (item.username === 'all') {
- username = 'all';
- description = (
- <FormattedMessage
- id='suggestion.mention.all'
- defaultMessage='Notifies everyone in the team'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else if (item.username === 'channel') {
- username = 'channel';
- description = (
- <FormattedMessage
- id='suggestion.mention.channel'
- defaultMessage='Notifies everyone in the channel'
- />
- );
- icon = <i className='mention__image fa fa-users fa-2x'/>;
- } else {
- username = item.username;
- description = Utils.getFullName(item);
- icon = (
- <img
- className='mention__image'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
- />
- );
- }
-
- let className = 'mentions__name';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <div className='pull-left'>
- {icon}
- </div>
- <div className='pull-left mention--align'>
- <span>
- {'@' + username}
- </span>
- <span className='mention__fullname'>
- {description}
- </span>
- </div>
- </div>
- );
- }
-}
-
-AtMentionSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class AtMentionProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext);
- if (captured) {
- const usernamePrefix = captured[1];
-
- const users = UserStore.getActiveOnlyProfiles(true);
- let filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.username.startsWith(usernamePrefix) && user.delete_at <= 0) {
- filtered.push(user);
- }
-
- if (filtered.length >= MaxUserSuggestions) {
- break;
- }
- }
-
- // add dummy users to represent the @all and @channel special mentions
- if ('all'.startsWith(usernamePrefix)) {
- filtered.push({username: 'all'});
- }
-
- if ('channel'.startsWith(usernamePrefix)) {
- filtered.push({username: 'channel'});
- }
-
- filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
-
- const mentions = filtered.map((user) => '@' + user.username);
-
- SuggestionStore.setMatchedPretext(suggestionId, captured[0]);
- SuggestionStore.addSuggestions(suggestionId, mentions, filtered, AtMentionSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/command_provider.jsx b/web/react/components/suggestion/command_provider.jsx
deleted file mode 100644
index fffb2df07..000000000
--- a/web/react/components/suggestion/command_provider.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as AsyncClient from '../../utils/async_client.jsx';
-
-class CommandSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'command';
- if (isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <div className='command__title'>
- <string>{item.suggestion} {item.hint}</string>
- </div>
- <div className='command__desc'>
- {item.description}
- </div>
- </div>
- );
- }
-}
-
-CommandSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class CommandProvider {
- handlePretextChanged(suggestionId, pretext) {
- if (pretext.startsWith('/')) {
- AsyncClient.getSuggestedCommands(pretext, suggestionId, CommandSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/emoticon_provider.jsx b/web/react/components/suggestion/emoticon_provider.jsx
deleted file mode 100644
index fd470cf21..000000000
--- a/web/react/components/suggestion/emoticon_provider.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import * as Emoticons from '../../utils/emoticons.jsx';
-
-const MAX_EMOTICON_SUGGESTIONS = 40;
-
-class EmoticonSuggestion extends React.Component {
- render() {
- const text = this.props.term;
- const name = this.props.item;
-
- let className = 'emoticon-suggestion';
- if (this.props.isSelection) {
- className += ' suggestion--selected';
- }
-
- return (
- <div
- className={className}
- onClick={this.props.onClick}
- >
- <div className='pull-left'>
- <img
- alt={text}
- className='emoticon-suggestion__image'
- src={Emoticons.getImagePathForEmoticon(name)}
- title={text}
- />
- </div>
- <div className='pull-left'>
- {text}
- </div>
- </div>
- );
- }
-}
-
-EmoticonSuggestion.propTypes = {
- item: React.PropTypes.string.isRequired,
- term: React.PropTypes.string.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class EmoticonProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/(?:^|\s)(:([a-zA-Z0-9_+\-]*))$/g).exec(pretext);
- if (captured) {
- const text = captured[1];
- const partialName = captured[2];
-
- const names = [];
-
- for (const emoticon of Emoticons.emoticonMap.keys()) {
- if (emoticon.indexOf(partialName) !== -1) {
- names.push(emoticon);
-
- if (names.length >= MAX_EMOTICON_SUGGESTIONS) {
- break;
- }
- }
- }
-
- // sort the emoticons so that emoticons starting with the entered text come first
- names.sort((a, b) => {
- const aPrefix = a.startsWith(partialName);
- const bPrefix = b.startsWith(partialName);
-
- if (aPrefix === bPrefix) {
- return a.localeCompare(b);
- } else if (aPrefix) {
- return -1;
- }
-
- return 1;
- });
-
- const terms = names.map((name) => ':' + name + ':');
-
- if (terms.length > 0) {
- SuggestionStore.setMatchedPretext(suggestionId, text);
- SuggestionStore.addSuggestions(suggestionId, terms, names, EmoticonSuggestion);
-
- // force the selection to be cleared since the order of elements may have changed
- SuggestionStore.clearSelection(suggestionId);
- }
- }
- }
-}
diff --git a/web/react/components/suggestion/search_channel_provider.jsx b/web/react/components/suggestion/search_channel_provider.jsx
deleted file mode 100644
index 66a534907..000000000
--- a/web/react/components/suggestion/search_channel_provider.jsx
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChannelStore from '../../stores/channel_store.jsx';
-import Constants from '../../utils/constants.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-
-class SearchChannelSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- onClick={onClick}
- className={className}
- >
- <i className='fa fa fa-plus-square'></i>{item.name}
- </div>
- );
- }
-}
-
-SearchChannelSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class SearchChannelProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\b(?:in|channel):\s*(\S*)$/i).exec(pretext);
- if (captured) {
- const channelPrefix = captured[1];
-
- const channels = ChannelStore.getAll();
- const publicChannels = [];
- const privateChannels = [];
-
- for (const id of Object.keys(channels)) {
- const channel = channels[id];
-
- // don't show direct channels
- if (channel.type !== Constants.DM_CHANNEL && channel.name.startsWith(channelPrefix)) {
- if (channel.type === Constants.OPEN_CHANNEL) {
- publicChannels.push(channel);
- } else {
- privateChannels.push(channel);
- }
- }
- }
-
- publicChannels.sort((a, b) => a.name.localeCompare(b.name));
- const publicChannelNames = publicChannels.map((channel) => channel.name);
-
- privateChannels.sort((a, b) => a.name.localeCompare(b.name));
- const privateChannelNames = privateChannels.map((channel) => channel.name);
-
- SuggestionStore.setMatchedPretext(suggestionId, channelPrefix);
-
- SuggestionStore.addSuggestions(suggestionId, publicChannelNames, publicChannels, SearchChannelSuggestion);
- SuggestionStore.addSuggestions(suggestionId, privateChannelNames, privateChannels, SearchChannelSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/search_suggestion_list.jsx b/web/react/components/suggestion/search_suggestion_list.jsx
deleted file mode 100644
index 60a5562fa..000000000
--- a/web/react/components/suggestion/search_suggestion_list.jsx
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-import SuggestionList from './suggestion_list.jsx';
-
-import {FormattedMessage} from 'mm-intl';
-
-export default class SearchSuggestionList extends SuggestionList {
- componentDidUpdate(prevProps, prevState) {
- if (this.state.items.length > 0 && prevState.items.length === 0) {
- this.getContent().perfectScrollbar();
- }
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.popover)).find('.popover-content');
- }
-
- renderChannelDivider(type) {
- let text;
- if (type === Constants.OPEN_CHANNEL) {
- text = (
- <FormattedMessage
- id='suggestion.search.public'
- defaultMessage='Public Channels'
- />
- );
- } else {
- text = (
- <FormattedMessage
- id='suggestion.search.private'
- defaultMessage='Private Groups'
- />
- );
- }
-
- return (
- <div
- key={type + '-divider'}
- className='search-autocomplete__divider'
- >
- <span>{text}</span>
- </div>
- );
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- for (let i = 0; i < this.state.items.length; i++) {
- const item = this.state.items[i];
- const term = this.state.terms[i];
- const isSelection = term === this.state.selection;
-
- // ReactComponent names need to be upper case when used in JSX
- const Component = this.state.components[i];
-
- // temporary hack to add dividers between public and private channels in the search suggestion list
- if (i === 0 || item.type !== this.state.items[i - 1].type) {
- if (item.type === Constants.OPEN_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.OPEN_CHANNEL));
- } else if (item.type === Constants.PRIVATE_CHANNEL) {
- items.push(this.renderChannelDivider(Constants.PRIVATE_CHANNEL));
- }
- }
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- isSelection={isSelection}
- onClick={this.handleItemClick.bind(this, term)}
- />
- );
- }
-
- return (
- <ReactBootstrap.Popover
- ref='popover'
- id='search-autocomplete__popover'
- className='search-help-popover autocomplete visible'
- placement='bottom'
- >
- {items}
- </ReactBootstrap.Popover>
- );
- }
-}
-
-SearchSuggestionList.propTypes = {
- ...SuggestionList.propTypes
-};
diff --git a/web/react/components/suggestion/search_user_provider.jsx b/web/react/components/suggestion/search_user_provider.jsx
deleted file mode 100644
index 0d553bfc4..000000000
--- a/web/react/components/suggestion/search_user_provider.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
-
-class SearchUserSuggestion extends React.Component {
- render() {
- const {item, isSelection, onClick} = this.props;
-
- let className = 'search-autocomplete__item';
- if (isSelection) {
- className += ' selected';
- }
-
- return (
- <div
- className={className}
- onClick={onClick}
- >
- <img
- className='profile-img rounded'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
- />
- <i className='fa fa fa-plus-square'></i>{item.username}
- </div>
- );
- }
-}
-
-SearchUserSuggestion.propTypes = {
- item: React.PropTypes.object.isRequired,
- isSelection: React.PropTypes.bool,
- onClick: React.PropTypes.func
-};
-
-export default class SearchUserProvider {
- handlePretextChanged(suggestionId, pretext) {
- const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext);
- if (captured) {
- const usernamePrefix = captured[1];
-
- const users = UserStore.getProfiles();
- let filtered = [];
-
- for (const id of Object.keys(users)) {
- const user = users[id];
-
- if (user.username.startsWith(usernamePrefix)) {
- filtered.push(user);
- }
- }
-
- filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
-
- const usernames = filtered.map((user) => user.username);
-
- SuggestionStore.setMatchedPretext(suggestionId, usernamePrefix);
- SuggestionStore.addSuggestions(suggestionId, usernames, filtered, SearchUserSuggestion);
- }
- }
-}
diff --git a/web/react/components/suggestion/suggestion_box.jsx b/web/react/components/suggestion/suggestion_box.jsx
deleted file mode 100644
index 12b098cbd..000000000
--- a/web/react/components/suggestion/suggestion_box.jsx
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from '../../utils/constants.jsx';
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-import * as Utils from '../../utils/utils.jsx';
-
-const KeyCodes = Constants.KeyCodes;
-
-export default class SuggestionBox extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleDocumentClick = this.handleDocumentClick.bind(this);
-
- this.handleChange = this.handleChange.bind(this);
- this.handleCompleteWord = this.handleCompleteWord.bind(this);
- this.handleKeyDown = this.handleKeyDown.bind(this);
- this.handlePretextChanged = this.handlePretextChanged.bind(this);
-
- this.suggestionId = Utils.generateId();
- }
-
- componentDidMount() {
- SuggestionStore.registerSuggestionBox(this.suggestionId);
- $(document).on('click', this.handleDocumentClick);
-
- SuggestionStore.addCompleteWordListener(this.suggestionId, this.handleCompleteWord);
- SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
- }
-
- componentWillUnmount() {
- SuggestionStore.removeCompleteWordListener(this.suggestionId, this.handleCompleteWord);
- SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
-
- SuggestionStore.unregisterSuggestionBox(this.suggestionId);
- $(document).off('click', this.handleDocumentClick);
- }
-
- getTextbox() {
- // this is to support old code that looks at the input/textarea DOM nodes
- return ReactDOM.findDOMNode(this.refs.textbox);
- }
-
- handleDocumentClick(e) {
- const container = $(ReactDOM.findDOMNode(this));
- if (!(container.is(e.target) || container.has(e.target).length > 0)) {
- // we can't just use blur for this because it fires and hides the children before
- // their click handlers can be called
- GlobalActions.emitClearSuggestions(this.suggestionId);
- }
- }
-
- handleChange(e) {
- const textbox = ReactDOM.findDOMNode(this.refs.textbox);
- const caret = Utils.getCaretPosition(textbox);
- const pretext = textbox.value.substring(0, caret);
-
- GlobalActions.emitSuggestionPretextChanged(this.suggestionId, pretext);
-
- if (this.props.onUserInput) {
- this.props.onUserInput(textbox.value);
- }
-
- if (this.props.onChange) {
- this.props.onChange(e);
- }
- }
-
- handleCompleteWord(term) {
- const textbox = ReactDOM.findDOMNode(this.refs.textbox);
- const caret = Utils.getCaretPosition(textbox);
-
- const text = this.props.value;
- const prefix = text.substring(0, caret - SuggestionStore.getMatchedPretext(this.suggestionId).length);
- const suffix = text.substring(caret);
-
- if (this.props.onUserInput) {
- this.props.onUserInput(prefix + term + ' ' + suffix);
- }
-
- // set the caret position after the next rendering
- window.requestAnimationFrame(() => {
- Utils.setCaretPosition(textbox, prefix.length + term.length + 1);
- });
- }
-
- handleKeyDown(e) {
- if (SuggestionStore.hasSuggestions(this.suggestionId)) {
- if (e.which === KeyCodes.UP) {
- GlobalActions.emitSelectPreviousSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.DOWN) {
- GlobalActions.emitSelectNextSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB) {
- GlobalActions.emitCompleteWordSuggestion(this.suggestionId);
- e.preventDefault();
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- } else if (this.props.onKeyDown) {
- this.props.onKeyDown(e);
- }
- }
-
- handlePretextChanged(pretext) {
- for (const provider of this.props.providers) {
- provider.handlePretextChanged(this.suggestionId, pretext);
- }
- }
-
- render() {
- const newProps = Object.assign({}, this.props, {
- onChange: this.handleChange,
- onKeyDown: this.handleKeyDown
- });
-
- let textbox = null;
- if (this.props.type === 'input') {
- textbox = (
- <input
- ref='textbox'
- type='text'
- {...newProps}
- />
- );
- } else if (this.props.type === 'textarea') {
- textbox = (
- <textarea
- ref='textbox'
- {...newProps}
- />
- );
- }
-
- const SuggestionListComponent = this.props.listComponent;
-
- return (
- <div>
- {textbox}
- <SuggestionListComponent suggestionId={this.suggestionId}/>
- </div>
- );
- }
-}
-
-SuggestionBox.defaultProps = {
- type: 'input'
-};
-
-SuggestionBox.propTypes = {
- listComponent: React.PropTypes.func.isRequired,
- type: React.PropTypes.oneOf(['input', 'textarea']).isRequired,
- value: React.PropTypes.string.isRequired,
- onUserInput: React.PropTypes.func,
- providers: React.PropTypes.arrayOf(React.PropTypes.object),
-
- // explicitly name any input event handlers we override and need to manually call
- onChange: React.PropTypes.func,
- onKeyDown: React.PropTypes.func
-};
diff --git a/web/react/components/suggestion/suggestion_list.jsx b/web/react/components/suggestion/suggestion_list.jsx
deleted file mode 100644
index 0f5907179..000000000
--- a/web/react/components/suggestion/suggestion_list.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import * as GlobalActions from '../../action_creators/global_actions.jsx';
-import SuggestionStore from '../../stores/suggestion_store.jsx';
-
-export default class SuggestionList extends React.Component {
- constructor(props) {
- super(props);
-
- this.getContent = this.getContent.bind(this);
-
- this.handleItemClick = this.handleItemClick.bind(this);
- this.handleSuggestionsChanged = this.handleSuggestionsChanged.bind(this);
-
- this.scrollToItem = this.scrollToItem.bind(this);
-
- this.state = {
- items: [],
- terms: [],
- components: [],
- selection: ''
- };
- }
-
- componentDidMount() {
- SuggestionStore.addSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- componentWillUnmount() {
- SuggestionStore.removeSuggestionsChangedListener(this.props.suggestionId, this.handleSuggestionsChanged);
- }
-
- getContent() {
- return $(ReactDOM.findDOMNode(this.refs.content));
- }
-
- handleItemClick(term, e) {
- GlobalActions.emitCompleteWordSuggestion(this.props.suggestionId, term);
-
- e.preventDefault();
- }
-
- handleSuggestionsChanged() {
- const selection = SuggestionStore.getSelection(this.props.suggestionId);
-
- this.setState({
- items: SuggestionStore.getItems(this.props.suggestionId),
- terms: SuggestionStore.getTerms(this.props.suggestionId),
- components: SuggestionStore.getComponents(this.props.suggestionId),
- selection
- });
-
- if (selection) {
- window.requestAnimationFrame(() => this.scrollToItem(this.state.selection));
- }
- }
-
- scrollToItem(term) {
- const content = this.getContent();
- const visibleContentHeight = content[0].clientHeight;
- const actualContentHeight = content[0].scrollHeight;
-
- if (visibleContentHeight < actualContentHeight) {
- const contentTop = content.scrollTop();
- const contentTopPadding = parseInt(content.css('padding-top'), 10);
- const contentBottomPadding = parseInt(content.css('padding-top'), 10);
-
- const item = $(ReactDOM.findDOMNode(this.refs[term]));
- const itemTop = item[0].offsetTop - parseInt(item.css('margin-top'), 10);
- const itemBottomMargin = parseInt(item.css('margin-bottom'), 10) + parseInt(item.css('padding-bottom'), 10);
- const itemBottom = item[0].offsetTop + item.height() + itemBottomMargin;
-
- if (itemTop - contentTopPadding < contentTop) {
- // the item is off the top of the visible space
- content.scrollTop(itemTop - contentTopPadding);
- } else if (itemBottom + contentTopPadding + contentBottomPadding > contentTop + visibleContentHeight) {
- // the item has gone off the bottom of the visible space
- content.scrollTop(itemBottom - visibleContentHeight + contentTopPadding + contentBottomPadding);
- }
- }
- }
-
- render() {
- if (this.state.items.length === 0) {
- return null;
- }
-
- const items = [];
- for (let i = 0; i < this.state.items.length; i++) {
- const item = this.state.items[i];
- const term = this.state.terms[i];
- const isSelection = term === this.state.selection;
-
- // ReactComponent names need to be upper case when used in JSX
- const Component = this.state.components[i];
-
- items.push(
- <Component
- key={term}
- ref={term}
- item={item}
- term={term}
- isSelection={isSelection}
- onClick={this.handleItemClick.bind(this, term)}
- />
- );
- }
-
- return (
- <div className='suggestion-list suggestion-list--top'>
- <div
- ref='content'
- className='suggestion-list__content suggestion-list__content--top'
- >
- {items}
- </div>
- </div>
- );
- }
-}
-
-SuggestionList.propTypes = {
- suggestionId: React.PropTypes.string.isRequired
-};