Skip to content

Commit 1680b01

Browse files
marko-bekhtayrodiere
authored andcommitted
Include highlighting in the guide search results
1 parent 6756365 commit 1680b01

File tree

5 files changed

+157
-82
lines changed

5 files changed

+157
-82
lines changed

_includes/index-docs.html

+12-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{% assign by_type = index.quarkus | map: "types" | first %}
66

77
<div id="guides-app"
8-
data-url="{{ site.search.url }}"
8+
data-search-api-server="{{ site.search.url }}"
99
data-initial-timeout="{{ site.search.initial-timeout }}"
1010
data-more-timeout="{{ site.search.more-timeout }}"
1111
data-min-chars="{{ site.search.min-chars }}"
@@ -51,11 +51,15 @@ <h1 class="title">{{ page.title }}</h1>
5151
</div>
5252
</Transition>
5353
<div class="grid-wrapper">
54-
<div v-for="card in cardHits"
55-
v-html="card.innerHTML"
56-
class="grid__item docs-card"
57-
:class="Array.from(card.classList)">
54+
{% raw %}
55+
<div v-for="hit in searchHits" :class="`${hit.type}bkg grid__item docs-card`">
56+
<h4><a :href="hit.url" :target="hit.url.startsWith('http') ? '_blank' : ''" v-html="hit.title"></a></h4>
57+
<div class="description" v-html="hit.summary"></div>
58+
<div class="content-highlights">
59+
<p v-for="contentLine in hit.content" v-html="contentLine"></p>
60+
</div>
5861
</div>
62+
{% endraw %}
5963
<Transition name="fade-in">
6064
<div v-if="loading" class="loading-placeholder">
6165
Loading...
@@ -92,7 +96,7 @@ <h3 id="guide">How-to Guides</h3>
9296
</div>
9397
<div class="grid-wrapper">
9498
{% for guide in values %}
95-
{% include index-doc-item.html class="guidebkg" docversion=docversion
99+
{% include index-doc-item.html class="howtobkg" docversion=docversion
96100
title=guide.title url=guide.url summary=guide.summary
97101
keywords=guide.keywords categories=guide.categories origin=guide.origin %}
98102
{% endfor %}
@@ -108,7 +112,7 @@ <h3 id="concept">Concepts</h3>
108112
</div>
109113
<div class="grid-wrapper">
110114
{% for guide in values %}
111-
{% include index-doc-item.html class="conceptbkg" docversion=docversion
115+
{% include index-doc-item.html class="conceptsbkg" docversion=docversion
112116
title=guide.title url=guide.url summary=guide.summary
113117
keywords=guide.keywords categories=guide.categories origin=guide.origin %}
114118
{% endfor %}
@@ -147,7 +151,7 @@ <h3>General Guides</h3>
147151
</div>
148152
<div class="grid-wrapper">
149153
{% for guide in values %}
150-
{% include index-doc-item.html class="referencebkg" docversion=docversion
154+
{% include index-doc-item.html class="guidebkg" docversion=docversion
151155
title=guide.title url=guide.url summary=guide.summary
152156
keywords=guide.keywords categories=guide.categories origin=guide.origin %}
153157
{% endfor %}

_includes/index-guides.html

+61-61
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{% assign data_source = include.source %}
33

44
<div id="guides-app"
5-
data-url="{{ site.search.url }}"
5+
data-search-api-server="{{ site.search.url }}"
66
data-initial-timeout="{{ site.search.initial-timeout }}"
77
data-more-timeout="{{ site.search.more-timeout }}"
88
data-min-chars="{{ site.search.min-chars }}"
@@ -28,70 +28,70 @@
2828
<div>
2929
<h1 class="title">{{ page.title }}</h1>
3030
</div>
31-
<div v-if="hasInput" class="grid-wrapper guides results vuejs"
32-
:class="{ empty: !loading && !hasHits, 'vuejs-enabled': true, loading: loading }">
33-
<div class="grid__item width-12-12 click-cards">
34-
<Transition name="fade-in">
35-
<div v-if="!loading && !hasHits" class="empty-placeholder">
36-
Sorry, no guides matched your search. Please try again.
37-
</div>
38-
</Transition>
39-
<Transition name="fade-in">
40-
<div v-if="!loading && !hasHits" class="empty-placeholder">
41-
Sorry, no guides matched your search. Please try again.
42-
</div>
43-
</Transition>
44-
<ul class="list">
45-
<li>
46-
<ul class="grid-wrapper list-item">
47-
<li v-for="card in cardHits"
48-
v-html="card.innerHTML"
49-
class="card"
50-
:class="Array.from(card.classList)">
51-
</li>
31+
<div class="grid-wrapper guides" id="docs">
32+
<div :class="`grid__item width-6-12 width-12-12-m guide-categories callout-wrapper ${!loading && hasHits ? 'hidden' : ''}`">
33+
<h3>View Category</h3>
34+
<ul>
35+
{% for item in site.data[data_source].categories %}
36+
<li>
37+
<a href="#{{ item.cat-id }}">{{ item.category }}</a>
38+
</li>
39+
{% endfor %}
5240
</ul>
53-
</li>
54-
</ul>
55-
<Transition name="fade-in">
56-
<div v-if="loading" class="loading-placeholder">
57-
Loading...
41+
</div>
42+
43+
<div class="grid__item width-6-12 width-12-12-m callout-wrapper">
44+
<div class="grid-wrapper callout">
45+
<div class="grid__item width-12-12">
46+
<h3>Quarkus Cheat Sheet</h3>
47+
</div>
48+
<div class="grid__item col">
49+
<a href="https://lordofthejars.github.io/quarkus-cheat-sheet/" target="_blank">
50+
<span class="col-icon"><i class="far fa-file-pdf"></i></span>
51+
<span class="col-link">Download full cheatsheet as PDF</span>
52+
</a>
53+
</div>
54+
<div class="grid__item col">
55+
<a href="https://developers.redhat.com/search?t=quarkus&f=type%7Echeat_sheet" target="_blank">Get more cheatsheets on the Red Hat Developers website <i class="fas fa-external-link-alt"></i></a>
56+
</div>
5857
</div>
59-
</Transition>
60-
</div>
61-
</div>
58+
</div>
59+
<div v-if="hasInput" class="grid__item width-12-12 click-cards vuejs"
60+
:class="{ empty: !loading && !hasHits, 'vuejs-enabled': true, loading: loading }">
61+
<Transition name="fade-in">
62+
<div v-if="!loading && !hasHits" class="empty-placeholder">
63+
Sorry, no guides matched your search. Please try again.
64+
</div>
65+
</Transition>
66+
<ul class="list results">
67+
<li>
68+
<ul class="grid-wrapper list-item">
69+
{% raw %}
70+
<li class="card" v-for="hit in searchHits">
71+
<a :href="hit.url" :target="hit.url.startsWith('http') ? '_blank' : ''"></a>
72+
<p class="title" v-html="hit.title"></p>
73+
<div class="description" v-html="hit.summary"></div>
74+
<div class="content-highlights">
75+
<p v-for="contentLine in hit.content" v-html="contentLine"></p>
76+
</div>
77+
<div class="origin" v-if="hit.url.startsWith('http')">
78+
<img src="/assets/images/quarkiverse_icon_default.svg" width="25" height="25"/>
79+
<span>Quarkiverse Hub</span>
80+
</div>
81+
</li>
82+
{% endraw %}
83+
</ul>
84+
</li>
85+
</ul>
86+
<Transition name="fade-in">
87+
<div v-if="loading" class="loading-placeholder">
88+
Loading...
89+
</div>
90+
</Transition>
91+
</div>
6292
<!-- Static content displayed when there is no input in the search form or Javascript is disabled,
6393
but also used as a source of data for cards displayed by the vue.js app. -->
64-
<div v-else class="grid-wrapper guides" id="docs">
65-
66-
<div class="grid__item width-6-12 width-12-12-m guide-categories callout-wrapper">
67-
<h3>View Category</h3>
68-
<ul>
69-
{% for item in site.data[data_source].categories %}
70-
<li>
71-
<a href="#{{ item.cat-id }}">{{ item.category }}</a>
72-
</li>
73-
{% endfor %}
74-
</ul>
75-
</div>
76-
77-
<div class="grid__item width-6-12 width-12-12-m callout-wrapper">
78-
<div class="grid-wrapper callout">
79-
<div class="grid__item width-12-12">
80-
<h3>Quarkus Cheat Sheet</h3>
81-
</div>
82-
<div class="grid__item col">
83-
<a href="https://lordofthejars.github.io/quarkus-cheat-sheet/" target="_blank">
84-
<span class="col-icon"><i class="far fa-file-pdf"></i></span>
85-
<span class="col-link">Download full cheatsheet as PDF</span>
86-
</a>
87-
</div>
88-
<div class="grid__item col">
89-
<a href="https://developers.redhat.com/search?t=quarkus&f=type%7Echeat_sheet" target="_blank">Get more cheatsheets on the Red Hat Developers website <i class="fas fa-external-link-alt"></i></a>
90-
</div>
91-
</div>
92-
</div>
93-
94-
<div class="grid__item width-12-12 click-cards">
94+
<div v-else class="grid__item width-12-12 click-cards">
9595
<ul class="list">
9696
{% for item in site.data[data_source].categories %}
9797
<li>

_sass/includes/whitecards.scss

+12
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ grid-column: span 3;
119119
line-height: 1.1rem;
120120
}
121121

122+
span.highlighted {
123+
font-weight: bold;
124+
font-style: italic;
125+
line-height: inherit;
126+
}
127+
128+
.content-highlights p {
129+
font-size: .7rem;
130+
line-height: .8rem;
131+
opacity: 0.8;
132+
}
133+
122134
&:hover, &:focus {
123135
background-color: $light-blue;
124136
border:1px solid $light-blue;

_sass/layouts/documentation.scss

+22-1
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,16 @@ Styles for the documentation index page
252252
}
253253
}
254254

255+
.content-highlights {
256+
margin: 1rem 0 0 90px;
257+
p {
258+
font-size: .7rem;
259+
line-height: .8rem;
260+
opacity: 0.8;
261+
margin-bottom: .5rem;
262+
}
263+
}
264+
255265
&.quarkiverse .origin {
256266
padding-left: 120px;
257267
text-align: left;
@@ -267,6 +277,12 @@ Styles for the documentation index page
267277
position: absolute;
268278
left: 90px;
269279
}
280+
281+
span.highlighted {
282+
font-weight: bold;
283+
color: inherit;
284+
line-height: inherit;
285+
}
270286
}
271287

