diff options
author | Sarah German <sgerman@gitlab.com> | 2022-09-22 18:10:42 +0300 |
---|---|---|
committer | Kati Paizee <kpaizee@gitlab.com> | 2022-09-22 18:10:42 +0300 |
commit | 6ef828e917f82a7bd8a621c8a652e3127666ac37 (patch) | |
tree | 62b2f8c2f54d46bda3a58e8c45b1cad2e9777514 /content/frontend | |
parent | a5ac9d88bd1251c252d53680e8af0df0eef1d743 (diff) |
Add Lunr search UI components
Diffstat (limited to 'content/frontend')
-rw-r--r-- | content/frontend/search/components/lunr_page.vue | 97 | ||||
-rw-r--r-- | content/frontend/search/components/lunr_search_form.vue | 23 | ||||
-rw-r--r-- | content/frontend/search/lunrsearch.js | 26 | ||||
-rw-r--r-- | content/frontend/search/search.js | 5 |
4 files changed, 150 insertions, 1 deletions
diff --git a/content/frontend/search/components/lunr_page.vue b/content/frontend/search/components/lunr_page.vue new file mode 100644 index 00000000..5edcb04c --- /dev/null +++ b/content/frontend/search/components/lunr_page.vue @@ -0,0 +1,97 @@ +<script> +/* global lunr */ +import { GlSearchBoxByClick, GlLink } from '@gitlab/ui'; + +export default { + components: { + GlSearchBoxByClick, + GlLink, + }, + data() { + return { + query: '', + submitted: false, + error: false, + results: [], + contentMap: [], + relevancy_threshold: 15, + }; + }, + computed: { + noResults() { + return this.submitted && !this.results.length && !this.error; + }, + }, + async created() { + try { + // Load the search index and content map. + const [indexResp, mapResp] = await Promise.all([ + fetch('/assets/javascripts/lunr-index.json'), + fetch('/assets/javascripts/lunr-map.json'), + ]); + const lindex = await indexResp.json(); + this.contentMap = await mapResp.json(); + + // Initialize Lunr. + const idx = lunr.Index.load(lindex); + window.idx = idx; + + // If we have a query string in the URL, run the search. + const searchParams = new URLSearchParams(window.location.search); + if (searchParams.has('query')) { + this.search(searchParams.get('query')); + } + } catch (e) { + this.handleError(e); + } + }, + methods: { + onSubmit() { + if (this.query) { + this.search(this.query); + } + }, + search(query) { + this.query = query; + this.submitted = true; + + // Run the search. + this.results = window.idx.search(this.query); + + // Limit the results by relevancy score. + this.results = this.results.filter((key) => key.score > this.relevancy_threshold); + + // Add page titles to the result set. + Object.keys(this.results).forEach((key) => { + const contentItem = this.contentMap.find(({ id }) => id === this.results[key].ref); + this.results[key].title = contentItem.h1; + }); + + // Add the search term to the URL to allow linking to result pages. + const url = new URL(window.location); + url.searchParams.set('query', this.query); + window.history.pushState(null, '', url.toString()); + }, + handleError() { + this.error = true; + }, + }, +}; +</script> + +<template> + <div class="lunr-search"> + <h1>Search</h1> + <gl-search-box-by-click v-model="query" :value="query" @submit="onSubmit" /> + <div v-if="results.length" class="gl-font-sm gl-mb-6">{{ results.length }} results found</div> + + <ul v-if="results.length"> + <li v-for="result in results" :key="result.ref"> + <gl-link :href="`/${result.ref}`">{{ result.title }}</gl-link> + </li> + </ul> + + <p v-if="noResults" class="gl-py-5">No results found.</p> + <p v-if="error" class="gl-py-5" data-testid="lunr-error">Error fetching search index.</p> + </div> +</template> diff --git a/content/frontend/search/components/lunr_search_form.vue b/content/frontend/search/components/lunr_search_form.vue new file mode 100644 index 00000000..958e5b07 --- /dev/null +++ b/content/frontend/search/components/lunr_search_form.vue @@ -0,0 +1,23 @@ +<script> +import { GlSearchBoxByClick } from '@gitlab/ui'; + +export default { + components: { + GlSearchBoxByClick, + }, + data() { + return { + query: '', + }; + }, + methods: { + onSubmit() { + window.location.href = `/search/?query=${encodeURI(this.query)}`; + }, + }, +}; +</script> + +<template> + <gl-search-box-by-click v-model="query" class="lunr-search" @submit="onSubmit" /> +</template> diff --git a/content/frontend/search/lunrsearch.js b/content/frontend/search/lunrsearch.js new file mode 100644 index 00000000..ae0a06d9 --- /dev/null +++ b/content/frontend/search/lunrsearch.js @@ -0,0 +1,26 @@ +import Vue from 'vue'; +import LunrPage from './components/lunr_page.vue'; +import LunrSearchForm from './components/lunr_search_form.vue'; + +// Search results page (/search) +document.addEventListener('DOMContentLoaded', () => { + return new Vue({ + el: '.js-lunrsearch', + render(createElement) { + return createElement(LunrPage); + }, + }); +}); + +// Homepage and interior navbar search forms +document.addEventListener('DOMContentLoaded', () => { + return new Vue({ + el: '.js-lunr-form', + components: { + LunrSearchForm, + }, + render(createElement) { + return createElement(LunrSearchForm); + }, + }); +}); diff --git a/content/frontend/search/search.js b/content/frontend/search/search.js index ab202445..7c53542f 100644 --- a/content/frontend/search/search.js +++ b/content/frontend/search/search.js @@ -27,7 +27,10 @@ export const getAlgoliaCredentials = (crawler = 'production') => { export const getDocsVersion = () => { let docsVersion = 'main'; - if (document.querySelector('meta[name="docsearch:version"]').content.length > 0) { + if ( + document.querySelector('meta[name="docsearch:version"]') && + document.querySelector('meta[name="docsearch:version"]').content.length + ) { docsVersion = document.querySelector('meta[name="docsearch:version"]').content; } return docsVersion; |