From 09e04c5c3a8d0babdc3dbccbef2ca1417719e156 Mon Sep 17 00:00:00 2001 From: shortcuts Date: Thu, 4 Feb 2021 13:27:14 +0100 Subject: [PATCH 1/7] Add required option `sourceId` to `AutocompleteSources` in `core`, add `data-autocomplete-source-id` on `section` tags, update `getSources` related tests --- examples/js/app.tsx | 3 ++ examples/js/shortcutsPlugin.tsx | 1 + .../src/__tests__/concurrency.test.ts | 1 + .../src/__tests__/getInputProps.test.ts | 1 + .../src/__tests__/getSources.test.ts | 4 ++ .../src/__tests__/stallThreshold.test.ts | 2 + .../src/types/AutocompleteSource.ts | 4 ++ .../__tests__/getNormalizedSources.test.ts | 43 ++++++++++++++++--- .../src/utils/getNormalizedSources.ts | 7 +++ .../src/__tests__/autocomplete.test.ts | 11 +++++ .../src/__tests__/positioning.test.ts | 1 + packages/autocomplete-js/src/render.tsx | 6 ++- .../src/types/AutocompleteSource.ts | 1 + .../src/createQuerySuggestionsPlugin.ts | 3 ++ .../createLocalStorageRecentSearchesPlugin.ts | 7 +++ .../src/createRecentSearchesPlugin.ts | 3 ++ test/utils/createSource.ts | 1 + 17 files changed, 92 insertions(+), 7 deletions(-) diff --git a/examples/js/app.tsx b/examples/js/app.tsx index 0faeda914..417395bfe 100644 --- a/examples/js/app.tsx +++ b/examples/js/app.tsx @@ -30,11 +30,13 @@ insightsClient('init', { appId, apiKey }); const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({ insightsClient }); const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({ + sourceId: 'recentSearchesPlugin', key: 'search', limit: 3, }); const querySuggestionsPlugin = createQuerySuggestionsPlugin({ searchClient, + sourceId: 'querySuggestionsPlugin', indexName: 'instant_search_demo_query_suggestions', getSearchParams({ state }) { return recentSearchesPlugin.data.getAlgoliaSearchParams({ @@ -62,6 +64,7 @@ autocomplete({ return [ { + sourceId: 'algoliaHits', getItems() { return getAlgoliaHits({ searchClient, diff --git a/examples/js/shortcutsPlugin.tsx b/examples/js/shortcutsPlugin.tsx index 76b104245..42f8b5732 100644 --- a/examples/js/shortcutsPlugin.tsx +++ b/examples/js/shortcutsPlugin.tsx @@ -14,6 +14,7 @@ export const shortcutsPlugin: AutocompletePlugin = { return [ { + sourceId: 'shortcutsPlugin', getItems() { return [ { diff --git a/packages/autocomplete-core/src/__tests__/concurrency.test.ts b/packages/autocomplete-core/src/__tests__/concurrency.test.ts index 1a02933dc..75e3a4ce3 100644 --- a/packages/autocomplete-core/src/__tests__/concurrency.test.ts +++ b/packages/autocomplete-core/src/__tests__/concurrency.test.ts @@ -15,6 +15,7 @@ describe.skip('concurrency', () => { return defer(() => { return [ { + sourceId: 'testSource', getItems() { return [{ label: query }]; }, diff --git a/packages/autocomplete-core/src/__tests__/getInputProps.test.ts b/packages/autocomplete-core/src/__tests__/getInputProps.test.ts index 648747b46..c3a4530a3 100644 --- a/packages/autocomplete-core/src/__tests__/getInputProps.test.ts +++ b/packages/autocomplete-core/src/__tests__/getInputProps.test.ts @@ -1049,6 +1049,7 @@ describe('getInputProps', () => { { label: '2', url: '#2' }, ], source: { + sourceId: 'testSource', getItemInputValue: expect.any(Function), getItemUrl: expect.any(Function), getItems: expect.any(Function), diff --git a/packages/autocomplete-core/src/__tests__/getSources.test.ts b/packages/autocomplete-core/src/__tests__/getSources.test.ts index b869b8009..f6243ff7c 100644 --- a/packages/autocomplete-core/src/__tests__/getSources.test.ts +++ b/packages/autocomplete-core/src/__tests__/getSources.test.ts @@ -47,6 +47,7 @@ describe('getSources', () => { getSources: () => { return [ { + sourceId: 'testSource', getItems() { return []; }, @@ -78,6 +79,7 @@ describe('getSources', () => { templates: expect.objectContaining({ item: expect.any(Function), }), + sourceId: expect.any(String), }, }), ]), @@ -92,6 +94,7 @@ describe('getSources', () => { getSources: () => { return [ { + sourceId: 'pluginSource', getItems() { return []; }, @@ -107,6 +110,7 @@ describe('getSources', () => { getSources: () => { return [ { + sourceId: 'testSource', getItems() { return []; }, diff --git a/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts b/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts index c2e91b470..dece1bed8 100644 --- a/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts +++ b/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts @@ -12,6 +12,7 @@ describe('stallThreshold', () => { return defer(() => { return [ { + sourceId: 'testSource', getItems() { return [{ label: '1' }, { label: 2 }]; }, @@ -60,6 +61,7 @@ describe('stallThreshold', () => { return defer(() => { return [ { + sourceId: 'testSource', getItems() { return [{ label: '1' }, { label: 2 }]; }, diff --git a/packages/autocomplete-core/src/types/AutocompleteSource.ts b/packages/autocomplete-core/src/types/AutocompleteSource.ts index 27f11d0f7..726422520 100644 --- a/packages/autocomplete-core/src/types/AutocompleteSource.ts +++ b/packages/autocomplete-core/src/types/AutocompleteSource.ts @@ -58,6 +58,10 @@ export interface AutocompleteSource { * You can trigger different behaviors based on the event `type`. */ onActive?(params: OnHighlightParams): void; + /** + * Applied to data-autocomplete-source-id on the section source container + */ + sourceId: string; } export type InternalAutocompleteSource = { diff --git a/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts b/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts index d5d1a0ede..7f81728d7 100644 --- a/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts +++ b/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts @@ -7,7 +7,7 @@ import { getNormalizedSources } from '../getNormalizedSources'; describe('getNormalizedSources', () => { test('returns a promise of sources', async () => { - const getSources = () => [{ getItems: () => [] }]; + const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }]; const params = { query: '', state: createState({ @@ -23,12 +23,17 @@ describe('getNormalizedSources', () => { getItems: expect.any(Function), onActive: expect.any(Function), onSelect: expect.any(Function), + sourceId: 'testSource', }, ]); }); test('filters out falsy sources', async () => { - const getSources = () => [{ getItems: () => [] }, false, undefined]; + const getSources = () => [ + { sourceId: 'testSource', getItems: () => [] }, + false, + undefined, + ]; const params = { query: '', state: createState({ @@ -44,6 +49,7 @@ describe('getNormalizedSources', () => { getItems: expect.any(Function), onActive: expect.any(Function), onSelect: expect.any(Function), + sourceId: 'testSource', }, ]); }); @@ -64,8 +70,33 @@ describe('getNormalizedSources', () => { ); }); + test('with with missing sourceId triggers invariant', async () => { + const getSources = () => [ + { + getItems() { + return []; + }, + templates: { + item() {}, + }, + }, + ]; + const params = { + query: '', + state: createState({}), + ...createScopeApi(), + }; + + // @ts-expect-error + await expect(getNormalizedSources(getSources, params)).rejects.toEqual( + new Error( + '[Autocomplete] The `getSources` function must return a `sourceId` string but returned type "undefined":\n\nundefined' + ) + ); + }); + test('provides a default implementation for getItemInputValue which returns the query', async () => { - const getSources = () => [{ getItems: () => [] }]; + const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }]; const params = { query: '', state: createState({ @@ -82,7 +113,7 @@ describe('getNormalizedSources', () => { }); test('provides a default implementation for getItemUrl', async () => { - const getSources = () => [{ getItems: () => [] }]; + const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }]; const params = { query: '', state: createState({}), @@ -97,7 +128,7 @@ describe('getNormalizedSources', () => { }); test('provides a default implementation for onSelect', async () => { - const getSources = () => [{ getItems: () => [] }]; + const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }]; const params = { query: '', state: createState({}), @@ -119,7 +150,7 @@ describe('getNormalizedSources', () => { }); test('provides a default implementation for onActive', async () => { - const getSources = () => [{ getItems: () => [] }]; + const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }]; const params = { query: '', state: createState({}), diff --git a/packages/autocomplete-core/src/utils/getNormalizedSources.ts b/packages/autocomplete-core/src/utils/getNormalizedSources.ts index 480f6eacb..3b22078c0 100644 --- a/packages/autocomplete-core/src/utils/getNormalizedSources.ts +++ b/packages/autocomplete-core/src/utils/getNormalizedSources.ts @@ -32,6 +32,13 @@ export function getNormalizedSources( Boolean(maybeSource) ) .map((source) => { + invariant( + source.sourceId !== undefined, + `The \`getSources\` function must return a \`sourceId\` string but returned type ${JSON.stringify( + typeof source.sourceId + )}:\n\n${JSON.stringify(source.sourceId, null, 2)}` + ); + const normalizedSource: InternalAutocompleteSource = { getItemInputValue({ state }) { return state.query; diff --git a/packages/autocomplete-js/src/__tests__/autocomplete.test.ts b/packages/autocomplete-js/src/__tests__/autocomplete.test.ts index 23035ae68..5bd5c03ef 100644 --- a/packages/autocomplete-js/src/__tests__/autocomplete.test.ts +++ b/packages/autocomplete-js/src/__tests__/autocomplete.test.ts @@ -11,6 +11,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return [ { label: 'Item 1' }, @@ -167,6 +168,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return []; }, @@ -206,6 +208,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return []; }, @@ -245,6 +248,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return []; }, @@ -290,6 +294,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return []; }, @@ -338,6 +343,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return []; }, @@ -383,6 +389,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return [ { label: 'Item 1' }, @@ -418,6 +425,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return [ { label: 'Item 1' }, @@ -449,6 +457,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return [ { label: 'Item 1' }, @@ -482,6 +491,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return [ { label: 'Item 1' }, @@ -512,6 +522,7 @@ describe('autocomplete-js', () => { getSources() { return [ { + sourceId: 'testSource', getItems() { return [ { label: 'Item 1' }, diff --git a/packages/autocomplete-js/src/__tests__/positioning.test.ts b/packages/autocomplete-js/src/__tests__/positioning.test.ts index 34b5f3e95..ee1540a26 100644 --- a/packages/autocomplete-js/src/__tests__/positioning.test.ts +++ b/packages/autocomplete-js/src/__tests__/positioning.test.ts @@ -35,6 +35,7 @@ const querySuggestionsFixturePlugin: AutocompletePlugin< getSources() { return [ { + sourceId: 'testSource', getItems() { return querySuggestions; }, diff --git a/packages/autocomplete-js/src/render.tsx b/packages/autocomplete-js/src/render.tsx index bcd588b6f..7eb5d13c9 100644 --- a/packages/autocomplete-js/src/render.tsx +++ b/packages/autocomplete-js/src/render.tsx @@ -92,7 +92,11 @@ export function renderPanel( dom.panel.classList.toggle('aa-Panel--stalled', state.status === 'stalled'); const sections = state.collections.map(({ source, items }, sourceIndex) => ( -
+
{source.templates.header && (
{source.templates.header({ diff --git a/packages/autocomplete-js/src/types/AutocompleteSource.ts b/packages/autocomplete-js/src/types/AutocompleteSource.ts index 943e12d3b..55e14645e 100644 --- a/packages/autocomplete-js/src/types/AutocompleteSource.ts +++ b/packages/autocomplete-js/src/types/AutocompleteSource.ts @@ -57,6 +57,7 @@ export type AutocompleteSource = WithTemplates< AutocompleteCoreSource, TItem >; + export type InternalAutocompleteSource = WithTemplates< InternalAutocompleteCoreSource, TItem diff --git a/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts b/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts index 11d0cef3f..5f172bd3c 100644 --- a/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts +++ b/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts @@ -17,6 +17,7 @@ export type CreateQuerySuggestionsPluginParams< > = { searchClient: SearchClient; indexName: string; + sourceId: string; getSearchParams?(params: { state: AutocompleteState }): SearchOptions; getTemplates?(params: GetTemplatesParams): SourceTemplates; }; @@ -26,6 +27,7 @@ export function createQuerySuggestionsPlugin< >({ searchClient, indexName, + sourceId, getSearchParams = () => ({}), getTemplates = defaultGetTemplates, }: CreateQuerySuggestionsPluginParams): AutocompletePlugin< @@ -36,6 +38,7 @@ export function createQuerySuggestionsPlugin< getSources({ query, setQuery, refresh, state }) { return [ { + sourceId, getItemInputValue({ item }) { return item.query; }, diff --git a/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts b/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts index 21632dd74..5de9edb28 100644 --- a/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts +++ b/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts @@ -34,6 +34,10 @@ export type CreateRecentSearchesLocalStorageOptions< * Function to search in the recent items. */ search?(params: SearchParams): Array>; + /** + * Applied to data-autocomplete-source-id on the section source container + */ + sourceId: string; }; type LocalStorageRecentSearchesPluginOptions< @@ -48,6 +52,7 @@ export function createLocalStorageRecentSearchesPlugin< limit = 5, getTemplates, search = defaultSearch, + sourceId, }: LocalStorageRecentSearchesPluginOptions): AutocompletePlugin< TItem, RecentSearchesPluginData @@ -56,10 +61,12 @@ export function createLocalStorageRecentSearchesPlugin< key: [LOCAL_STORAGE_KEY, key].join(':'), limit, search, + sourceId, }); return createRecentSearchesPlugin({ getTemplates, storage, + sourceId, }); } diff --git a/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts b/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts index 44fda124c..9312366bf 100644 --- a/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts +++ b/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts @@ -21,12 +21,14 @@ export type RecentSearchesPluginData = { export type CreateRecentSearchesPluginParams< TItem extends RecentSearchesItem > = { + sourceId: string; storage: RecentSearchesStorage; getTemplates?(params: GetTemplatesParams): SourceTemplates; }; export function createRecentSearchesPlugin({ storage, + sourceId, getTemplates = defaultGetTemplates, }: CreateRecentSearchesPluginParams): AutocompletePlugin< TItem, @@ -68,6 +70,7 @@ export function createRecentSearchesPlugin({ return [ { + sourceId, getItemInputValue({ item }) { return item.query; }, diff --git a/test/utils/createSource.ts b/test/utils/createSource.ts index 7c92f9411..537611309 100644 --- a/test/utils/createSource.ts +++ b/test/utils/createSource.ts @@ -8,6 +8,7 @@ export function createSource( source?: Partial> ): InternalAutocompleteSource { return { + sourceId: 'testSource', getItemInputValue: ({ state }) => state.query, getItemUrl: () => undefined, onActive: () => {}, From a64dce5712b14a8a59a63d12f49d019019d14200 Mon Sep 17 00:00:00 2001 From: shortcuts Date: Thu, 4 Feb 2021 13:47:17 +0100 Subject: [PATCH 2/7] Update docs --- packages/website/docs/autocomplete-js.md | 1 + packages/website/docs/createAutocomplete.md | 1 + packages/website/docs/creating-a-renderer.md | 1 + packages/website/docs/keyboard-navigation.md | 1 + packages/website/docs/sources.md | 14 ++++++++++++++ 5 files changed, 18 insertions(+) diff --git a/packages/website/docs/autocomplete-js.md b/packages/website/docs/autocomplete-js.md index f6ee5f2bb..234cb3dc1 100644 --- a/packages/website/docs/autocomplete-js.md +++ b/packages/website/docs/autocomplete-js.md @@ -29,6 +29,7 @@ const autocompleteSearch = autocomplete({ getSources() { return [ { + sourceId: 'querySuggestionsSources', getItemInputValue: ({ item }) => item.query, getItems({ query }) { return getAlgoliaHits({ diff --git a/packages/website/docs/createAutocomplete.md b/packages/website/docs/createAutocomplete.md index ab815b647..bffbe9f85 100644 --- a/packages/website/docs/createAutocomplete.md +++ b/packages/website/docs/createAutocomplete.md @@ -22,6 +22,7 @@ const autocomplete = createAutocomplete({ getSources() { return [ { + sourceId: 'querySuggestionsSource', getItemInputValue: ({ item }) => item.query, getItems({ query }) { return getAlgoliaHits({ diff --git a/packages/website/docs/creating-a-renderer.md b/packages/website/docs/creating-a-renderer.md index 8712f4e60..f23505a36 100644 --- a/packages/website/docs/creating-a-renderer.md +++ b/packages/website/docs/creating-a-renderer.md @@ -44,6 +44,7 @@ function Autocomplete() { return [ // (3) Use an Algolia index source. { + sourceId: 'querySuggestionsSource', getItemInputValue({ item }) { return item.query; }, diff --git a/packages/website/docs/keyboard-navigation.md b/packages/website/docs/keyboard-navigation.md index de1f74370..da474c06c 100644 --- a/packages/website/docs/keyboard-navigation.md +++ b/packages/website/docs/keyboard-navigation.md @@ -24,6 +24,7 @@ const autocomplete = createAutocomplete({ getSources() { return [ { + sourceId: 'mySource', getItemUrl({ item }) { return item.url; }, diff --git a/packages/website/docs/sources.md b/packages/website/docs/sources.md index fd0ab0038..c33352fbb 100644 --- a/packages/website/docs/sources.md +++ b/packages/website/docs/sources.md @@ -16,6 +16,7 @@ const autocomplete = createAutocomplete({ getSources() { return [ { + sourceId: 'staticSource', getItems() { return [ { label: 'Twitter', url: 'https://twitter.com' }, @@ -40,6 +41,7 @@ const autocomplete = createAutocomplete({ getSources() { return [ { + sourceId: 'staticSource', getItems({ query }) { return [ { label: 'Twitter', url: 'https://twitter.com' }, @@ -71,6 +73,7 @@ const autocomplete = createAutocomplete({ getSources() { return [ { + sourceId: 'algoliaHits', getItems({ query }) { return getAlgoliaHits({ searchClient, @@ -112,6 +115,7 @@ const autocomplete = createAutocomplete({ if (!query) { [ { + sourceId: 'staticSource', getItems() { return [ { label: 'Twitter', url: 'https://twitter.com' }, @@ -127,6 +131,7 @@ const autocomplete = createAutocomplete({ return [ { + sourceId: 'algoliaHits', getItems() { return getAlgoliaHits({ searchClient, @@ -184,12 +189,14 @@ const autocomplete = createAutocomplete({ return [ { + sourceId: 'querySuggestionsSource', getItems() { return querySuggestions.hits; }, getItemInputValue: ({ item }) => item.query, }, { + sourceId: 'algoliaHits', getItems() { return products.hits; }, @@ -283,6 +290,12 @@ Called when an item is active. You can trigger different behaviors if the item is active following a mouse event or a keyboard event based on the `event` param. +### `sourceId` + +> `string` + +Applied to `data-autocomplete-source-id` on the `section` source container + ### `templates` (specific to `@algolia/autocomplete-js`) > `SourceTemplate` @@ -336,6 +349,7 @@ const autocompleteSearch = autocomplete({ getSources() { return [ { + sourceId: 'querySuggestionsSource', getItemInputValue({ item }) { return item.query; }, From be7a689610c6c97f3ac214e1e6b960bba8c56482 Mon Sep 17 00:00:00 2001 From: shortcuts Date: Thu, 4 Feb 2021 13:55:30 +0100 Subject: [PATCH 3/7] Fix test description --- .../src/utils/__tests__/getNormalizedSources.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts b/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts index 7f81728d7..b6e79e35c 100644 --- a/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts +++ b/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts @@ -70,7 +70,7 @@ describe('getNormalizedSources', () => { ); }); - test('with with missing sourceId triggers invariant', async () => { + test('with missing sourceId triggers invariant', async () => { const getSources = () => [ { getItems() { From dcc7d84398bcb65ce000c55ffa5d3b04a2d89797 Mon Sep 17 00:00:00 2001 From: shortcuts Date: Thu, 4 Feb 2021 15:44:08 +0100 Subject: [PATCH 4/7] Fix tests, default `sourceId` on plugins --- examples/js/app.tsx | 4 +-- .../src/__tests__/concurrency.test.ts | 7 ++--- .../src/__tests__/getInputProps.test.ts | 2 +- .../src/__tests__/stallThreshold.test.ts | 12 ++++---- .../src/types/AutocompleteSource.ts | 2 +- .../__tests__/getNormalizedSources.test.ts | 30 ++++++++++++++++--- .../src/utils/getNormalizedSources.ts | 6 ++-- .../src/createQuerySuggestionsPlugin.ts | 4 +-- .../createLocalStorageRecentSearchesPlugin.ts | 7 ----- .../src/createRecentSearchesPlugin.ts | 4 +-- 10 files changed, 41 insertions(+), 37 deletions(-) diff --git a/examples/js/app.tsx b/examples/js/app.tsx index 417395bfe..a03594667 100644 --- a/examples/js/app.tsx +++ b/examples/js/app.tsx @@ -30,13 +30,11 @@ insightsClient('init', { appId, apiKey }); const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({ insightsClient }); const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({ - sourceId: 'recentSearchesPlugin', key: 'search', limit: 3, }); const querySuggestionsPlugin = createQuerySuggestionsPlugin({ searchClient, - sourceId: 'querySuggestionsPlugin', indexName: 'instant_search_demo_query_suggestions', getSearchParams({ state }) { return recentSearchesPlugin.data.getAlgoliaSearchParams({ @@ -64,7 +62,7 @@ autocomplete({ return [ { - sourceId: 'algoliaHits', + sourceId: 'products', getItems() { return getAlgoliaHits({ searchClient, diff --git a/packages/autocomplete-core/src/__tests__/concurrency.test.ts b/packages/autocomplete-core/src/__tests__/concurrency.test.ts index 75e3a4ce3..9921cb1cd 100644 --- a/packages/autocomplete-core/src/__tests__/concurrency.test.ts +++ b/packages/autocomplete-core/src/__tests__/concurrency.test.ts @@ -1,6 +1,6 @@ import userEvent from '@testing-library/user-event'; -import { defer } from '../../../../test/utils'; +import { createSource, defer } from '../../../../test/utils'; import { createAutocomplete } from '../createAutocomplete'; describe.skip('concurrency', () => { @@ -14,12 +14,11 @@ describe.skip('concurrency', () => { return defer(() => { return [ - { - sourceId: 'testSource', + createSource({ getItems() { return [{ label: query }]; }, - }, + }), ]; }, delays[deferCount]); }; diff --git a/packages/autocomplete-core/src/__tests__/getInputProps.test.ts b/packages/autocomplete-core/src/__tests__/getInputProps.test.ts index c3a4530a3..8a56a473d 100644 --- a/packages/autocomplete-core/src/__tests__/getInputProps.test.ts +++ b/packages/autocomplete-core/src/__tests__/getInputProps.test.ts @@ -1049,7 +1049,7 @@ describe('getInputProps', () => { { label: '2', url: '#2' }, ], source: { - sourceId: 'testSource', + sourceId: expect.any(String), getItemInputValue: expect.any(Function), getItemUrl: expect.any(Function), getItems: expect.any(Function), diff --git a/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts b/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts index dece1bed8..5c1ab4bf8 100644 --- a/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts +++ b/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts @@ -1,6 +1,6 @@ import userEvent from '@testing-library/user-event'; -import { defer } from '../../../../test/utils'; +import { createSource, defer } from '../../../../test/utils'; import { createAutocomplete } from '../createAutocomplete'; describe('stallThreshold', () => { @@ -11,12 +11,11 @@ describe('stallThreshold', () => { getSources() { return defer(() => { return [ - { - sourceId: 'testSource', + createSource({ getItems() { return [{ label: '1' }, { label: 2 }]; }, - }, + }), ]; }, 500); }, @@ -60,12 +59,11 @@ describe('stallThreshold', () => { getSources() { return defer(() => { return [ - { - sourceId: 'testSource', + createSource({ getItems() { return [{ label: '1' }, { label: 2 }]; }, - }, + }), ]; }, 500); }, diff --git a/packages/autocomplete-core/src/types/AutocompleteSource.ts b/packages/autocomplete-core/src/types/AutocompleteSource.ts index 726422520..ca562c0ca 100644 --- a/packages/autocomplete-core/src/types/AutocompleteSource.ts +++ b/packages/autocomplete-core/src/types/AutocompleteSource.ts @@ -59,7 +59,7 @@ export interface AutocompleteSource { */ onActive?(params: OnHighlightParams): void; /** - * Applied to data-autocomplete-source-id on the section source container + * Identifier for the source. */ sourceId: string; } diff --git a/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts b/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts index b6e79e35c..c4cf54f5f 100644 --- a/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts +++ b/packages/autocomplete-core/src/utils/__tests__/getNormalizedSources.test.ts @@ -70,7 +70,7 @@ describe('getNormalizedSources', () => { ); }); - test('with missing sourceId triggers invariant', async () => { + test('with missing `sourceId` triggers invariant', async () => { const getSources = () => [ { getItems() { @@ -89,9 +89,31 @@ describe('getNormalizedSources', () => { // @ts-expect-error await expect(getNormalizedSources(getSources, params)).rejects.toEqual( - new Error( - '[Autocomplete] The `getSources` function must return a `sourceId` string but returned type "undefined":\n\nundefined' - ) + new Error('[Autocomplete] A source must provide a `sourceId` string.') + ); + }); + + test('with wrong `sourceId` type triggers invariant', async () => { + const getSources = () => [ + { + sourceId: ['testSource'], + getItems() { + return []; + }, + templates: { + item() {}, + }, + }, + ]; + const params = { + query: '', + state: createState({}), + ...createScopeApi(), + }; + + // @ts-expect-error + await expect(getNormalizedSources(getSources, params)).rejects.toEqual( + new Error('[Autocomplete] A source must provide a `sourceId` string.') ); }); diff --git a/packages/autocomplete-core/src/utils/getNormalizedSources.ts b/packages/autocomplete-core/src/utils/getNormalizedSources.ts index 3b22078c0..fb560c50b 100644 --- a/packages/autocomplete-core/src/utils/getNormalizedSources.ts +++ b/packages/autocomplete-core/src/utils/getNormalizedSources.ts @@ -33,10 +33,8 @@ export function getNormalizedSources( ) .map((source) => { invariant( - source.sourceId !== undefined, - `The \`getSources\` function must return a \`sourceId\` string but returned type ${JSON.stringify( - typeof source.sourceId - )}:\n\n${JSON.stringify(source.sourceId, null, 2)}` + typeof source.sourceId === 'string', + 'A source must provide a `sourceId` string.' ); const normalizedSource: InternalAutocompleteSource = { diff --git a/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts b/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts index 5f172bd3c..517baa1a8 100644 --- a/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts +++ b/packages/autocomplete-plugin-query-suggestions/src/createQuerySuggestionsPlugin.ts @@ -17,7 +17,6 @@ export type CreateQuerySuggestionsPluginParams< > = { searchClient: SearchClient; indexName: string; - sourceId: string; getSearchParams?(params: { state: AutocompleteState }): SearchOptions; getTemplates?(params: GetTemplatesParams): SourceTemplates; }; @@ -27,7 +26,6 @@ export function createQuerySuggestionsPlugin< >({ searchClient, indexName, - sourceId, getSearchParams = () => ({}), getTemplates = defaultGetTemplates, }: CreateQuerySuggestionsPluginParams): AutocompletePlugin< @@ -38,7 +36,7 @@ export function createQuerySuggestionsPlugin< getSources({ query, setQuery, refresh, state }) { return [ { - sourceId, + sourceId: 'querySuggestionsPlugin', getItemInputValue({ item }) { return item.query; }, diff --git a/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts b/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts index 5de9edb28..21632dd74 100644 --- a/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts +++ b/packages/autocomplete-plugin-recent-searches/src/createLocalStorageRecentSearchesPlugin.ts @@ -34,10 +34,6 @@ export type CreateRecentSearchesLocalStorageOptions< * Function to search in the recent items. */ search?(params: SearchParams): Array>; - /** - * Applied to data-autocomplete-source-id on the section source container - */ - sourceId: string; }; type LocalStorageRecentSearchesPluginOptions< @@ -52,7 +48,6 @@ export function createLocalStorageRecentSearchesPlugin< limit = 5, getTemplates, search = defaultSearch, - sourceId, }: LocalStorageRecentSearchesPluginOptions): AutocompletePlugin< TItem, RecentSearchesPluginData @@ -61,12 +56,10 @@ export function createLocalStorageRecentSearchesPlugin< key: [LOCAL_STORAGE_KEY, key].join(':'), limit, search, - sourceId, }); return createRecentSearchesPlugin({ getTemplates, storage, - sourceId, }); } diff --git a/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts b/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts index 9312366bf..666e1b57f 100644 --- a/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts +++ b/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts @@ -21,14 +21,12 @@ export type RecentSearchesPluginData = { export type CreateRecentSearchesPluginParams< TItem extends RecentSearchesItem > = { - sourceId: string; storage: RecentSearchesStorage; getTemplates?(params: GetTemplatesParams): SourceTemplates; }; export function createRecentSearchesPlugin({ storage, - sourceId, getTemplates = defaultGetTemplates, }: CreateRecentSearchesPluginParams): AutocompletePlugin< TItem, @@ -70,7 +68,7 @@ export function createRecentSearchesPlugin({ return [ { - sourceId, + sourceId: 'recentSearchesPlugin', getItemInputValue({ item }) { return item.query; }, From 60f61c2e6b04e0282ab9da915419f17177b6ac07 Mon Sep 17 00:00:00 2001 From: shortcuts Date: Fri, 5 Feb 2021 11:36:44 +0100 Subject: [PATCH 5/7] add reference to `sourceId` and `data-autocomplete-source-id` in autocomplete-js --- .../autocomplete-js/src/types/AutocompleteSource.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/autocomplete-js/src/types/AutocompleteSource.ts b/packages/autocomplete-js/src/types/AutocompleteSource.ts index 55e14645e..d0ee2d232 100644 --- a/packages/autocomplete-js/src/types/AutocompleteSource.ts +++ b/packages/autocomplete-js/src/types/AutocompleteSource.ts @@ -53,8 +53,17 @@ type WithTemplates = TType & { templates: SourceTemplates; }; +export interface AutocompleteCoreSourceWithDocs + extends AutocompleteCoreSource { + /** + * Identifier for the source. + * It is used as value for the `data-autocomplete-source-id` attribute of the source `section` container. + */ + sourceId: string; +} + export type AutocompleteSource = WithTemplates< - AutocompleteCoreSource, + AutocompleteCoreSourceWithDocs, TItem >; From 8c94c0524baabe95062b4c9c8c4d2772f3492e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vannicatte?= <20689156+shortcuts@users.noreply.github.com> Date: Fri, 5 Feb 2021 11:46:08 +0100 Subject: [PATCH 6/7] Update packages/website/docs/sources.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: François Chalifour --- packages/website/docs/sources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/docs/sources.md b/packages/website/docs/sources.md index c33352fbb..2ceb703e0 100644 --- a/packages/website/docs/sources.md +++ b/packages/website/docs/sources.md @@ -294,7 +294,7 @@ You can trigger different behaviors if the item is active following a mouse even > `string` -Applied to `data-autocomplete-source-id` on the `section` source container +Identifier for the source. It is used as value for the `data-autocomplete-source-id` attribute of the source `section` container. ### `templates` (specific to `@algolia/autocomplete-js`) From 78f8d84d47523b9f8e974b830ea1a21b1e648c53 Mon Sep 17 00:00:00 2001 From: shortcuts Date: Fri, 5 Feb 2021 11:59:37 +0100 Subject: [PATCH 7/7] Fix `onActive` params type --- packages/autocomplete-core/src/types/AutocompleteSource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/autocomplete-core/src/types/AutocompleteSource.ts b/packages/autocomplete-core/src/types/AutocompleteSource.ts index b032f120b..5c7058683 100644 --- a/packages/autocomplete-core/src/types/AutocompleteSource.ts +++ b/packages/autocomplete-core/src/types/AutocompleteSource.ts @@ -57,7 +57,7 @@ export interface AutocompleteSource { * An item is highlighted either via keyboard navigation or via mouse over. * You can trigger different behaviors based on the event `type`. */ - onActive?(params: OnHighlightParams): void; + onActive?(params: OnActiveParams): void; /** * Identifier for the source. */