2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017-2018 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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 import React, {Component} from 'react';
22 import { PropTypes } from 'prop-types';
23 import {Button} from 'react-bootstrap';
24 import AutoSuggest from 'react-autosuggest';
25 import Highlighter from 'react-highlight-words';
26 import debounce from 'lodash.debounce';
27 import {ButtonGroup} from 'react-bootstrap';
28 import Modal from 'react-bootstrap/lib/Modal';
29 import {Link} from 'react-router-dom';
30 import {genericRequest} from 'app/networking/NetworkCalls.js';
32 import {changeUrlAddress} from 'utils/Routes.js';
40 SEARCH_PLACEHOLDER_TEXT
41 } from './AutoCompleteSearchBarConstants.js';
43 export default class AutoCompleteSearchBar extends Component {
45 value: PropTypes.string,
46 suggestions: PropTypes.array,
47 cachedSuggestions: PropTypes.array,
48 suggestionName: PropTypes.string
60 componentWillMount() {
61 this.debouncedLoadSuggestions =
62 debounce(this.props.onSuggestionsFetchRequested, SEARCH_DEBOUNCE_TIME);
65 onSuggestionsFetchRequested = ({value}) => {
66 this.debouncedLoadSuggestions({value});
69 isValidSearch(value) {
70 return (value && value !== NO_MATCHES_FOUND);
73 isValidSuggestionObject(suggestionObj) {
74 return (suggestionObj &&
75 Object.keys(suggestionObj).length > 0 &&
76 this.isValidSearch(suggestionObj.text));
79 getSelectedSuggestionObj(value, cachedSuggestions) {
80 let matchesSuggestion = {};
82 if (this.isValidSearch(value)) {
83 for (let suggestionIndex in cachedSuggestions) {
84 if (cachedSuggestions[suggestionIndex].text === value) {
85 matchesSuggestion = cachedSuggestions[suggestionIndex];
91 return matchesSuggestion;
95 cachedSuggestion, invalidSearchCallback, dispatchAnalytics, value) {
96 if (this.isValidSuggestionObject(cachedSuggestion)) {
97 changeUrlAddress(cachedSuggestion, this.props.history);
98 //Call analytics if defined
99 if (dispatchAnalytics) {
103 invalidSearchCallback(value);
110 suggestionName, cachedSuggestions,
111 onInputChange, onInvalidSearch,
112 onClearSuggestionsTextFieldRequested,
113 onSuggestionsClearRequested,
117 placeholder: SEARCH_PLACEHOLDER_TEXT,
119 onChange: onInputChange
122 let closeHelpModal = () => {
123 this.setState({helpModalShow: false});
125 let showHelpModal = () => {
126 genericRequest('/schema/searchable', true, 'GET').then(res=>{
127 let searchDOM = res.sort(function(a, b) {
128 var compareA = (a['node-type']).toLowerCase();
129 var compareB = (b['node-type']).toLowerCase();
130 if(compareA < compareB){
133 if(compareA > compareB){
139 <div><p><strong>{prop['node-type']}:</strong></p><p>{prop['searchable-attributes']}</p></div>
142 this.setState({searchable: searchDOM, helpModalShow: true});
145 this.setState({searchable: 'An error occurred, please try again later.', helpModalShow: true});
148 this.setState({searchable: 'An error occurred, please try again later.', helpModalShow: true});
152 let clearButtonClass = (value.length > 0)
153 ? 'auto-complete-clear-button'
154 : 'auto-complete-clear-button hidden';
157 <div className='auto-complete-search'>
159 suggestions={suggestions}
160 getSuggestionValue={suggestion => suggestion[suggestionName]}
161 onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
162 onSuggestionsClearRequested={onSuggestionsClearRequested}
163 onSuggestionSelected={(event, {suggestion}) => {
164 this.newSearchSelected(suggestion, onInvalidSearch, dispatchAnalytics, value);
165 this.props.onClearSuggestionsTextFieldRequested();
167 renderSuggestion={this.renderSuggestion}
168 inputProps={inputProps}
169 focusFirstSuggestion={false}
170 renderSuggestionsContainer={this.renderSuggestionsContainer}/>
171 <ButtonGroup className='auto-complete-search-button-group'>
172 <Button type='submit' className={clearButtonClass}
173 onClick={onClearSuggestionsTextFieldRequested}>
174 <i className={ICON_CLASS_CLEAR} aria-hidden='true'/>
176 <Button type='submit' className='auto-complete-help-button' onClick={showHelpModal}>
177 <i className={ICON_CLASS_HELP} aria-hidden='true'/>
179 <Button type='submit' className='auto-complete-search-button' onClick={() => {
180 this.newSearchSelected(this.getSelectedSuggestionObj(value, cachedSuggestions),
181 onInvalidSearch, dispatchAnalytics, value);
182 this.props.onSuggestionsClearRequested();
184 <i className={ICON_CLASS_SEARCH} aria-hidden='true'/>
187 <div className='static-modal'>
188 <Modal show={this.state.helpModalShow} onHide={closeHelpModal}>
190 <Modal.Title>Searchable Fields</Modal.Title>
193 <div className='modal-searchable'>
194 {this.state.searchable}
198 <Button onClick={closeHelpModal}>Close</Button>
206 renderSuggestion(suggestion, {query}) {
207 let toHighLightArray = query.split(' ');
208 let suggestionTextArray = suggestion.text.split(' ');
211 if (suggestion.text !== NO_MATCHES_FOUND) {
212 // render the suggestion as a clickable link
214 <div className='suggestionFlexContainer'>
215 <span key={'sugSpan1'}
216 className='suggestionColumnTwo'>
217 <Link style={{textDecoration: 'none'}}
218 to={'/' + suggestion.route + '/' + suggestion.hashId}
220 {suggestionTextArray.map(
223 <span key={arrayIndex + 'sugSpan3'}>
224 <Highlighter key={arrayIndex + 'high'}
225 highlightClassName='highlight'
226 searchWords={toHighLightArray}
227 textToHighlight={suggestionTextArray[arrayIndex]}
229 { ++arrayIndex ? ' ' : ' '}
237 // render the suggestion as plain text
239 <div className='suggestionFlexContainer'>
240 <span key={'sugSpan1'}
241 className='suggestionColumnTwo'>
242 {suggestionTextArray.map(
245 <span key={arrayIndex + 'sugSpan3'}>
246 <Highlighter key={arrayIndex + 'high'}
247 highlightClassName='highlight'
248 searchWords={toHighLightArray}
249 textToHighlight={suggestionTextArray[arrayIndex]}
251 { ++arrayIndex ? ' ' : ' '}
261 renderSuggestionsContainer({children, ...rest}) {
262 if (children !== null && children.props.items.length < 5) {
263 rest.className = 'react-autosuggest__suggestions-containerCopy';
266 <div {...rest.containerProps} {...rest}>