-
Notifications
You must be signed in to change notification settings - Fork 231
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New search experience via Swiftype (#5223)
* Prototyping new Swiftype search integration * Remove script for building search index * Remove search * Script cleanup * Style adjustments * Relocate search bar to top of left nav * Back out inadvertent package.json additions * Reparent the Swiftype autocomplete container Co-authored-by: Christian Nunciato <[email protected]>
- Loading branch information
1 parent
5df058e
commit f0a24ce
Showing
20 changed files
with
111 additions
and
371 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,172 +1,38 @@ | ||
"use strict"; | ||
|
||
(function () { | ||
var searchBox = document.getElementById("search-query"); | ||
var spinner = document.getElementById("search-spinner"); | ||
var searchResultsContainer = document.getElementById("search-results"); | ||
|
||
// Use a worker to download and setup the index in the background. | ||
var worker = new Worker("/js/search-worker.js"); | ||
worker.onmessage = function (message) { | ||
var payload = message.data.payload; | ||
displaySearchResults(payload.results); | ||
}; | ||
|
||
// Extract the query from the browser's URL. | ||
var query = getQueryVariable("q"); | ||
if (query) { | ||
// Set the search-box's value to the query. | ||
searchBox.value = query; | ||
// Update the page title to include the query. | ||
document.title = query + " - Pulumi"; | ||
// Kick-off the search by sending a message to the worker. | ||
worker.postMessage({ type: "search", payload: query }); | ||
} else { | ||
// If no query, display empty results. | ||
displaySearchResults([]); | ||
} | ||
|
||
// Display the results of the search. | ||
function displaySearchResults(results) { | ||
// Hide the spinner. | ||
spinner.style.display = "none"; | ||
|
||
if (results.length) { | ||
// Group the results by category. | ||
var categoryMap = {}; | ||
for (var i = 0; i < results.length; i++) { | ||
var result = results[i]; | ||
var categoryName = getCategoryName(result.url); | ||
var category = categoryMap[categoryName] = categoryMap[categoryName] || []; | ||
prepareResult(result, categoryName); | ||
category.push(result); | ||
} | ||
|
||
// Build up the HTML to display. | ||
var appendString = ""; | ||
for (var i = 0; i < categories.length; i++) { | ||
var categoryName = categories[i].name; | ||
var category = categoryMap[categoryName]; | ||
if (category && category.length > 0) { | ||
appendString += buildCategoryString(categoryName, category); | ||
// Swiftype appends the autocomplete results container to the body element, which prevents us | ||
// from being able to position it in a way that scrolls alongside the other content in the L2 nav. | ||
// So we listen for DOM changes, and when Swiftype appends the element, we reposition it below the | ||
// input field. | ||
|
||
// Only bother doing this if we're on a page with a search box. | ||
if (document.querySelector("#search-container")) { | ||
const observer = new MutationObserver((mutations, observer) => { | ||
var [ mutation ] = mutations; | ||
|
||
// Only bother when nodes are added. | ||
if (mutation && mutation.addedNodes && mutation.addedNodes.length > 0) { | ||
const [ newNode ] = mutation.addedNodes; | ||
|
||
if (newNode && (typeof newNode.getAttribute === "function") && newNode.getAttribute("id") === "st-injected-content") { | ||
|
||
// Find our results container and reparent the Swiftype container with it. | ||
var resultsContainer = document.querySelector("#search-results"); | ||
if (resultsContainer) { | ||
resultsContainer.appendChild(newNode); | ||
} | ||
|
||
// Stop listening. | ||
observer.disconnect(); | ||
} | ||
searchResultsContainer.innerHTML = appendString; | ||
} else { | ||
searchResultsContainer.innerHTML = "<p>No results found.</p>"; | ||
} | ||
} | ||
}); | ||
|
||
var defaultCategory = "Other"; | ||
var categories = [ | ||
// Start listening for DOM mutation events. | ||
observer.observe( | ||
document.querySelector("body"), | ||
{ | ||
name: "APIs", | ||
predicate: function (url) { | ||
return url.startsWith("/docs/reference/pkg/"); | ||
} | ||
attributes: false, | ||
childList: true, // New childNodes are all we care about. | ||
subtree: false, | ||
}, | ||
{ | ||
name: "CLI", | ||
predicate: function (url) { | ||
return url.startsWith("/docs/reference/cli/"); | ||
} | ||
}, | ||
{ | ||
name: "Tutorials", | ||
predicate: function (url) { | ||
return url.startsWith("/docs/get-started/") || | ||
url.startsWith("/docs/tutorials/") || | ||
url.startsWith("/docs/guides/"); | ||
} | ||
}, | ||
{ | ||
name: defaultCategory, | ||
predicate: function (url) { | ||
return true; | ||
} | ||
}, | ||
]; | ||
|
||
function getCategoryName(url) { | ||
for (var i = 0; i < categories.length; i++) { | ||
var category = categories[i]; | ||
if (category.predicate(url)) { | ||
return category.name; | ||
} | ||
} | ||
return defaultCategory; | ||
} | ||
|
||
function prepareResult(result, categoryName) { | ||
switch (categoryName) { | ||
case "APIs": | ||
if (result.title.startsWith("Module ")) { | ||
result.display = result.title.substring("Module ".length); | ||
result.type = "module"; | ||
return; | ||
} else if (result.title.startsWith("Package ")) { | ||
result.display = result.title.substring("Package ".length); | ||
result.type = "package"; | ||
return; | ||
} | ||
break; | ||
|
||
case "CLI": | ||
if (result.title.length === 0 && result.url.startsWith("/docs/reference/cli/")) { | ||
var regex = /\/docs\/reference\/cli\/([a-z_]+)/gm; | ||
var match = regex.exec(result.url) | ||
if (match !== null) { | ||
result.display = match[1].replace(/_/g, " "); | ||
return; | ||
} | ||
} | ||
break; | ||
} | ||
|
||
result.display = result.title || result.url; | ||
} | ||
|
||
function buildCategoryString(categoryName, category) { | ||
var appendString = "<div class='search-results-category'><h2>" + categoryName + " (" + category.length + ")</h2>"; | ||
|
||
// Display the top 5 results first. | ||
appendString += "<ul>"; | ||
var topResults = category.splice(0, 5); | ||
for (var i = 0; i < topResults.length; i++) { | ||
var item = topResults[i]; | ||
var prefix = getPrefix(item, categoryName); | ||
appendString += "<li><a href='" + item.url + "' title='" + item.display + "'>" + prefix + item.display + "</a>"; | ||
} | ||
appendString += "</ul>"; | ||
|
||
// Now display any remaining results, sorted alphabetically. | ||
if (category.length > 0) { | ||
category.sort(function (l, r) { | ||
return l.display.toUpperCase() > r.display.toUpperCase() ? 1 : -1; | ||
}); | ||
appendString += "<ul>"; | ||
for (var i = 0; i < category.length; i++) { | ||
var item = category[i]; | ||
var prefix = getPrefix(item, categoryName); | ||
appendString += "<li><a href='" + item.url + "'>" + prefix + item.display + "</a>"; | ||
} | ||
appendString += "</ul>"; | ||
} | ||
|
||
appendString += "</div>"; | ||
return appendString; | ||
} | ||
|
||
function getPrefix(result, categoryName) { | ||
if (result.type) { | ||
switch (result.type) { | ||
case "module": | ||
return "<span class='symbol module' title='Module'></span>"; | ||
case "package": | ||
return "<span class='symbol package' title='Package'></span>" | ||
} | ||
} | ||
|
||
return ""; | ||
} | ||
})(); | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,54 @@ | ||
.search-results-category { | ||
@apply mb-4 justify-between flex-1 p-4 bg-gray-200 text-sm overflow-auto rounded; | ||
// Override the default styles of the Swiftype input field. | ||
.st-default-search-input { | ||
display: block !important; | ||
width: 100% !important; | ||
font-size: .875rem !important; | ||
padding: .75rem !important; | ||
padding-left: 2.5rem !important; | ||
border-radius: .25rem !important; | ||
border-color: #cbd5e0 !important; /* border-gray-400 */ | ||
font-family: "Open Sans" !important; | ||
background: none !important; | ||
|
||
&:not(:last-of-type) { | ||
@apply mr-4 | ||
} | ||
@include transition; | ||
|
||
h2 { | ||
@apply mt-0 mb-4 text-gray-700 text-xl; | ||
&:focus { | ||
border-color: #52a6da !important; /* border-blue-500 */ | ||
} | ||
|
||
ul { | ||
@apply p-0 list-none; | ||
@screen md { | ||
margin-right: 1em !important; | ||
} | ||
} | ||
|
||
li { | ||
.symbol { | ||
&:before { | ||
@apply text-white text-xs rounded mr-2 px-1; | ||
} | ||
// Override the default styles of the Swiftype autocomplete results container. | ||
.st-default-autocomplete { | ||
z-index: 40 !important; /* z-40 */ | ||
|
||
&.module { | ||
&:before { | ||
content: "M"; | ||
@apply bg-green-700; | ||
} | ||
} | ||
// Swiftype's JS imperatively positions the autocomplete container in relation to the top-left | ||
// corner of the document, which is okay if your search box scrolls with the page, but since ours | ||
// scrolls and sticks independently of the main content pane, this doesn't work. Overriding the | ||
// top and left settings allows the container to be positioned absolutely in place. | ||
top: auto !important; | ||
left: auto !important; | ||
|
||
&.package { | ||
&:before { | ||
content: "P"; | ||
@apply bg-purple-700; | ||
} | ||
} | ||
.st-query-present { | ||
.st-ui-result { | ||
.st-ui-type-heading { | ||
font-size: 0.75rem !important; /* text-xs */ | ||
color: #4387c7 !important; /* text-blue-600 */ | ||
margin-bottom: 4px !important; | ||
} | ||
.st-ui-type-detail { | ||
color: #4a5568 !important; /* text-gray-600 */ | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Override the default styles of the results overlay. | ||
.st-ui-container { | ||
@screen lg { | ||
top: 80px !important; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
ignoreFiles = [ "content/docs/.*" ] | ||
ignoreFiles = [ "content/docs/reference/pkg/*" ] | ||
refLinksErrorLevel = "WARNING" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.