Skip to content

Commit

Permalink
✨✅ feat: display loader within hooks if necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
niketpathak committed Nov 18, 2024
1 parent 028f427 commit 0b76c74
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 17 deletions.
14 changes: 14 additions & 0 deletions cypress/e2e/typeahead-testable.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,20 @@ context('Typeahead', () => {
cy.get('@input20').type('r', { delay: 100 });
cy.get('.typeahead-test-twenty .tt-suggestion').first().should('contain.text', 'Grey');
cy.get('@input20').clear();

// should display loader template within the updateHits hooks
cy.intercept('GET', 'https://restcountries.com/v2/name/a', {
fixture: 'countries.json',
throttleKbps: 1000,
delay: 1000,
}).as('getCountries');
cy.get('#input-twentyA').as('input20A').type('a', { delay: 0 });
cy.get('.typeahead-test-twentyA .tt-loader').should('exist');
cy.get('.typeahead-test-twentyA .tt-list').as('list20A').children().should('have.length', 5);
cy.wait('@getCountries');
cy.get('.typeahead-test-twentyA .tt-header').should('not.exist');
cy.get('.typeahead-test-twentyA .tt-footer').should('not.exist');
cy.get('@input20A').clear();
});

// https://on.cypress.io/interacting-with-elements
Expand Down
35 changes: 35 additions & 0 deletions demo/assets/js/testable.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
const colors = [
{ name: 'Red', value: 'RD', hash: 'red' },
{ name: 'Blue', value: 'BL', hash: 'blue', group: 'Shades of Blue' },
Expand Down Expand Up @@ -988,6 +989,7 @@ const test19 = typeahead({
},
});

// eslint-disable-next-line no-undef
const test20 = typeahead({
input: document.getElementById('input-twenty'),
source: {
Expand Down Expand Up @@ -1058,3 +1060,36 @@ const test20 = typeahead({
},
},
});

// eslint-disable-next-line no-undef
const test20A = typeahead({
input: document.getElementById('input-twentyA'),
source: {
local: [],
keys: ['name', 'capital'],
},
hooks: {
updateHits: async (resultSet, loader) => {
loader();
const response = await fetch(`https://restcountries.com/v2/name/${resultSet.query}`);
const text = await response.text();
resultSet.hits = text && JSON.parse(text);
loader(false);

return resultSet;
},
},
classNames: {
wrapper: 'typeahead-standalone typeahead-test-twentyA',
},
highlight: true,
templates: {
suggestion: (item) => {
return item.name + ', ' + item.capital;
},
loader: () => 'LOADING...',
header: () => '',
footer: () => '',
notFound: () => 'Oops...Nothing Found 😪 <br>Try another country...',
},
});
12 changes: 12 additions & 0 deletions demo/testable.html
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,18 @@ <h3 class="subtitle">20. Hooks</h3>
</form>
</section>

<section class="section-twentyA top-section">
<form method="GET">
<h3 class="subtitle">20A. updateHits hook</h3>
<p class="details">Fetch within hook and use loading template</p>
<div>
<label for="loading-template-test-20A">Countries</label>
<input id="input-twentyA" name="loading-template-test-20A" type="search" autocomplete="off" placeholder="Search...">
<button type="submit">OK</button>
</div>
</form>
</section>

<br />
</main>
<link rel="stylesheet" href="./../dist/basic.css" />
Expand Down
13 changes: 7 additions & 6 deletions docs/pages/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ <h4><a href="#config?id=hooks" id="hooks" title="Hooks config param - typeahead-
Currently, there is 1 hook available - <code>updateHits</code> which is am async function that receives 1 parameter <b>resultSet</b>
containing 3 properties:
<ul>
<li>- items (the suggestions returned by the search index)</li>
<li>- hits (the suggestions returned by the search index)</li>
<li>- query (the users input query),</li>
<li>- count (the total number of suggestions found in the search index).</li>
</ul>
Expand All @@ -962,14 +962,15 @@ <h4><a href="#config?id=hooks" id="hooks" title="Hooks config param - typeahead-
},
hooks: {
updateHits: (resultSet) => {
resultSet.items.push({name: "new suggestion"}); // add new suggestion
// resultSet.items.filter( ... ); // filter the suggestions
// resultSet.items.sort( ... ); // custom sort the suggestions
resultSet.hits.push({name: "new suggestion"}); // add new suggestion
// resultSet.hits.filter( ... ); // filter the suggestions
// resultSet.hits.sort( ... ); // custom sort the suggestions
// resultSet.count = 5000; // to set the total results found

// you can also make an AJAX request to fetch results
// const response = await fetch('https://example.com', { method: 'GET'});
// const response = await fetch('https://example.com');
// const text = await response.text();
// resultSet.items = text && JSON.parse(text);
// resultSet.hits = text && JSON.parse(text);

return resultSet; // you must return the resultSet
},
Expand Down
3 changes: 2 additions & 1 deletion src/common.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ export interface typeaheadConfig<T extends Dictionary> {
* @returns A promise containing nothing/void or with the ResultSet
*/
updateHits: (
resultSet: Pick<ResultSet<T>, 'hits' | 'query' | 'count'>
resultSet: Pick<ResultSet<T>, 'hits' | 'query' | 'count'>,
loader: (visible: boolean) => void
) => Promise<void> | Promise<Pick<ResultSet<T>, 'hits' | 'count'>>;
};
}
Expand Down
32 changes: 22 additions & 10 deletions src/typeahead-standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,17 +283,24 @@ const typeahead = <T extends Dictionary>(config: typeaheadConfig<T>): typeaheadR
}
};

