-
-
Notifications
You must be signed in to change notification settings - Fork 170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] implement-start-of-search #213
Open
kkemple
wants to merge
38
commits into
selfdefined:prod
Choose a base branch
from
kkemple:wip-search
base: prod
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
d1859dc
implement-start-of-search
kkemple b65b1ac
Merge branch 'prod' into wip-search
kkemple 59315c2
rename filter for removing markdown formatting
kkemple c4d6c7e
Merge branch 'wip-search' of https://github.com/kkemple/selfdefined i…
kkemple 5e6a3a7
fix conflicts
kkemple dc9e6b8
remove unused dev dependencies
kkemple 12de561
readd engine field
kkemple e908972
move search to serverless function
kkemple 15ed80b
Merge branch 'prod' into wip-search
kkemple fd6e0a5
update styling for search
kkemple 6b97a9e
Merge branch 'wip-search' of https://github.com/kkemple/selfdefined i…
kkemple 30c3f7e
Merge branch 'prod' into wip-search
kkemple 9ac06a3
update search to show snippets with searched text highlighted
kkemple 9572418
Merge branch 'wip-search' of https://github.com/kkemple/selfdefined i…
kkemple 302f6d5
Merge branch 'prod' into wip-search
kkemple 52ddbc8
rename search json file and permalink
kkemple 1954503
Merge branch 'wip-search' of https://github.com/kkemple/selfdefined i…
kkemple 87866d7
moved search to search page
kkemple 274f3b1
remove unused function in search component
kkemple 0f63bf5
Update 11ty/search.njk
kkemple 44f4c6d
Update 11ty/search.njk
kkemple 4489ffa
refactor HTML, CSS, and add search to results page
kkemple a268118
move serverless function into project
kkemple fd6b361
update search url in search results page
kkemple 202a22d
add missing dependencies for search function
kkemple f5ee15d
add variable declaration for definition in loop in search function
kkemple 953df5d
fix callback signatures in search function
kkemple 3ff2848
Merge branch 'prod' into wip-search
kkemple 665af72
add search results table of contents
9a527d5
feat(search): make smooth scrolling global default
ovlb 303fd28
feat(search): abstract button and input styles, mk focus more visible
ovlb b9680a7
Merge branch 'prod' into wip-search
ovlb 5393ea1
🙈 revert to using netlify
ovlb 4bd2312
Merge branch 'wip-search' of github.com:kkemple/selfdefined into wip-…
ovlb b8eaec3
Merge branch 'prod' into wip-search
ovlb 7e01f98
feat(search): port dark mode, style clean ups
ovlb 17903be
Merge branch 'prod' into wip-search
kkemple 10b14b7
Merge branch 'prod' into wip-search
kkemple File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<form method="get" action="/search" class="js-search-form search__form"> | ||
<label for="q" class="search__label sub-headline">Look up a definition</label> | ||
<div class="search__actions"> | ||
<input type="search" class="search__input" name="q" /> | ||
<button type="submit" class="-main">Search</button> | ||
</div> | ||
</form> |
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 |
---|---|---|
|
@@ -5,7 +5,6 @@ | |
<main> | ||
{{ content | safe }} | ||
{% include 'components/table-of-content.njk' %} | ||
|
||
</main> | ||
|
||
{% endblock %} | ||
|
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 |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{% extends 'layouts/base.njk' %} | ||
{% set titleWithPath = 'Search « ' %} | ||
|
||
{% block content %} | ||
<main> | ||
{{ content | safe }} | ||
</main> | ||
{% endblock %} |
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 |
---|---|---|
@@ -0,0 +1,14 @@ | ||
--- | ||
permalink: search-data.json | ||
--- | ||
[ | ||
{%- for item in collections.search -%} | ||
{ | ||
"url" : "{{ item.url }}", | ||
"title" : "{{ item.data.title }}", | ||
"defined" : "{{ item.data.defined }}", | ||
"subterms": "{{item.data.sub_terms | extractSubTermText }}", | ||
"content" : "{{ item.templateContent | removeMarkdownFormatting }}" | ||
}{% if not loop.last %},{% else %}{%- endif -%} | ||
{%- endfor -%} | ||
] |
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 |
---|---|---|
@@ -0,0 +1,104 @@ | ||
--- | ||
layout: layouts/search.njk | ||
--- | ||
|
||
<div class="article-content"> | ||
<div> | ||
{% include 'components/sub-page-header.njk' %} | ||
<ul class="js-search-results-menu seach-results-menu" /> | ||
</div> | ||
|
||
<div class="page"> | ||
<header class="introduction"> | ||
<h1 class="introduction__title thicc-headline"> | ||
Search Results | ||
</h1> | ||
</header> | ||
|
||
<section class="search"> | ||
{% include 'components/search.njk' %} | ||
<h2 aria-live="polite" role="status" class="js-search-title">Building your search results, one moment.</h2> | ||
<ul class="js-search-results-list" class="u-no-padding-left list" /> | ||
</section> | ||
</div> | ||
</div> | ||
|
||
{% block pageScript %} | ||
<script> | ||
function getAllOccurrences(str, match) { | ||
const matchLength = match.length; | ||
const indices = [] | ||
let startIndex = 0, index | ||
|
||
str = str.toLowerCase(); | ||
match = match.toLowerCase(); | ||
|
||
while ((index = str.indexOf(match, startIndex)) > -1) { | ||
indices.push(index); | ||
startIndex = index + matchLength; | ||
} | ||
|
||
return indices.map(index => { | ||
// this needs some cleaning up | ||
// currently there are edge cases that break the selection of the substring | ||
const start = index < 50 ? index : index - 50; | ||
const end = matchLength + 100; | ||
const exerpt = str.substr(start, end) | ||
|
||
return exerpt.replace(match, `<span class="search-results-list__list-item__highlight">${match}</span>`) | ||
}) | ||
} | ||
|
||
// don't use es6+ here because it will not be transpiled | ||
window.addEventListener('DOMContentLoaded', function() { | ||
function buildResultListItem(result, search) { | ||
const occurrences = getAllOccurrences(result.content, search) | ||
return `<h3 id="${result.title}"><a href="${result.url}">${result.title}</a></h3>${occurrences.map(occurrence => `<p>...${occurrence}...</p>`).join('')}` | ||
} | ||
|
||
function buildResultMenuItem(result) { | ||
return `<a href="#${result.title}">${result.title}</a>` | ||
} | ||
|
||
const init = async () => { | ||
try { | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const $searchTitle = document.querySelector('.js-search-title') | ||
const $resultsContainer = document.querySelector('.js-search-results-list') | ||
const $tableOfContentsContainer = document.querySelector('.js-search-results-menu') | ||
|
||
// remove any previous results from the list and menu | ||
$resultsContainer.innerHTML = '' | ||
$tableOfContentsContainer.innerHTML = '' | ||
|
||
const value = urlParams.get('q') | ||
const matches = await fetch(`/.netlify/functions/search?q=${value}`, {mode: 'cors'}) | ||
// const matches = await fetch(`https://climb-phi-staging.begin.app/self-defined-app-search?q=${value}`, {mode: 'cors'}) | ||
const results = await matches.json() | ||
|
||
$resultsContainer.innerHTML = '' | ||
$searchTitle.innerText = `We found ${results.length} ${results.length === 1 ? 'definition' : 'definitions'} containing \u275d${value}\u275e` | ||
|
||
// better to create a fragment here | ||
results.forEach((result) => { | ||
// build the actual result list item | ||
let $childListElement = document.createElement('li') | ||
$childListElement.classList.add('search-results-list__list-item') | ||
$childListElement.innerHTML = buildResultListItem(result, value) | ||
$resultsContainer.appendChild($childListElement) | ||
|
||
// build the table of content item | ||
let $childMenuElement = document.createElement('li') | ||
$childMenuElement.classList.add('search-results-menu__list-item') | ||
$childMenuElement.innerHTML = buildResultMenuItem(result, value) | ||
$tableOfContentsContainer.appendChild($childMenuElement) | ||
}) | ||
|
||
} catch (error) { | ||
console.log('error in search!', error) | ||
} | ||
} | ||
init() | ||
}) | ||
</script> | ||
{% endblock %} |
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 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 |
---|---|---|
@@ -0,0 +1,19 @@ | ||
button { | ||
appearance: none; | ||
border: none; | ||
border-radius: 0.25rem; | ||
cursor: pointer; | ||
font-family: $ext-sans; | ||
font-weight: bold; | ||
padding: 0.5rem 0.75rem; | ||
|
||
&.-main { | ||
background: var(--clr-very-blue); | ||
color: white; | ||
} | ||
|
||
&:focus { | ||
outline: 0.125rem solid var(--clr-very-blue); | ||
outline-offset: 0.125rem; | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,13 @@ | ||
input[type='text'], | ||
input[type='search'] { | ||
background: var(--clr-background); | ||
border: 0.125rem solid var(--clr-foreground); | ||
border-radius: 0.25rem; | ||
color: inherit; | ||
padding: 0.5rem 0.75rem; | ||
|
||
&:focus { | ||
border-color: var(--clr-very-blue); | ||
outline: 2px solid var(--clr-very-blue); | ||
} | ||
} |
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,5 +1,7 @@ | ||
* { | ||
box-sizing: border-box; | ||
font-family: inherit; | ||
font-size: inherit; | ||
} | ||
|
||
*::before { | ||
|
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 |
---|---|---|
@@ -0,0 +1,20 @@ | ||
.search__form { | ||
background: transparent; | ||
margin-bottom: 2rem; | ||
} | ||
|
||
.search__label { | ||
display: block; | ||
} | ||
|
||
.search__actions { | ||
display: flex; | ||
} | ||
|
||
.search__input { | ||
margin-right: 0.5rem; | ||
} | ||
|
||
.search-results-list__list-item__highlight { | ||
background: var(--clr-pale-yellow); | ||
} |
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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
const lunr = require('lunr'); | ||
const { default: axios } = require('axios'); | ||
|
||
exports.handler = async function({ queryStringParameters }, _, callback) { | ||
try { | ||
if (!queryStringParameters) { | ||
callback(new Error('missing query string parameter "q"')); | ||
} | ||
|
||
const { data: definitions } = await axios.get( | ||
'https://deploy-preview-213--selfdefined.netlify.app/search-data.json' | ||
); | ||
|
||
// create a map based on title for faster retrieval in search results | ||
const definitionMap = definitions.reduce((map, definition) => { | ||
map[definition.title] = definition; | ||
return map; | ||
}, {}); | ||
|
||
// create the full text search | ||
const idx = lunr(function() { | ||
this.ref('title'); | ||
this.field('title'); | ||
this.field('subterms'); | ||
this.field('content'); | ||
|
||
// load definitions into lunr index | ||
for (const definition in definitions) { | ||
this.add(definitions[definition]); | ||
} | ||
}); | ||
|
||
const matches = idx.search(queryStringParameters.q); | ||
const results = matches.map((match) => definitionMap[match.ref]); | ||
|
||
console.log(results) | ||
callback(null, { | ||
body: JSON.stringify(results), | ||
statusCode: 200 | ||
}); | ||
} catch (error) { | ||
console.log(error); | ||
callback(error); | ||
} | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kkemple: input:label associations are created with the
for
attribute pointing to a unique ID*. Thank you to @colabottles for pointing this out; I missed it!It's worth noting, the
title
attribute is not a good technique for defining the accessible name of a control because browsers and assistive technologies may not actually report thetitle
to the user. It's better to usearia-label
when we cannot use a real<label>
element, and we do have a real label here!*paging @ovlb in case he has some thoughts on a safer unique ID than
q
:)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dengeist
aria-label
is the better method, I agree. This is one case that ARIA should be used. Thanks for pointing that out!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re ID: might be safer to namespace it into
search-query
or alike. Currently it’s the only input we have, but it will probably not stay this way.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the input name need to change if we change the ID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily. The
name
is what’s used in FormData,document.forms.formName
, or — if old school — when handling aPOST
request coming from a form action on the server. Whereas the ID is used in HTML to link label and input. I wrote an article where this is explained in more detail: Association of labels and inputs.