272288
.tutorialbkg {
@@ -275,11 +291,16 @@ Styles for the documentation index page
275291
}
276292

277293
.guidebkg {
294+
background: url($baseurl + '/assets/images/documentation/docsicon-referencedocs.svg') no-repeat;
295+
background-size: 80px 80px;
296+
}
297+
298+
.howtobkg {
278299
background: url($baseurl + '/assets/images/documentation/docsicon-guides.svg') no-repeat;
279300
background-size: 80px 80px;
280301
}
281302

282-
.conceptbkg {
303+
.conceptsbkg {
283304
background: url($baseurl + '/assets/images/documentation/docsicon-concepts.svg') no-repeat;
284305
background-size: 80px 80px;
285306
}

assets/javascript/guides-app.js

+50-12
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const appElement = document.querySelector(appSelector);
1919

2020
const app = createApp({
2121
props: {
22-
url: String,
22+
searchApiServer: String,
2323
initialTimeout: Number,
2424
moreTimeout: Number,
2525
minChars: Number,
@@ -85,8 +85,8 @@ const app = createApp({
8585
hasHits() {
8686
return this.search.result.hits.length > 0
8787
},
88-
cardHits() {
89-
return this.search.result.hits.map(hit => this.guidesPathToCardHtmlElement.get(hit.id)).filter(e => e)
88+
searchHits() {
89+
return this.search.result.hits
9090
}
9191
},
9292
mounted() {
@@ -101,7 +101,37 @@ const app = createApp({
101101
cards = appElement.querySelectorAll(cardSelector)
102102
}
103103
this.guidesPathToCardHtmlElement = new Map(Array.from(cards)
104-
.map(element => [new URL(element.querySelector('a').href).pathname, element]))
104+
.map(element => {
105+
const link = element.querySelector('h4 a')
106+
if (link) {
107+
// new versions:
108+
const url = link.getAttribute('href');
109+
return [
110+
new URL(link.href).pathname,
111+
{
112+
url: url,
113+
className: element.className,
114+
title: link.innerHTML,
115+
summary: element.querySelector('div .description').innerHTML,
116+
keywords: element.querySelector('div .keywords').innerHTML,
117+
categories: element.querySelector('div .categories').innerHTML,
118+
origin: element.querySelector('div .origin')?.innerHTML
119+
}];
120+
} else {
121+
// older Quarkus versions:
122+
const url = element.querySelector('a').getAttribute('href')
123+
return [
124+
url,
125+
{
126+
url: url,
127+
className: element.className,
128+
title: element.querySelector('p.title').innerHTML,
129+
summary: element.querySelector('div.description').innerHTML,
130+
keywords: element.querySelector('div.keywords').innerHTML,
131+
origin: element.querySelector('div.origin')?.innerHTML
132+
}];
133+
}
134+
}))
105135

106136
// Load more results on scroll
107137
document.addEventListener('scroll', e => {
@@ -157,11 +187,14 @@ const app = createApp({
157187
}
158188
const queryParams = {
159189
page: this.search.page,
160-
version: this.quarkusVersion
190+
version: this.quarkusVersion,
191+
contentSnippets: 2,
192+
contentSnippetsLength: 120,
193+
highlightCssClass: 'highlighted'
161194
}
162195
Object.assign(queryParams, this.search.input)
163196
const result = await this._jsonFetch(controller, 'GET', queryParams, timeout)
164-
this.search.result.hits = this.search.result.hits.concat(result.hits)
197+
this.search.result.hits = this.search.result.hits.concat(this._processHits(result.hits))
165198
this.search.result.hasMoreHits = result.hits.length > 0
166199
}
167200
catch(error) {
@@ -182,9 +215,15 @@ const app = createApp({
182215
}
183216
}
184217
},
218+
_processHits(serverHits) {
219+
return serverHits.map(hit => {
220+
hit.content = hit?.content.map(paragraph => `...${paragraph}...`)
221+
return hit;
222+
})
223+
},
185224
async _jsonFetch(controller, method, queryParams, timeout) {
186225
const timeoutId = setTimeout(() => controller.abort(), timeout)
187-
const response = await fetch(this.url + '?' + ( new URLSearchParams( queryParams ) ).toString(), {
226+
const response = await fetch(`${this.searchApiServer}api/guides/search?${new URLSearchParams( queryParams )}`, {
188227
method: method,
189228
signal: controller.signal,
190229
body: null
@@ -203,21 +242,20 @@ const app = createApp({
203242

204243
return Array.from(this.guidesPathToCardHtmlElement)
205244
.filter(([path, card]) => this._javascriptFilter(card, terms, categories))
206-
.map(([path, _]) => { return { id: path } })
245+
.map(([_, card]) => card)
207246
},
208247
_javascriptFilter(card, terms, categories) {
209248
let match = true
210249
if (match && categories) {
211-
const categoriesElem = card.getElementsByClassName('categories').item(0)
212-
match = categoriesElem && this._containsAllCaseInsensitive(categoriesElem, categories)
250+
match = this._containsAllCaseInsensitive(card.categories, categories)
213251
}
214252
if (match && terms) {
215-
match = this._containsAllCaseInsensitive(card, terms)
253+
match = this._containsAllCaseInsensitive(`${card.keywords}${card.summary}${card.title}${card.categories}`, terms)
216254
}
217255
return match
218256
},
219257
_containsAllCaseInsensitive(elem, terms) {
220-
const text = (elem.textContent || elem.innerText || '').toLowerCase()
258+
const text = (elem ? elem : '').toLowerCase();
221259
for (let i in terms) {
222260
if (text.indexOf(terms[i].toLowerCase()) < 0) {
223261
return false

0 commit comments

Comments
 (0)