Skip to content
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

[WEB-1230] -> Input Caching in typesense search #470

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions scripts/indexr.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,24 @@ const pageQuery = `
fields {
slug
}
excerpt(pruneLength: 6700)
excerpt(pruneLength: 100)
}
}
}
}
`;

function pageToTypesenseRecord({ node }) {
const { id, frontmatter, ...rest } = node;
console.log('node', node);
const { id, frontmatter, fields = {}, headings = [], ...rest } = node;

const formattedHeadings = headings.map(h => h.value || '').filter(Boolean);
return {
objectID: id,
...frontmatter,
title: frontmatter.title || '',
search_keyword: String(frontmatter.search_keyword || ''),
slug: fields.slug || '',
excerpt: frontmatter.excerpt || '',
headings: formattedHeadings,
...rest,
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/SearchHits.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export const CustomSearchBox = connectSearchBox(SearchBox);
/* eslint-disable react/no-danger */
const Hits = ({ hits }) => (
<ul className="style">
{hits.length < 1 ? <li>No search results found</li> : ''}
{hits.length < 1 ? <li className='px-5 py-3 rounded-lg text-center'>No search results found</li> : ''}
{hits.map((hit) => (
<li key={hit.title} className="shadow sm:rounded-lg">
<Link to={hit.fields.slug}>
<Link to={hit?.slug}>
<span className="search-title" dangerouslySetInnerHTML={{ __html: hit._highlightResult.title.value }} />
<p dangerouslySetInnerHTML={{ __html: hit._snippetResult.excerpt.value }} />
</Link>
Expand Down
6 changes: 3 additions & 3 deletions src/components/SearchHits.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ais-highlight-0000000000 {

.style {
list-style: none;
padding-top: 20px;
padding-top: 14px;
li {
margin-bottom: 6px;
background-color: #fff;
Expand All @@ -35,15 +35,15 @@ ais-highlight-0000000000 {
}
p {
margin: 6px 0;
font-size: 16px;
font-size: 14px;
line-height: 20px;
overflow-wrap: break-word;
}
}

.search-title {
font-weight: 700;
font-size: 14px;
font-size: 16px;
overflow-wrap: break-word;
color: #0E5AD9;
}
Expand Down
61 changes: 38 additions & 23 deletions src/components/SearchInputBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,11 @@ import { SearchHits } from './SearchHits';

import TypesenseInstantsearchAdapter from "typesense-instantsearch-adapter";

// window.$ = $;

/* Algolia Search Bar */
const ClickOutHandler = require('react-onclickout');

// Create the Typesense InstantSearch Adapter instance

// @ts-ignore
console.log(process.env.TYPESENSE_SEARCH_API_KEY);
console.log(process.env.TYPESENSE_HOST);
// console.log(process.env.TYPESENSE_SEARCH_API_KEY);
// console.log(process.env.TYPESENSE_HOST);
const typesenseInstantsearchAdapter = new TypesenseInstantsearchAdapter({
server: {
apiKey: process.env.TYPESENSE_SEARCH_API_KEY, // Use your Typesense search-only API key here
Expand All @@ -43,6 +38,16 @@ const typesenseInstantsearchAdapter = new TypesenseInstantsearchAdapter({

export const searchClient = typesenseInstantsearchAdapter.searchClient;

const debounce = (func, delay) => {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
};

class SearchInputBox extends React.Component {
constructor(props) {
super(props);
Expand All @@ -52,29 +57,46 @@ class SearchInputBox extends React.Component {
cookie: '',
hasInput: false,
refresh: false,
searchQuery: ''
};

this.debouncedSearch = debounce(this.handleSearch, 300);
}

// Algolia - clicking out exits searchbox
onClickOut = (event) => {
const searchInput = document.getElementsByClassName(
'ais-SearchBox-input',
)[0].value;
const domNode = ReactDOM.findDOMNode(this);
if (searchInput === '' || !domNode || !domNode.contains(event.target))
this.setState({ hasInput: false });
} // end onClickOut
}

handleKeyUp = (event) => {
const query = event.currentTarget.value;
this.setState({
hasInput: query.length > 2,
});

/* eslint-enabe class-methods-use-this */
this.setState({ searchQuery: '' }, () => {
this.debouncedSearch(query);
});
}

handleSearch = (query) => {
console.log('Searching for:', query);
this.setState({
refresh: !this.state.refresh,
searchQuery: query
});
}

render() {
const {
refresh, hasInput
refresh, hasInput, searchQuery
} = this.state;
return (
<>

<div className={!hasInput ? 'form-inline flex w-1/5 items-center pl-4' : 'form-inline flex w-1/5 items-center pl-4 float-searchBox'}>
<label htmlFor="search-lc" />

Expand All @@ -84,9 +106,8 @@ class SearchInputBox extends React.Component {
indexName={process.env.TYPESENSE_COLLECTION}
refresh={refresh}
>
<Configure hitsPerPage={5} />
<Configure hitsPerPage={10} />

{/* forcefeed className because component does not accept natively as prop */}
<div className="search-icon">
<svg
width="15"
Expand All @@ -97,7 +118,6 @@ class SearchInputBox extends React.Component {
>
<path d="M8 16C9.77498 15.9996 11.4988 15.4054 12.897 14.312L17.293 18.708L18.707 17.294L14.311 12.898C15.405 11.4997 15.9996 9.77544 16 8C16 3.589 12.411 0 8 0C3.589 0 0 3.589 0 8C0 12.411 3.589 16 8 16ZM8 2C11.309 2 14 4.691 14 8C14 11.309 11.309 14 8 14C4.691 14 2 11.309 2 8C2 4.691 4.691 2 8 2Z" fill="black"/>
</svg>

</div>
<SearchBox
className="w-full pl-2"
Expand All @@ -107,30 +127,25 @@ class SearchInputBox extends React.Component {
translations={{
placeholder: 'Search',
}}
onKeyUp={(event) => {
this.setState({
hasInput: event.currentTarget.value.length > 2,
});
}}
onKeyUp={this.handleKeyUp}
/>

<div className={!hasInput ? 'input-empty' : 'absolute bg-white search_results border'}>
<div className="container">
<div className="row">
<div className="col-12">
<SearchHits hitComponent={Hits} />
{searchQuery && <SearchHits hitComponent={Hits} />}
</div>
</div>
<div className="row">
<div className="col-12">
<Pagination />
{searchQuery && <Pagination />}
</div>
</div>
</div>
</div>
</InstantSearch>
</ClickOutHandler>

</div>
</>
);
Expand Down
23 changes: 17 additions & 6 deletions src/templates/page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -350,22 +350,27 @@ body {
}
.float-searchBox {
position: fixed;
top: 0;
right: 0;
top: 30px;
right: 30px;
z-index: 3;
padding: 1.6rem 1rem;
height: 100%;
max-height: 94vh;
display: block;
border-left: 1px solid rgba(229, 231, 235, var(--tw-border-opacity));
background-color: #fff;
border-radius: 8px;
backdrop-filter: blur(26px);
padding: 0.9rem 1rem;
transition: all 0.25s ease-in;
@media (max-width: 767px) {
width: 85% !important;
height: 90vh !important;
}
@media (min-width: 768px) {
width: 34%;
width: 45%;
height: 90vh !important;
}
@media (min-width: 1024px) {
width: 28%;
width: 35%;
}
@media (min-width: 1280px) {
width: 24.1%;
Expand All @@ -391,6 +396,7 @@ body {
border-left: 0;
background-color: #f5f5f5;
overflow-y: auto;
border-radius: 8px;
@media (max-width: 767px) {
top: 71px;
}
Expand All @@ -409,6 +415,11 @@ body {
}
}
}
.ais-SearchBox-input{
font-size: 18px;
border-radius: 5px;
padding: .5rem 1rem;
}
}
.ais-SearchBox-input{
outline: none!important;
Expand Down
Loading