docbrown/app/javascript/src/utils/search.js
Nick Taylor 18980034d3 Nick/feature/search to preact (#532)
* Works, but lots of cleanup/questions.

* Fixed comment.

* Only want the search script to load once with InstantClick active.

* Removed unnecessary DOMContentLoaded from copy paste.

* 🔧 Enabled (P)React Devtools.

* Updated comment about reactToEvent.

* Removed unnecessary server-side generated JS for search.

* <Search /> component now has component state.

* Search functions are in there own module now.

* 👷Refactor

* Render propped it up.

* Fixed issue with encoding of search term.

* Removed data-no-instant attr from script.

* 👷Refactor

* Now flash of search loading is avoided..

* Moved search under components folder.

* Tests

* fixedEncodeURIComponent does not need to be exported.

* Tests

* Folders to ignore from the VS Code Local History extension.

* Tests

* Snapshot test for <Search /> component.

* Tests

* Now the <SearchContainer /> handles the '/' key shortcut.

* Made the search box ID a prop defaulting to 'nav-search'

* Excluded barrel files from code coverage.

* Componentized search CSS.

* Storybook stories for <Search /> component.

* Removed extension folders to ignore.

* Fixed logic for "/" key triggering search.

* updated jest snapshot for <Search /> component.

* updated package.json lock file.

* Disabled the import/prefer-default-export rule.

* Now search term clears if not on a non-search results page.

* removed comment that is no longer valid.

* No longer using render prop for Search. Doesn't make sense as it's not stuff that is reused.

* Added preact-render-spy do dev deps.
2018-07-19 17:02:48 -04:00

80 lines
2.1 KiB
JavaScript

// TODO: We should really be using the xss package by installing it in package.json
// but for now filterXSS is global because of legacy JS
function getParameterByName(name, url = window.location.href) {
const sanitizedName = name.replace(/[[\]]/g, '\\$&');
const regex = new RegExp(`[?&]${sanitizedName}(=([^&#]*)|&|#|$)`);
const results = regex.exec(url);
if (!results) {
return null;
}
if (!results[2]) {
return '';
}
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
function getFilterParameters(url) {
const filters = getParameterByName('filters', url);
if (filters) {
return `&filters=${filters}`;
}
return '';
}
export const hasInstantClick = () => typeof instantClick !== 'undefined';
function fixedEncodeURIComponent(str) {
// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
return encodeURIComponent(str).replace(
/[!'()*]/g,
c => `%${c.charCodeAt(0).toString(16)}`,
);
}
export function displaySearchResults({
searchTerm,
location = window.location,
}) {
const baseUrl = location.origin;
const sanitizedQuery = fixedEncodeURIComponent(searchTerm);
const filterParameters = getFilterParameters(location.href);
InstantClick.display(
`${baseUrl}/search?q=${sanitizedQuery}${filterParameters}`,
);
}
export function getInitialSearchTerm(querystring) {
const matches = /(?:&|\?)?q=([^&=]+)/.exec(querystring);
const rawSearchTerm =
matches !== null && matches.length === 2
? decodeURIComponent(matches[1])
: '';
const query = filterXSS(rawSearchTerm);
const divForDecode = document.createElement('div');
divForDecode.innerHTML = query;
return divForDecode.firstChild !== null
? divForDecode.firstChild.nodeValue
: query;
}
export function preloadSearchResults({
searchTerm,
location = window.location,
}) {
const encodedQuery = fixedEncodeURIComponent(
searchTerm.replace(/^[ ]+|[ ]+$/g, ''),
);
InstantClick.preload(
`${location.origin}/search?q=${encodedQuery}${getFilterParameters(
location.href,
)}`,
);
}