charity-base-search
Version:
React component for CharityBase search input
203 lines (185 loc) • 6.15 kB
JavaScript
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import debounce from 'lodash.debounce';
import FilterSuggest, { HelperText, Input, TextField } from 'filter-suggest';
import svgSearch from './search.svg';
var DEBOUNCE_TIME = 100;
var applyDebounced = debounce(function (f, x) {
return f(x);
}, DEBOUNCE_TIME);
var QUERY = '\n query CBS_SEARCH_FILTERS(\n $search: String!\n $filterType: [String]\n ) {\n CHC {\n getFilters(\n search: $search\n filterType: $filterType\n ) {\n id\n value\n label\n filterType\n score\n }\n }\n }\n';
var getData = function getData(apiUrl, apiKey, variables) {
return fetch(apiUrl + '?query=' + QUERY + '&variables=' + JSON.stringify(variables), {
headers: {
Authorization: 'Apikey ' + apiKey
}
}).then(function (x) {
return x.json();
});
};
var getSearchItem = function getSearchItem(inputValue) {
return {
id: 'search-' + inputValue,
value: inputValue,
label: inputValue,
filterType: 'search'
};
};
var errorMessage = function errorMessage(_ref) {
var error = _ref.error,
empty = _ref.empty,
loading = _ref.loading,
searchTerm = _ref.searchTerm;
if (error) {
return 'Oops something went wrong, please try again';
}
if (empty && searchTerm.length > 1 && !loading) {
return 'Sorry we can\'t find anything matching \'' + searchTerm + '\'';
}
return null;
};
var CharityBaseSearch = function CharityBaseSearch(_ref2) {
var apiKey = _ref2.apiKey,
apiUrl = _ref2.apiUrl,
className = _ref2.className,
filterTypes = _ref2.filterTypes,
hideAcknowledgement = _ref2.hideAcknowledgement,
label = _ref2.label,
mapItem = _ref2.mapItem,
menuClassName = _ref2.menuClassName,
onBlur = _ref2.onBlur,
onFocus = _ref2.onFocus,
_onSelect = _ref2.onSelect,
outlined = _ref2.outlined,
style = _ref2.style,
textFieldClassName = _ref2.textFieldClassName;
var _useState = useState(''),
inputValue = _useState[0],
setInputValue = _useState[1];
var _useState2 = useState(''),
debouncedInputValue = _useState2[0],
setDebouncedInputValue = _useState2[1];
var _useState3 = useState(null),
data = _useState3[0],
setData = _useState3[1];
var _useState4 = useState(null),
error = _useState4[0],
setError = _useState4[1];
var _useState5 = useState(false),
loading = _useState5[0],
setLoading = _useState5[1];
var onInputValueChange = function onInputValueChange(x) {
var value = x || '';
setInputValue(value);
applyDebounced(setDebouncedInputValue, value.trim());
};
useEffect(function () {
var ignore = false;
if (debouncedInputValue) {
var variables = { search: debouncedInputValue, filterType: filterTypes };
setLoading(true);
getData(apiUrl, apiKey, variables).then(function (_ref3) {
var data = _ref3.data,
errors = _ref3.errors;
if (ignore) return;
if (errors) {
// setData(null)
return setError(errors[0].message); // graphql error
}
setError(null);
setData(data);
}).catch(function (error) {
if (ignore) return;
// setData(null)
setError(error && error.message || 'Unknown error'); // probably network error
}).then(function () {
if (ignore) return;
setLoading(false);
});
} else {
setError(null);
setData(null);
}
return function () {
ignore = true;
};
}, [apiUrl, apiKey, debouncedInputValue, JSON.stringify(filterTypes)]);
var syncItems = [getSearchItem(inputValue)].reduce(function (agg, x) {
return filterTypes && filterTypes.indexOf(x.filterType) === -1 ? agg : [].concat(agg, [x]);
}, []);
var filterItems = data && data.CHC ? [].concat(syncItems, data.CHC.getFilters) : [];
return React.createElement(
'div',
{ className: className, style: style },
hideAcknowledgement ? null : React.createElement(
'a',
{
className: 'cbs-acknowledgement',
href: 'https://charitybase.uk',
target: '_blank',
rel: 'noopener noreferrer'
},
'Powered by CharityBase'
),
React.createElement(FilterSuggest, {
errorMessage: errorMessage({
error: error,
loading: loading,
empty: filterItems.length === 0,
searchTerm: debouncedInputValue
}),
inputValue: inputValue,
items: filterItems.map(mapItem),
label: label,
leadingIcon: React.createElement('img', { width: 24, height: 24, src: svgSearch }),
loading: loading,
menuClassName: menuClassName,
onBlur: onBlur,
onFocus: onFocus,
onInputValueChange: onInputValueChange,
onSelect: function onSelect(item) {
onInputValueChange('');
_onSelect(item);
},
outlined: outlined,
textFieldClassName: textFieldClassName
})
);
};
CharityBaseSearch.propTypes = process.env.NODE_ENV !== "production" ? {
apiKey: PropTypes.string.isRequired,
apiUrl: PropTypes.string,
className: PropTypes.string,
filterTypes: PropTypes.array,
hideAcknowledgement: PropTypes.bool,
label: PropTypes.string,
mapItem: PropTypes.func,
menuClassName: PropTypes.string,
onBlur: PropTypes.func,
onFocus: PropTypes.func,
onSelect: PropTypes.func.isRequired,
outlined: PropTypes.bool,
style: PropTypes.object,
textFieldClassName: PropTypes.string
} : {};
CharityBaseSearch.defaultProps = {
apiUrl: 'https://charitybase.uk/api/graphql',
label: 'Search charities',
mapItem: function mapItem(_ref4) {
var id = _ref4.id,
filterType = _ref4.filterType,
value = _ref4.value,
label = _ref4.label;
return { // __typename ?
id: id,
filterType: filterType,
value: value,
label: label,
icon: null,
primary: label,
secondary: 'Filter by ' + filterType
};
}
};
export default CharityBaseSearch;
export { HelperText, Input, TextField };