Skip to content

Commit 55c8e1f

Browse files
author
Andrew Brookins
committed
Add search via redis-sitesearch
1 parent 914b665 commit 55c8e1f

File tree

12 files changed

+2650
-36
lines changed

12 files changed

+2650
-36
lines changed

docusaurus.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ module.exports = {
1111
favicon: 'img/favicon.ico',
1212
organizationName: 'redis-developer', // Usually your GitHub org/user name.
1313
projectName: 'redis-developer', // Usually your repo name.
14+
stylesheets: [
15+
'@abrookins-redis/redis-sitesearch-frontend/dist/style.css'
16+
],
1417
themeConfig: {
1518

1619
// ...
@@ -157,6 +160,6 @@ module.exports = {
157160
],
158161
],
159162
plugins: [
160-
'docusaurus-plugin-sass', path.resolve(__dirname, 'plugins', 'gtm')
163+
'docusaurus-plugin-sass', path.resolve(__dirname, 'plugins', 'gtm')
161164
],
162165
};

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@
1212
"clear": "docusaurus clear"
1313
},
1414
"dependencies": {
15+
"@abrookins-redis/redis-sitesearch-frontend": "^1.0.2",
1516
"@docusaurus/core": "2.0.0-alpha.70",
1617
"@docusaurus/preset-classic": "2.0.0-alpha.70",
1718
"@docusaurus/theme-search-algolia": "^2.0.0-alpha.70",
1819
"@mdx-js/react": "^1.6.21",
1920
"clsx": "^1.1.1",
2021
"docusaurus-plugin-sass": "^0.1.11",
22+
"i": "^0.3.6",
23+
"npm": "^7.5.4",
2124
"react": "^16.8.4",
2225
"react-dom": "^16.8.4",
2326
"react-scroll": "^1.8.1",

src/pages/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Hero from '@theme/Hero';
1010
import Languages from '@theme/Languages';
1111
import Demos from '@theme/Demos';
1212
import Resources from '@theme/Resources';
13+
import Search from '@theme/Search';
1314

1415
function Home() {
1516
const context = useDocusaurusContext();
@@ -21,6 +22,7 @@ function Home() {
2122
<Hero />
2223
<main className="home-main">
2324
<Languages />
25+
<Search />
2426
<Demos />
2527
<Resources />
2628
</main>

src/theme/DocSidebar/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Logo from '@theme/Logo';
1717
import IconArrow from '@theme/IconArrow';
1818
import IconMenu from '@theme/IconMenu';
1919
import styles from './styles.module.css';
20+
import Search from '@theme/Search';
2021
const MOBILE_TOGGLE_SIZE = 24;
2122

2223
import useBaseUrl from '@docusaurus/useBaseUrl';
@@ -189,6 +190,7 @@ function DocSidebar({
189190
'menu--show': showResponsiveSidebar,
190191
[styles.menuWithAnnouncementBar]: !isAnnouncementBarClosed && scrollY === 0
191192
})}>
193+
<Search inline="true"/>
192194
<button aria-label={showResponsiveSidebar ? 'Close Menu' : 'Open Menu'} aria-haspopup="true" className="button button--secondary button--sm menu__button" type="button" onClick={() => {
193195
setShowResponsiveSidebar(!showResponsiveSidebar);
194196
}}>

src/theme/LayoutHead/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ export default function LayoutHead(props) {
5959

6060
<link rel="preconnect" href="https://fonts.gstatic.com" />
6161
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&family=Roboto+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"></link>
62+
63+
<link href="/static/css/redis-sitesearch.css" rel="stylesheet"/>
64+
<link href="/static/css/redis-sitesearch-overrides.css" rel="stylesheet"/>
6265
</Head>
6366

6467
<SearchMetadatas tag={DEFAULT_SEARCH_TAG} locale={currentLocale} {...searchMetadatas} />
@@ -71,5 +74,5 @@ export default function LayoutHead(props) {
7174
>
7275
{metadatas.map((metadata, i) => <meta key={`metadata_${i}`} {...metadata} />)}
7376
</Head>
74-
</>;
77+
</>;
7578
}

src/theme/Search/index.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import React, {useEffect} from 'react'
2+
import RedisSiteSearch from '@abrookins-redis/redis-sitesearch-frontend/dist/redis-sitesearch.esm'
3+
4+
const SEARCH_API_URL = "https://search-service.redislabs.com/search"
5+
const SEARCH_SITE = "https://developer.redislabs.com"
6+
const THIRTY_SECONDS = 30000
7+
const SEARCH_LOGO = '<a class="powered-by-redisearch" href="https://oss.redislabs.com/redisearch/"></a>'
8+
9+
10+
function escapeHtml(str) {
11+
return str.replace(/[&<>"'\/]/g, function (s) {
12+
var entityMap = {
13+
"&": "&amp;",
14+
"<": "&lt;",
15+
">": "&gt;",
16+
'"': '&quot;',
17+
"'": '&#39;',
18+
"/": '&#x2F;'
19+
};
20+
21+
return entityMap[s];
22+
});
23+
}
24+
25+
function setWithExpiry(key, value, ttl) {
26+
const now = new Date()
27+
28+
const item = {
29+
value: value,
30+
expiry: now.getTime() + ttl,
31+
}
32+
localStorage.setItem(key, JSON.stringify(item))
33+
}
34+
35+
36+
function getWithExpiry(key) {
37+
const itemStr = localStorage.getItem(key)
38+
if (!itemStr) {
39+
return null
40+
}
41+
const item = JSON.parse(itemStr)
42+
const now = new Date()
43+
44+
if (now.getTime() > item.expiry) {
45+
localStorage.removeItem(key)
46+
return null
47+
}
48+
49+
return item.value
50+
}
51+
52+
53+
function Search(props) {
54+
useEffect(function onFirstMount() {
55+
const searchLogo = document.querySelector('.redisearch-logo')
56+
var lastQuery
57+
58+
searchLogo.addEventListener('mousedown', (e) => {
59+
e.preventDefault()
60+
})
61+
62+
new RedisSiteSearch('#redis-sitesearch', {
63+
debounceTime: 2,
64+
zIndex: 100,
65+
66+
search: input => {
67+
const trimmedInput = input.trim()
68+
const url = `${SEARCH_API_URL}?q=${trimmedInput}*&site=${SEARCH_SITE}`
69+
70+
if (input.length === 0) {
71+
return []
72+
}
73+
74+
try { xhr.abort(); } catch(e){}
75+
76+
// Save the query so we can append it to a selected result URL later.
77+
// We use this to track search queries.
78+
lastQuery = trimmedInput
79+
80+
const cachedResults = getWithExpiry(url)
81+
82+
if (cachedResults) {
83+
return cachedResults
84+
}
85+
86+
return new Promise(resolve => {
87+
fetch(url)
88+
.catch(function(err) {
89+
console.error('Error querying search API: ', err)
90+
resolve([])
91+
})
92+
.then(function(response) {
93+
if (response === undefined || !response.ok) {
94+
console.error('Error querying search API: ', response)
95+
resolve([])
96+
return
97+
}
98+
return response.json()
99+
})
100+
.then(function(data) {
101+
if (data === undefined || !data) {
102+
return
103+
}
104+
// Push a fake 'no results' document if there were no results.
105+
var results
106+
if (!data.results.length) {
107+
const safeInput = escapeHtml(trimmedInput)
108+
results = [
109+
{
110+
title: '',
111+
section_title: `No results found for '${safeInput}'`,
112+
body: '',
113+
hierarchy: [''],
114+
},
115+
]
116+
} else {
117+
results = data.results
118+
}
119+
120+
setWithExpiry(url, results, THIRTY_SECONDS)
121+
resolve(results)
122+
})
123+
})
124+
},
125+
126+
renderResult: (result, props) => {
127+
let sectionTitle = "",
128+
hasSectionTitle = result.section_title !== ""
129+
130+
if (hasSectionTitle) {
131+
sectionTitle = `
132+
<div className="search-section-title">
133+
${result.section_title}
134+
</div>
135+
`
136+
}
137+
138+
return `
139+
<li ${props}>
140+
<div class="search-left">
141+
<div class="search-title">
142+
${result.title}
143+
</div>
144+
</div>
145+
<div class="search-right">
146+
${sectionTitle}
147+
<div class="search-body">
148+
${result.body}
149+
</div>
150+
</div>
151+
</li>
152+
`
153+
},
154+
155+
getResultValue: result => "",
156+
157+
// Open the selected article in
158+
// a new window
159+
onSubmit: result => {
160+
if (result) {
161+
lastQuery = encodeURIComponent(lastQuery)
162+
window.open(`${result.url}?s=${lastQuery}`, "_top")
163+
}
164+
},
165+
166+
onUpdate: (results, selectedIndex) => {
167+
const redisearchLogo = document.querySelector('.redisearch-logo')
168+
if (results.length) {
169+
redisearchLogo.innerHTML = SEARCH_LOGO
170+
} else {
171+
redisearchLogo.innerHTML = ""
172+
}
173+
}
174+
})
175+
})
176+
177+
if (props.inline) {
178+
return <div id="redis-sitesearch" className="redis-sitesearch redis-sitesearch-inline">
179+
<input className="redis-sitesearch-input"/>
180+
181+
<div className="redis-sitesearch-result-list-wrapper">
182+
<ul className="redis-sitesearch-result-list"></ul>
183+
<div className="redisearch-logo">
184+
</div>
185+
</div>
186+
</div>
187+
} else {
188+
return <section className="rds-search">
189+
<div className="container">
190+
<h2 className="section-title">Search</h2>
191+
<div id="redis-sitesearch" className="redis-sitesearch">
192+
<input className="redis-sitesearch-input"/>
193+
194+
<div className="redis-sitesearch-result-list-wrapper">
195+
<ul className="redis-sitesearch-result-list"></ul>
196+
<div className="redisearch-logo">
197+
</div>
198+
</div>
199+
</div>
200+
</div>
201+
</section>;
202+
}
203+
}
204+
205+
export default Search;

0 commit comments

Comments
 (0)