UNPKG

cruk-searchkit

Version:

Set of overriden and new searchkit components designed specifically for Cancer Research UK

259 lines (227 loc) 7.24 kB
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> } }