* 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.
80 lines
2.1 KiB
JavaScript
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,
|
|
)}`,
|
|
);
|
|
}
|