const loader = () => {
/**
* Display/Hide a loader
* @param visible A boolean to indicate whether the loader template should be rendered or not. Defaults to true
* @returns void
*/
const loader = (visible = true) => {
if (!templates?.loader) {
return;
}

if (!fetchInProgress) {
const loaderEl = listContainer.querySelector(`.${classNames.loader}`);
const loaderEl = listContainer.querySelector(`.${classNames.loader}`);
if (!visible) {
loaderEl && listContainer.removeChild(loaderEl);
return;
}

if (loaderEl) return; // loader is already displayed, do nothing

// display spinner/loader
const loaderDiv = document.createElement('div');
loaderDiv.classList.add(classNames.loader);
Expand All @@ -303,18 +310,23 @@ const typeahead = <T extends Dictionary>(config: typeaheadConfig<T>): typeaheadR
} else {
listContainer.appendChild(loaderDiv);
}

show();
};

/**
* Responsible for drawing/updating the view
*/
const update = async (): Promise<void> => {
// hook to update Hits before displaying results from tree
const results_mod = await hooks.updateHits({
hits: resultSet.hits,
query: resultSet.query,
count: resultSet.count,
});
const results_mod = await hooks.updateHits(
{
hits: resultSet.hits,
query: resultSet.query,
count: resultSet.count,
},
loader
);
if (results_mod?.hits?.length) {
resultSet.hits = results_mod.hits;
resultSet.count = results_mod.count ?? results_mod.hits.length;
Expand Down Expand Up @@ -643,7 +655,7 @@ const typeahead = <T extends Dictionary>(config: typeaheadConfig<T>): typeaheadR
return;
}

loader();
loader(fetchInProgress);

let transformed: T[] = [];

Expand All @@ -667,7 +679,7 @@ const typeahead = <T extends Dictionary>(config: typeaheadConfig<T>): typeaheadR
remoteQueryCache[thumbprint] = true;
remoteResponseCache[thumbprint] = transformed || [];
fetchInProgress = false;
loader();
loader(fetchInProgress);
if (transformed.length && resultSet.query.length) {
calcSuggestions(transformed);
update();
Expand Down

0 comments on commit 0b76c74

Please sign in to comment.