Adding filter bar
[aai/sparky-fe.git] / src / generic-components / autoCompleteSearchBar / AutoCompleteSearchBar.jsx
1 /*
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23 import React, {Component} from 'react';
24 import {Button} from 'react-bootstrap';
25 import AutoSuggest from 'react-autosuggest';
26 import Highlighter from 'react-highlight-words';
27 import debounce from 'lodash.debounce';
28 import {ButtonGroup} from 'react-bootstrap';
29 import {Link} from 'react-router-dom';
30
31 import {changeUrlAddress} from 'utils/Routes.js';
32
33 import {
34   ICON_CLASS_SEARCH,
35   ICON_CLASS_CLEAR,
36   SEARCH_DEBOUNCE_TIME,
37   NO_MATCHES_FOUND,
38   SEARCH_PLACEHOLDER_TEXT
39 } from './AutoCompleteSearchBarConstants.js';
40
41 export default class AutoCompleteSearchBar extends Component {
42   static propTypes = {
43     value: React.PropTypes.string,
44     suggestions: React.PropTypes.array,
45     cachedSuggestions: React.PropTypes.array,
46     suggestionName: React.PropTypes.string
47   };
48
49   componentWillMount() {
50     this.debouncedLoadSuggestions =
51       debounce(this.props.onSuggestionsFetchRequested, SEARCH_DEBOUNCE_TIME);
52   }
53
54   onSuggestionsFetchRequested = ({value}) => {
55     this.debouncedLoadSuggestions({value});
56   };
57
58   isValidSearch(value) {
59     return (value && value !== NO_MATCHES_FOUND);
60   }
61
62   isValidSuggestionObject(suggestionObj) {
63     return (suggestionObj &&
64     Object.keys(suggestionObj).length > 0 &&
65     this.isValidSearch(suggestionObj.text));
66   }
67
68   getSelectedSuggestionObj(value, cachedSuggestions) {
69     let matchesSuggestion = {};
70
71     if (this.isValidSearch(value)) {
72       for (let suggestionIndex in cachedSuggestions) {
73         if (cachedSuggestions[suggestionIndex].text === value) {
74           matchesSuggestion = cachedSuggestions[suggestionIndex];
75           break;
76         }
77       }
78     }
79
80     return matchesSuggestion;
81   }
82
83   newSearchSelected(
84     cachedSuggestion, invalidSearchCallback, dispatchAnalytics, value) {
85     if (this.isValidSuggestionObject(cachedSuggestion)) {
86       changeUrlAddress(cachedSuggestion, this.props.history);
87       //Call analytics if defined
88       if (dispatchAnalytics) {
89         dispatchAnalytics();
90       }
91     } else {
92       invalidSearchCallback(value);
93     }
94   }
95
96   render() {
97     const {
98             value, suggestions,
99             suggestionName, cachedSuggestions,
100             onInputChange, onInvalidSearch,
101             onClearSuggestionsTextFieldRequested,
102             onSuggestionsClearRequested,
103             dispatchAnalytics
104           } = this.props;
105     const inputProps = {
106       placeholder: SEARCH_PLACEHOLDER_TEXT,
107       value,
108       onChange: onInputChange
109     };
110
111     let clearButtonClass = (value.length > 0)
112       ? 'auto-complete-clear-button'
113       : 'auto-complete-clear-button hidden';
114     return (
115       <div className='auto-complete-search'>
116         <AutoSuggest
117           suggestions={suggestions}
118           getSuggestionValue={suggestion => suggestion[suggestionName]}
119           onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
120           onSuggestionsClearRequested={onSuggestionsClearRequested}
121           onSuggestionSelected={(event, {suggestion}) => {
122             this.newSearchSelected(suggestion, onInvalidSearch, dispatchAnalytics, value);
123             this.props.onClearSuggestionsTextFieldRequested();
124           }}
125           renderSuggestion={this.renderSuggestion}
126           inputProps={inputProps}
127           focusFirstSuggestion={false}
128           renderSuggestionsContainer={this.renderSuggestionsContainer}/>
129         <ButtonGroup className='auto-complete-search-button-group'>
130           <Button type='submit' className={clearButtonClass}
131                   onClick={onClearSuggestionsTextFieldRequested}>
132             <i className={ICON_CLASS_CLEAR} aria-hidden='true'/>
133           </Button>
134
135           <Button type='submit' className='auto-complete-search-button' onClick={() => {
136             this.newSearchSelected(this.getSelectedSuggestionObj(value, cachedSuggestions),
137               onInvalidSearch, dispatchAnalytics, value);
138             this.props.onSuggestionsClearRequested();
139           }}>
140             <i className={ICON_CLASS_SEARCH} aria-hidden='true'/>
141           </Button>
142         </ButtonGroup>
143       </div>
144     );
145   }
146
147   renderSuggestion(suggestion, {query}) {
148     let toHighLightArray = query.split(' ');
149     let suggestionTextArray = suggestion.text.split(' ');
150     let arrayIndex = 0;
151
152     if (suggestion.text !== NO_MATCHES_FOUND) {
153       // render the suggestion as a clickable link
154       return (
155         <div className='suggestionFlexContainer'>
156         <span key={'sugSpan1'}
157               className='suggestionColumnTwo'>
158           <Link style={{textDecoration: 'none'}}
159                 to={'/' + suggestion.route + '/' + suggestion.hashId}
160                 replace={true}>
161             {suggestionTextArray.map(
162               function () {
163                 return (
164                   <span key={arrayIndex + 'sugSpan3'}>
165                     <Highlighter key={arrayIndex + 'high'}
166                                  highlightClassName='highlight'
167                                  searchWords={toHighLightArray}
168                                  textToHighlight={suggestionTextArray[arrayIndex]}/>
169                     { ++arrayIndex ? ' ' : ' '}
170                  </span>);
171
172               })} </Link>
173       </span>
174         </div>
175       );
176     } else {
177       // render the suggestion as plain text
178       return (
179         <div className='suggestionFlexContainer'>
180           <span key={'sugSpan1'}
181                 className='suggestionColumnTwo'>
182               {suggestionTextArray.map(
183                 function () {
184                   return (
185                     <span key={arrayIndex + 'sugSpan3'}>
186                       <Highlighter key={arrayIndex + 'high'}
187                                    highlightClassName='highlight'
188                                    searchWords={toHighLightArray}
189                                    textToHighlight={suggestionTextArray[arrayIndex]}/>
190                       { ++arrayIndex ? ' ' : ' '}
191                    </span>);
192
193                 })}
194           </span>
195         </div>
196       );
197     }
198   }
199
200   renderSuggestionsContainer({children, ...rest}) {
201     if (children !== null && children.props.items.length < 5) {
202       rest.className = 'react-autosuggest__suggestions-containerCopy';
203     }
204     return (
205       <div {...rest}>
206         {children}
207       </div>
208     );
209   }
210 }