content/frontend/search/search_helpers.js (87 lines of code) (raw):
/**
* Shared functions for Lunr and Google search.
*/
/**
* Check URL parameters for search parameters.
*
* We support "q" for the query string as it's a Google standard,
* and also "query" as it has been long-documented in the
* GitLab handbook as a Docs search parameter.
*
* See https://about.gitlab.com/handbook/tools-and-tips/searching/
*
* @returns
* An object containing query parameters.
*/
export const getSearchParamsFromURL = () => {
const searchParams = new URLSearchParams(window.location.search);
return {
qParam: searchParams.get('q') || searchParams.get('query') || '',
pageParam: searchParams.get('page') || '',
filterParam: searchParams.get('filters') || '',
};
};
/**
* Update URL parameters.
*
* This allows users to retrace their steps after a search.
*
* @param params Object
* Key/value pairs with the param name and value.
* Values can be strings or arrays.
*/
export const updateURLParams = (params) => {
const queryString = Object.entries(params)
.filter(([, value]) => value !== '' && !(Array.isArray(value) && value.length === 0))
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
window.history.pushState(null, '', `${window.location.pathname}?${queryString}`);
};
/**
* Search filters.
*
* Option properties:
* - text: Used for checkbox labels
* - value: References values in the "docs-site-section" metatag, which is included each search result.
* - id: Machine-friendly version of the text, used for analytics and URL params.
*/
export const SEARCH_FILTERS = [
{
title: 'Filter by',
options: [
{
text: 'Tutorials',
value: 'tutorials',
id: 'tutorials',
},
{
text: 'Installation docs',
value: 'install,subscribe',
id: 'install',
},
{
text: 'Administration docs',
value: 'administer,subscribe',
id: 'administer',
},
{
text: 'User docs',
value: 'subscribe,use_gitlab,gitlab_duo',
id: 'user',
},
{
text: 'Extension and API docs',
value: 'extend',
id: 'extend',
},
{
text: 'Contributor docs',
value: 'contribute',
id: 'contribute',
},
{
text: 'Solution docs',
value: 'solutions',
id: 'solutions',
},
],
},
];
/**
* Convert between filter values and filter IDs.
*
* @param Array arr
* Selected filters to convert.
* @param Boolean isToID
* true to convert to IDs, false to convert to values
*
* @returns Array
*/
export const convertFilterValues = (arr, isToID) => {
const convertedArr = arr.map((item) => {
for (const filter of SEARCH_FILTERS) {
for (const option of filter.options) {
if ((isToID && option.value === item) || (!isToID && option.id === item)) {
return isToID ? option.id : option.value;
}
}
}
return null;
});
return convertedArr.filter((item) => item !== null);
};
/**
* Keyboard shortcuts.
*/
export const activateKeyboardShortcut = () => {
document.addEventListener('keydown', (e) => {
// Focus on the search form with the forward slash and S keys.
const shortcutKeys = ['/', 's'];
if (!shortcutKeys.includes(e.key) || e.ctrlKey || e.metaKey) return;
if (/^(?:input|textarea|select|button)$/i.test(e.target.tagName)) return;
e.preventDefault();
document.querySelector('input[type="search"]').focus();
});
};
/**
* Remove formatting and boilerplate text from a page title
*
* We use this when we want to reference the title of a page,
* without any extra formatting.
*/
export const cleanTitle = (htmlTitle) => {
return (
htmlTitle
// Remove any text that starts with " | "
.replace(/\s*\|.*$/, '')
// Some pages use backticks to style words in titles as code.
// We don't want to include these in places where we aren't parsing markdown
.replaceAll('`', '')
.trim()
);
};