cruk-searchkit
Version:
Set of overriden and new searchkit components designed specifically for Cancer Research UK
259 lines (227 loc) • 7.24 kB
JSX
import React from 'react';
import throttle from 'lodash/throttle'
import {
SearchBox
} from "searchkit";
import CRUKCustomElasticGetter from '../getter/CRUKCustomElasticGetter';
import CRUKSearchkitAutocompleteList from '../autocomplete/CRUKSearchkitAutocompleteList';
import { isUndefined } from 'lodash';
/**
* Override the render method on the SearchBox component to alter the markup.
*/
export default class CRUKSearchkitSearchBox extends SearchBox {
static buildResults = (results) => {
return results.title_suggest[0].options.map((v) => {
return v.text;
});
}
static propTypes = {
autocompleteEnable: React.PropTypes.bool,
test: React.PropTypes.bool,
autocompleteItems: React.PropTypes.array,
...SearchBox.propTypes
}
constructor(props) {
super(props);
this.state = {
clearText: false,
focused: false,
autocompleteActive: false,
input: undefined,
autocompleteItems: [],
selectedItem: 0
};
this.lastSearchMs = 0;
this.throttledSearch = throttle(()=> {
this.searchQuery(this.accessor.getQueryString())
}, props.searchThrottleTime);
this.autocompleteToggle = this.autocompleteToggle.bind(this);
this.inputState = this.inputState.bind(this);
this.handleSearchButton = this.handleSearchButton.bind(this);
}
handleKeyUp(e) {
if (!this.props.autocompleteEnable) return;
const { autocompleteItems, selectedItem } = this.state;
if (this.refs.queryField.value === '') {
this.setState({ autocompleteActive: false });
return;
}
const self = this;
if (e.keyCode === 40 || e.keyCode === 38) {
this.handleAutocompleteItems(e.keyCode);
return;
}
if (this.props.test) return;
const getter = new CRUKCustomElasticGetter(`${this.searchkit.host}/_suggest`);
getter.autocompleteRequest(this.refs.queryField.value)
.then(function (response) {
self.setState({
autocompleteItems: CRUKSearchkitSearchBox.buildResults(response.data),
autocompleteActive: true
});
})
.catch(function (error) {
console.log(error);
});
}
autocompleteToggle(flag) {
this.setState({ autocompleteActive: flag });
}
inputState(state) {
this.setState({ input: state });
}
handleAutocompleteItems(arrow) {
let { autocompleteItems, selectedItem } = this.state;
if (this.props.test) {
autocompleteItems = this.props.autocompleteItems;
}
if (autocompleteItems.length < 1) return;
const selectedItemIndex = (() => {
if (arrow === 40) {
if (selectedItem < autocompleteItems.length) return selectedItem + 1;
return autocompleteItems.length;
}
if (arrow === 38) {
if (selectedItem > 1) return selectedItem - 1;
return 1;
}
return 0;
})();
this.setState({
selectedItem: selectedItemIndex
});
}
setFocusState(focused) {
if (!focused){
const { input } = this.state;
if (this.props.blurAction === 'search'
&& !isUndefined(input)
&& input != this.getAccessorValue()){
this.searchQuery(input);
}
/**
* Behave differently if this component has autocomplete enabled, needs
* to be smarter innit.
*/
if (this.props.autocompleteEnable) {
if (!this.state.autocompleteActive) {
this.setState({ focused });
}
} else {
this.setState({
focused,
input: undefined // Flush (should use accessor's state now)
})
}
} else {
this.setState({ focused });
}
}
handleSearchButton(e) {
const w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
const { clearText } = this.state;
if (w <= 768 && clearText) {
e.preventDefault();
this.setState({
focused: true,
clearText: false
}, () => {
this.refs.queryField.value = '';
});
this.refs.queryField.focus();
}
}
onSubmit(event) {
const { autocompleteItems, selectedItem } = this.state;
event.preventDefault();
let query = this.getValue();
if (this.props.autocompleteEnable && selectedItem > 0) {
query = autocompleteItems[selectedItem - 1];
this.setState({
input: query,
selectedItem: 0,
autocompleteActive: false
}, () => {
this.searchQuery(query);
});
}
/**
* De-focus the input.
*/
if (document.activeElement != document.body) {
document.activeElement.blur();
}
}
componentDidMount() {
const self = this;
if (!this.props.test) {
this.searchkit.addResultsListener((results) => {
const w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
const clearText = w <= 768 ? true : false;
self.setState({
input: self.getAccessorValue(),
clearText
});
});
}
}
render() {
const { focused, selectedItem, clearText } = this.state;
let { autocompleteItems, autocompleteActive } = this.state;
let wrapper_class = 'cr-input-group cr-input-group--lg cr-search-input';
let placeholder = this.props.placeholder || this.translate('searchbox.placeholder');
if (focused) {
wrapper_class += ' cr-input-group--focused';
}
if (this.props.test && this.props.autocompleteItems) {
autocompleteItems = this.props.autocompleteItems;
autocompleteActive = true;
}
if (clearText) {
wrapper_class += ' cr-search-input--closer';
}
return <form action="." onSubmit={this.onSubmit.bind(this)}>
<div className={wrapper_class}>
<label htmlFor="search-input" id="search-label">{placeholder}</label>
<input
type="search"
data-qa="query"
id="search-input"
className="cr-input-group__input cr-search-input__input"
placeholder={placeholder}
value={this.getValue()}
onFocus={this.setFocusState.bind(this, true)}
onBlur={this.setFocusState.bind(this, false)}
onKeyUp={this.handleKeyUp.bind(this)}
ref="queryField"
autoFocus={this.props.autofocus}
onChange={this.onChange.bind(this)}
aria-describedby="search-label"
autoComplete="off"
autoCorrect="off"
tabIndex="1"
/>
<span className="cr-input-group__button cr-search-input__button">
<button
type="submit"
className="btn"
aria-label="Submit your search"
data-qa="submit"
onClick={this.handleSearchButton}
>
<span className="cr-input-group__icon glyphicon glyphicon-search" aria-hidden="true"></span>
</button>
</span>
{ this.props.autocompleteEnable &&
<CRUKSearchkitAutocompleteList
selectedItem={selectedItem}
inputState={this.inputState}
autocompleteActive={autocompleteActive}
autocompleteItems={autocompleteItems}
toggle={this.autocompleteToggle}
/>
}
</div>
</form>
}
}