Skip to content

Commit d0ca975

Browse files
authored
docs(instantsearch): keep autocomplete in sync with query during navigation (#1036)
1 parent 006c113 commit d0ca975

File tree

6 files changed

+266
-108
lines changed

6 files changed

+266
-108
lines changed

examples/instantsearch/env.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
import * as preact from 'preact';
2-
31
// Parcel picks the `source` field of the monorepo packages and thus doesn't
42
// apply the Babel config. We therefore need to manually override the constants
53
// in the app, as well as the React pragmas.
64
// See https://twitter.com/devongovett/status/1134231234605830144
75
(global as any).__DEV__ = process.env.NODE_ENV !== 'production';
86
(global as any).__TEST__ = false;
9-
(global as any).h = preact.h;
10-
(global as any).React = preact;

examples/instantsearch/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
"@algolia/autocomplete-plugin-query-suggestions": "1.7.3",
1414
"@algolia/autocomplete-plugin-recent-searches": "1.7.3",
1515
"@algolia/autocomplete-theme-classic": "1.7.3",
16-
"@algolia/client-search": "4.9.1",
17-
"algoliasearch": "4.9.1",
18-
"instantsearch.js": "4.22.0",
19-
"preact": "10.5.13"
16+
"@algolia/client-search": "4.14.2",
17+
"algoliasearch": "4.14.2",
18+
"instantsearch.js": "4.49.0"
2019
},
2120
"devDependencies": {
2221
"parcel": "2.7.0"

examples/instantsearch/src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ import { startAutocomplete } from './autocomplete';
55
import { search } from './instantsearch';
66

77
search.start();
8-
startAutocomplete();
8+
startAutocomplete(search);

examples/instantsearch/src/autocomplete.tsx

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { autocomplete } from '@algolia/autocomplete-js';
44
import { createQuerySuggestionsPlugin } from '@algolia/autocomplete-plugin-query-suggestions';
55
import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches';
6-
import { h, Fragment } from 'preact';
6+
import { InstantSearch } from 'instantsearch.js';
77

88
import {
99
debouncedSetInstantSearchUiState,
@@ -42,29 +42,27 @@ function getItemUrl({ query, category }) {
4242
});
4343
}
4444

