Skip to content

Commit

Permalink
feat(create-instantsearch-app): support Autocomplete in InstantSearch…
Browse files Browse the repository at this point in the history
….js template (#5857)

Co-authored-by: Dhaya <154633+dhayab@users.noreply.github.com>
  • Loading branch information
sarahdayan and dhayab authored Sep 25, 2023
1 parent 771deea commit 4a27e5b
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,60 @@ describe('flags', () => {
).toEqual(expect.objectContaining({ insights: false }));
});
});

describe('autocomplete', () => {
test('with usage of autocomplete in searchInputType', async () => {
expect(
await postProcessAnswers({
configuration: {},
templateConfig: {
libraryName: 'instantsearch.js',
flags: {
autocomplete: '>= 4.52',
},
},
optionsFromArguments: {},
answers: { searchInputType: 'autocomplete' },
})
).toEqual(
expect.objectContaining({
searchInputType: 'autocomplete',
flags: expect.objectContaining({ autocomplete: true }),
})
);
});

test('without usage of autocomplete in searchInputType', async () => {
expect(
await postProcessAnswers({
configuration: {},
templateConfig: {
libraryName: 'instantsearch.js',
flags: {
autocomplete: '>= 4.52',
},
},
optionsFromArguments: {},
answers: { searchInputType: 'searchbox' },
})
).toEqual(
expect.objectContaining({
searchInputType: 'searchbox',
flags: expect.objectContaining({ autocomplete: false }),
})
);
});

test('without config', async () => {
expect(
(
await postProcessAnswers({
configuration: {},
templateConfig: {},
optionsFromArguments: {},
})
).flags
).toEqual(expect.objectContaining({ autocomplete: false }));
});
});
});
85 changes: 85 additions & 0 deletions packages/create-instantsearch-app/src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,91 @@ const getQuestions = ({ appName }) => ({
when: ({ appId, apiKey, indexName }) =>
attributesForFaceting.length === 0 && appId && apiKey && indexName,
},
{
type: 'list',
name: 'searchInputType',
message: 'Type of search input',
choices: [
{
name: 'Autocomplete with suggested and recent searches',
value: 'autocomplete',
},
{ name: 'Regular search box', value: 'searchbox' },
],
default: 'autocomplete',
when: ({ libraryVersion, template }) => {
const templatePath = getTemplatePath(template);
const templateConfig = getAppTemplateConfig(templatePath);

const selectedLibraryVersion = libraryVersion;
const requiredLibraryVersion =
templateConfig.flags && templateConfig.flags.autocomplete;
const supportsAutocomplete =
selectedLibraryVersion &&
requiredLibraryVersion &&
semver.satisfies(selectedLibraryVersion, requiredLibraryVersion, {
includePrerelease: true,
});

return supportsAutocomplete;
},
},
{
type: 'input',
name: 'querySuggestionsIndexName',
message: 'Index name for suggested searches',
suffix: `\n ${chalk.gray('This must be a Query Suggestions index')}`,
default: 'instant_search_demo_query_suggestions',
when: ({ searchInputType }) => searchInputType === 'autocomplete',
},
{
type: 'list',
name: 'autocompleteLibraryVersion',
message: () => `Autocomplete version`,
choices: async () => {
const libraryName = '@algolia/autocomplete-js';

try {
const versions = await fetchLibraryVersions(libraryName);
const latestStableVersion = semver.maxSatisfying(versions, '1', {
includePrerelease: false,
});

if (!latestStableVersion) {
return versions;
}

return [
new inquirer.Separator('Latest stable version (recommended)'),
latestStableVersion,
new inquirer.Separator('All versions'),
...versions,
];
} catch (err) {
const fallbackLibraryVersion = '1.11.0';

console.log();
console.error(
chalk.red(
`Cannot fetch versions for library "${chalk.cyan(libraryName)}".`
)
);
console.log();
console.log(
`Fallback to ${chalk.cyan(
fallbackLibraryVersion
)}, please upgrade the dependency after generating the app.`
);
console.log();

return [
new inquirer.Separator('Available versions'),
fallbackLibraryVersion,
];
}
},
when: ({ searchInputType }) => searchInputType === 'autocomplete',
},
],
widget: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ async function postProcessAnswers({
insights:
Boolean(templateConfig.flags && templateConfig.flags.insights) &&
semver.satisfies(libraryVersion, templateConfig.flags.insights),
autocomplete:
Boolean(templateConfig.flags && templateConfig.flags.autocomplete) &&
combinedAnswers.searchInputType === 'autocomplete',
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
flags: {
dynamicWidgets: '>= 4.30',
insights: '>= 4.55',
autocomplete: '>= 4.52',
},
templateName: 'instantsearch.js',
appName: 'instantsearch.js-app',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<link rel="shortcut icon" href="./favicon.png">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@7/themes/satellite-min.css">
{{#if flags.autocomplete}}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-theme-classic@1.11.0/dist/theme.min.css">
{{/if}}
<link rel="stylesheet" href="./src/index.css">
<link rel="stylesheet" href="./src/app.css">

Expand Down Expand Up @@ -50,6 +53,11 @@

<script src="https://cdn.jsdelivr.net/npm/algoliasearch@4.10.5/dist/algoliasearch-lite.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@{{libraryVersion}}"></script>
{{#if flags.autocomplete}}
<script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-js@{{autocompleteLibraryVersion}}/dist/umd/index.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-plugin-query-suggestions@{{autocompleteLibraryVersion}}/dist/umd/index.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@algolia/autocomplete-plugin-recent-searches@{{autocompleteLibraryVersion}}/dist/umd/index.production.min.js"></script>
{{/if}}
<script src="./src/app.js"></script>
</body>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"prettier": "2.6.2"
},
"dependencies": {
{{#if flags.autocomplete}}
"@algolia/autocomplete-js": "{{autocompleteLibraryVersion}}",
"@algolia/autocomplete-plugin-recent-searches": "{{autocompleteLibraryVersion}}",
"@algolia/autocomplete-plugin-query-suggestions": "{{autocompleteLibraryVersion}}",
{{/if}}
"algoliasearch": "4",
"instantsearch.js": "{{libraryVersion}}"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
const { algoliasearch, instantsearch } = window;
{{#if flags.autocomplete}}
const { autocomplete } = window['@algolia/autocomplete-js'];
const { createLocalStorageRecentSearchesPlugin } = window[
'@algolia/autocomplete-plugin-recent-searches'
];
const { createQuerySuggestionsPlugin } = window[
'@algolia/autocomplete-plugin-query-suggestions'
];
{{/if}}

const searchClient = algoliasearch('{{appId}}', '{{apiKey}}');

Expand All @@ -8,13 +17,21 @@ const search = instantsearch({
{{#if flags.insights}}insights: true,{{/if}}
});

{{#if flags.autocomplete}}
const virtualSearchBox = instantsearch.connectors.connectSearchBox(() => {});
{{/if}}

search.addWidgets([
{{#unless flags.autocomplete}}
instantsearch.widgets.searchBox({
container: '#searchbox',
{{#if searchPlaceholder}}
placeholder: '{{searchPlaceholder}}',
{{/if}}
}),
{{else}}
virtualSearchBox({}),
{{/unless}}
instantsearch.widgets.hits({
container: '#hits',
{{#if attributesToDisplay}}
Expand Down Expand Up @@ -74,3 +91,81 @@ search.addWidgets([
]);

search.start();

{{#if flags.autocomplete}}
const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
key: 'instantsearch',
limit: 3,
transformSource({ source }) {
return {
...source,
onSelect({ setIsOpen, setQuery, item, event }) {
onSelect({ setQuery, setIsOpen, event, query: item.label });
},
};
},
});

const querySuggestionsPlugin = createQuerySuggestionsPlugin({
searchClient,
indexName: '{{querySuggestionsIndexName}}',
getSearchParams() {
return recentSearchesPlugin.data.getAlgoliaSearchParams({ hitsPerPage: 6 });
},
transformSource({ source }) {
return {
...source,
sourceId: 'querySuggestionsPlugin',
onSelect({ setIsOpen, setQuery, event, item }) {
onSelect({ setQuery, setIsOpen, event, query: item.query });
},
getItems(params) {
if (!params.state.query) {
return [];
}

return source.getItems(params);
},
};
},
});

autocomplete({
container: '#searchbox',
{{#if searchPlaceholder}}
placeholder: '{{searchPlaceholder}}',
{{/if}}
openOnFocus: true,
detachedMediaQuery: 'none',
onSubmit({ state }) {
setInstantSearchUiState({ query: state.query });
},
plugins: [recentSearchesPlugin, querySuggestionsPlugin],
});

function setInstantSearchUiState(indexUiState) {
search.mainIndex.setIndexUiState({ page: 1, ...indexUiState });
}

function onSelect({ setIsOpen, setQuery, event, query }) {
if (isModifierEvent(event)) {
return;
}

setQuery(query);
setIsOpen(false);
setInstantSearchUiState({ query });
}

function isModifierEvent(event) {
const isMiddleClick = event.button === 1;

return (
isMiddleClick ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
);
}
{{/if}}
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
{{#if flags.autocomplete}}
import { autocomplete } from '@algolia/autocomplete-js';
import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches';
import { createQuerySuggestionsPlugin } from '@algolia/autocomplete-plugin-query-suggestions';
{{/if}}
import algoliasearch from 'algoliasearch/lite';
import instantsearch from 'instantsearch.js/dist/instantsearch.production.min';

declare global {
interface Window {
algoliasearch: typeof algoliasearch;
instantsearch: typeof instantsearch;
{{#if flags.autocomplete}}
'@algolia/autocomplete-js': {
autocomplete: typeof autocomplete;
};
'@algolia/autocomplete-plugin-recent-searches': {
createLocalStorageRecentSearchesPlugin: typeof createLocalStorageRecentSearchesPlugin;
};
'@algolia/autocomplete-plugin-query-suggestions': {
createQuerySuggestionsPlugin: typeof createQuerySuggestionsPlugin;
};
{{/if}}
}
}

0 comments on commit 4a27e5b

Please sign in to comment.