45-
function ItemWrapper({ children, query, category }) {
45+
function getItemWrapper({ html, children, query, category }) {
4646
const uiState = {
4747
query,
4848
hierarchicalMenu: {
4949
[INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE]: [category],
5050
},
5151
};
5252

53-
return (
54-
<a
55-
className="aa-ItemLink"
56-
href={getInstantSearchUrl(uiState)}
57-
onClick={(event) => {
58-
if (!isModifierEvent(event)) {
59-
// Bypass the original link behavior if there's no event modifier
60-
// to set the InstantSearch UI state without reloading the page.
61-
event.preventDefault();
62-
}
63-
}}
64-
>
65-
{children}
66-
</a>
67-
);
53+
return html`<a
54+
class="aa-ItemLink"
55+
href=${getInstantSearchUrl(uiState)}
56+
onClick=${(event) => {
57+
if (!isModifierEvent(event)) {
58+
// Bypass the original link behavior if there's no event modifier
59+
// to set the InstantSearch UI state without reloading the page.
60+
event.preventDefault();
61+
}
62+
}}
63+
>
64+
${children}
65+
</a>`;
6866
}
6967

7068
const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
@@ -94,15 +92,14 @@ const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
9492
// and plug it to the InstantSearch router.
9593
item(params) {
9694
const { children } = (source.templates.item(params) as any).props;
95+
const { item, html } = params;
9796

98-
return (
99-
<ItemWrapper
100-
query={params.item.label}
101-
category={params.item.category}
102-
>
103-
{children}
104-
</ItemWrapper>
105-
);
97+
return getItemWrapper({
98+
query: item.label,
99+
category: item.category,
100+
html,
101+
children,
102+
});
106103
},
107104
},
108105
};
@@ -115,7 +112,7 @@ const querySuggestionsPluginInCategory = createQuerySuggestionsPlugin({
115112
getSearchParams() {
116113
const currentCategory = getInstantSearchCurrentCategory();
117114

118-
return recentSearchesPlugin.data.getAlgoliaSearchParams({
115+
return recentSearchesPlugin.data!.getAlgoliaSearchParams({
119116
hitsPerPage: 3,
120117
facetFilters: [
121118
`${INSTANT_SEARCH_INDEX_NAME}.facets.exact_matches.${INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE}.value:${currentCategory}`,
@@ -152,22 +149,22 @@ const querySuggestionsPluginInCategory = createQuerySuggestionsPlugin({
152149
},
153150
templates: {
154151
...source.templates,
155-
header() {
156-
return (
157-
<Fragment>
158-
<span className="aa-SourceHeaderTitle">In {currentCategory}</span>
159-
<div className="aa-SourceHeaderLine" />
160-
</Fragment>
161-
);
152+
header({ html }) {
153+
return html`
154+
<span class="aa-SourceHeaderTitle">In ${currentCategory}</span>
155+
<div class="aa-SourceHeaderLine" />
156+
`;
162157
},
163158
item(params) {
164159
const { children } = (source.templates.item(params) as any).props;
160+
const { item, html } = params;
165161

166-
return (
167-
<ItemWrapper query={params.item.query} category={currentCategory}>
168-
{children}
169-
</ItemWrapper>
170-
);
162+
return getItemWrapper({
163+
query: item.query,
164+
category: currentCategory,
165+
html,
166+
children,
167+
});
171168
},
172169
},
173170
};
@@ -181,12 +178,12 @@ const querySuggestionsPlugin = createQuerySuggestionsPlugin({
181178
const currentCategory = getInstantSearchCurrentCategory();
182179

183180
if (!currentCategory) {
184-
return recentSearchesPlugin.data.getAlgoliaSearchParams({
181+
return recentSearchesPlugin.data!.getAlgoliaSearchParams({
185182
hitsPerPage: 6,
186183
});
187184
}
188185

189-
return recentSearchesPlugin.data.getAlgoliaSearchParams({
186+
return recentSearchesPlugin.data!.getAlgoliaSearchParams({
190187
hitsPerPage: 3,
191188
facetFilters: [
192189
`${INSTANT_SEARCH_INDEX_NAME}.facets.exact_matches.${INSTANT_SEARCH_HIERARCHICAL_ATTRIBUTE}.value:-${currentCategory}`,
@@ -229,29 +226,26 @@ const querySuggestionsPlugin = createQuerySuggestionsPlugin({
229226
},
230227
templates: {
231228
...source.templates,
232-
header() {
229+
header({ html }) {
233230
if (!currentCategory) {
234231
return null;
235232
}
236233

237-
return (
238-
<Fragment>
239-
<span className="aa-SourceHeaderTitle">In other categories</span>
240-
<div className="aa-SourceHeaderLine" />
241-
</Fragment>
242-
);
234+
return html`
235+
<span class="aa-SourceHeaderTitle">In other categories</span>
236+
<div class="aa-SourceHeaderLine" />
237+
`;
243238
},
244239
item(params) {
245240
const { children } = (source.templates.item(params) as any).props;
241+
const { item, html } = params;
246242

247-
return (
248-
<ItemWrapper
249-
query={params.item.query}
250-
category={params.item.__autocomplete_qsCategory}
251-
>
252-
{children}
253-
</ItemWrapper>
254-
);
243+
return getItemWrapper({
244+
query: item.query,
245+
category: item.__autocomplete_qsCategory,
246+
html,
247+
children,
248+
});
255249
},
256250
},
257251
};
@@ -260,8 +254,10 @@ const querySuggestionsPlugin = createQuerySuggestionsPlugin({
260254

261255
const searchPageState = getInstantSearchUiState();
262256

263-
export function startAutocomplete() {
264-
autocomplete({
257+
export function startAutocomplete(searchInstance: InstantSearch) {
258+
let skipInstantSearchStateUpdate = false;
259+
260+
const { setQuery } = autocomplete({
265261
container: '#autocomplete',
266262
placeholder: 'Search for products',
267263
openOnFocus: true,
@@ -292,9 +288,17 @@ export function startAutocomplete() {
292288
});
293289
},
294290
onStateChange({ prevState, state }) {
295-
if (prevState.query !== state.query) {
291+
if (!skipInstantSearchStateUpdate && prevState.query !== state.query) {
296292
debouncedSetInstantSearchUiState({ query: state.query });
297293
}
294+
skipInstantSearchStateUpdate = false;
298295
},
299296
});
297+
298+
window.addEventListener('popstate', () => {
299+
skipInstantSearchStateUpdate = true;
300+
setQuery(
301+
(searchInstance.helper && searchInstance.helper.state.query) || ''
302+
);
303+
});
300304
}

examples/instantsearch/src/instantsearch.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,16 @@ search.addWidgets([
5858
}));
5959
},
6060
templates: {
61-
item: `
61+
item: (hit, { html, components }) => html`
6262
<article class="hit">
6363
<div class="hit-image">
64-
<img src="{{image}}" alt="{{name}}">
64+
<img src="${hit.image}" alt="${hit.name}" />
6565
</div>
6666
<div>
67-
<h1>
68-
{{#helpers.snippet}}{ "attribute": "name" }{{/helpers.snippet}}
69-
</h1>
67+
<h1>${components.Snippet({ hit, attribute: 'name' })}</h1>
7068
<div>
71-
By <strong>{{brand}}</strong> in <strong>{{category}}</strong>
69+
By <strong>${hit.brand}</strong> in
70+
<strong>${hit.category}</strong>
7271
</div>
7372
</div>
7473
@@ -81,7 +80,8 @@ search.addWidgets([
8180
gap: 8px;
8281
"
8382
>
84-
{{#rating}}
83+
${hit.rating > 0 &&
84+
html`
8585
<div
8686
style="
8787
display: grid;
@@ -101,11 +101,13 @@ search.addWidgets([
101101
stroke-linecap="round"
102102
stroke-linejoin="round"
103103
>
104-
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
104+
<polygon
105+
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
106+
/>
105107
</svg>
106-
{{rating}}
108+
${hit.rating}
107109
</div>
108-
{{/rating}}
110+
`}
109111
110112
<div
111113
style="
@@ -130,9 +132,11 @@ search.addWidgets([
130132
top: 1px;
131133
"
132134
>
133-
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
135+
<path
136+
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
137+
></path>
134138
</svg>
135-
<span>{{comments}}</span>
139+
<span>${hit.comments}</span>
136140
</div>
137141
</div>
138142
</article>

0 commit comments

Comments
 (0)