From 0cf0a93bf3e5c04972e70c716867ce82e220c640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chalifour?= Date: Fri, 14 Feb 2020 11:39:07 +0100 Subject: [PATCH] feat(core): support `onSelect` on sources --- packages/autocomplete-core/onInput.ts | 6 +- packages/autocomplete-core/onKeyDown.ts | 18 ++- packages/autocomplete-core/propGetters.ts | 31 ++++- packages/autocomplete-core/types/api.ts | 43 ++++--- packages/autocomplete-core/types/events.ts | 16 --- .../types/{propGetters.ts => getters.ts} | 0 packages/autocomplete-core/types/index.ts | 3 +- packages/autocomplete-presets/results.ts | 8 +- stories/react.stories.tsx | 113 ++++++++++++------ 9 files changed, 155 insertions(+), 83 deletions(-) delete mode 100644 packages/autocomplete-core/types/events.ts rename packages/autocomplete-core/types/{propGetters.ts => getters.ts} (100%) diff --git a/packages/autocomplete-core/onInput.ts b/packages/autocomplete-core/onInput.ts index c0ec3dcbd..a1e0821ea 100644 --- a/packages/autocomplete-core/onInput.ts +++ b/packages/autocomplete-core/onInput.ts @@ -32,7 +32,7 @@ export function onInput({ setStatus, setContext, nextState = {}, -}: OnInputOptions): void { +}: OnInputOptions): Promise { if (lastStalledId) { clearTimeout(lastStalledId); } @@ -52,7 +52,7 @@ export function onInput({ nextState.isOpen ?? props.shouldDropdownShow({ state: store.getState() }) ); - return; + return Promise.resolve(); } setStatus('loading'); @@ -61,7 +61,7 @@ export function onInput({ setStatus('stalled'); }, props.stallThreshold); - props + return props .getSources({ query, state: store.getState(), diff --git a/packages/autocomplete-core/onKeyDown.ts b/packages/autocomplete-core/onKeyDown.ts index bdb1a4ab7..8b75ca67e 100644 --- a/packages/autocomplete-core/onKeyDown.ts +++ b/packages/autocomplete-core/onKeyDown.ts @@ -156,9 +156,23 @@ export function onKeyDown({ nextState: { isOpen: false, }, - }); + }).then(() => { + suggestion.source.onSelect({ + suggestion: item, + suggestionValue: inputValue, + suggestionUrl: itemUrl, + source: suggestion.source, + state: store.getState(), + setHighlightedIndex, + setQuery, + setSuggestions, + setIsOpen, + setStatus, + setContext, + }); - props.onStateChange({ state: store.getState() }); + props.onStateChange({ state: store.getState() }); + }); if (itemUrl !== undefined) { props.navigator.navigate({ diff --git a/packages/autocomplete-core/propGetters.ts b/packages/autocomplete-core/propGetters.ts index b3620403c..bbc9c4ed7 100644 --- a/packages/autocomplete-core/propGetters.ts +++ b/packages/autocomplete-core/propGetters.ts @@ -239,11 +239,13 @@ export function getPropGetters({ return; } + const inputValue = source.getInputValue({ + suggestion: item, + state: store.getState(), + }); + onInput({ - query: source.getInputValue({ - suggestion: item, - state: store.getState(), - }), + query: inputValue, store, props, setHighlightedIndex, @@ -255,9 +257,26 @@ export function getPropGetters({ nextState: { isOpen: false, }, - }); + }).then(() => { + source.onSelect({ + suggestion: item, + suggestionValue: inputValue, + suggestionUrl: source.getSuggestionUrl({ + suggestion: item, + state: store.getState(), + }), + source, + state: store.getState(), + setHighlightedIndex, + setQuery, + setSuggestions, + setIsOpen, + setStatus, + setContext, + }); - props.onStateChange({ state: store.getState() }); + props.onStateChange({ state: store.getState() }); + }); }, ...rest, }; diff --git a/packages/autocomplete-core/types/api.ts b/packages/autocomplete-core/types/api.ts index 5ba1f8892..41ef4de25 100644 --- a/packages/autocomplete-core/types/api.ts +++ b/packages/autocomplete-core/types/api.ts @@ -1,7 +1,6 @@ -import { AutocompleteAccessibilityGetters } from './propGetters'; +import { AutocompleteAccessibilityGetters } from './getters'; import { AutocompleteSetters } from './setters'; import { AutocompleteState } from './state'; -import { EventHandlerParams, ItemEventHandlerParams } from './events'; export interface AutocompleteApi extends AutocompleteSetters, @@ -14,11 +13,29 @@ export interface AutocompleteSuggestion { items: TItem[]; } -export interface GetSourcesOptions extends AutocompleteSetters { +export interface GetSourcesParams extends AutocompleteSetters { query: string; state: AutocompleteState; } +interface ItemParams { + suggestion: TItem; + suggestionValue: ReturnType['getInputValue']>; + suggestionUrl: ReturnType['getSuggestionUrl']>; + source: AutocompleteSource; +} + +interface OnSelectParams + extends AutocompleteSetters, + ItemParams { + state: AutocompleteState; +} + +interface OnSubmitParams extends AutocompleteSetters { + state: AutocompleteState; + event: Event; +} + export interface PublicAutocompleteSource { /** * Get the string value of the suggestion. The value is used to fill the search box. @@ -45,14 +62,14 @@ export interface PublicAutocompleteSource { * Function called when the input changes. You can use this function to filter/search the items based on the query. */ getSuggestions( - options: GetSourcesOptions + params: GetSourcesParams ): | Array> | Promise>>; /** * Called when an item is selected. */ - onSelect?: (options: ItemEventHandlerParams) => void; + onSelect?(params: OnSelectParams): void; } export type AutocompleteSource = { @@ -62,7 +79,7 @@ export type AutocompleteSource = { }; export type GetSources = ( - options: GetSourcesOptions + params: GetSourcesParams ) => Promise>>; export interface Environment { @@ -131,10 +148,6 @@ export interface PublicAutocompleteOptions { * @default 0 */ defaultHighlightedIndex?: number; - /** - * The function called when an item is selected. - */ - // onSelect(): void; /** * Whether to show the highlighted suggestion as completion in the input. * @@ -162,7 +175,7 @@ export interface PublicAutocompleteOptions { * The sources to get the suggestions from. */ getSources( - options: GetSourcesOptions + params: GetSourcesParams ): | Array> | Promise>>; @@ -181,11 +194,11 @@ export interface PublicAutocompleteOptions { /** * The function called to determine whether the dropdown should open. */ - shouldDropdownShow?(options: { state: AutocompleteState }): boolean; + shouldDropdownShow?(params: { state: AutocompleteState }): boolean; /** * The function called when the autocomplete form is submitted. */ - onSubmit?(params: EventHandlerParams): void; + onSubmit?(params: OnSubmitParams): void; } // Props manipulated internally with default values. @@ -202,6 +215,6 @@ export interface AutocompleteOptions { getSources: GetSources; environment: Environment; navigator: Navigator; - shouldDropdownShow(options: { state: AutocompleteState }): boolean; - onSubmit(params: EventHandlerParams): void; + shouldDropdownShow(params: { state: AutocompleteState }): boolean; + onSubmit(params: OnSubmitParams): void; } diff --git a/packages/autocomplete-core/types/events.ts b/packages/autocomplete-core/types/events.ts deleted file mode 100644 index 23c3d42c5..000000000 --- a/packages/autocomplete-core/types/events.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AutocompleteSetters } from './setters'; -import { AutocompleteState } from './state'; -import { AutocompleteSource } from './api'; - -export interface EventHandlerParams extends AutocompleteSetters { - state: AutocompleteState; - event: Event; -} - -export interface ItemEventHandlerParams - extends EventHandlerParams { - suggestion: TItem; - suggestionValue: ReturnType['getInputValue']>; - suggestionUrl: ReturnType['getSuggestionUrl']>; - source: AutocompleteSource; -} diff --git a/packages/autocomplete-core/types/propGetters.ts b/packages/autocomplete-core/types/getters.ts similarity index 100% rename from packages/autocomplete-core/types/propGetters.ts rename to packages/autocomplete-core/types/getters.ts diff --git a/packages/autocomplete-core/types/index.ts b/packages/autocomplete-core/types/index.ts index 92fc94bff..31e9e79b5 100644 --- a/packages/autocomplete-core/types/index.ts +++ b/packages/autocomplete-core/types/index.ts @@ -1,6 +1,5 @@ export * from './api'; -export * from './events'; -export * from './propGetters'; +export * from './getters'; export * from './setters'; export * from './state'; export * from './store'; diff --git a/packages/autocomplete-presets/results.ts b/packages/autocomplete-presets/results.ts index 6a367aa39..f1097ebd5 100644 --- a/packages/autocomplete-presets/results.ts +++ b/packages/autocomplete-presets/results.ts @@ -12,7 +12,7 @@ interface SearchParameters { params?: QueryParameters; } -interface GetAlgoliaSourceOptions { +interface GetAlgoliaSourceParams { searchClient: SearchClient; queries: SearchParameters[]; } @@ -20,7 +20,7 @@ interface GetAlgoliaSourceOptions { export function getAlgoliaSource({ searchClient, queries, -}: GetAlgoliaSourceOptions) { +}: GetAlgoliaSourceParams) { if (typeof (searchClient as Client).addAlgoliaAgent === 'function') { if (__DEV__) { (searchClient as Client).addAlgoliaAgent( @@ -52,7 +52,7 @@ export function getAlgoliaSource({ export function getAlgoliaResults({ searchClient, queries, -}: GetAlgoliaSourceOptions): Promise { +}: GetAlgoliaSourceParams): Promise { return getAlgoliaSource({ searchClient, queries }).then(response => { return response.results; }); @@ -61,7 +61,7 @@ export function getAlgoliaResults({ export function getAlgoliaHits({ searchClient, queries, -}: GetAlgoliaSourceOptions): Promise { +}: GetAlgoliaSourceParams): Promise { return getAlgoliaSource({ searchClient, queries }).then(response => { const results = response.results; diff --git a/stories/react.stories.tsx b/stories/react.stories.tsx index cb82d5691..94939769f 100644 --- a/stories/react.stories.tsx +++ b/stories/react.stories.tsx @@ -13,42 +13,85 @@ const searchClient = algoliasearch( '6be0576ff61c053d5f9a3225e2a90f76' ); -storiesOf('React', module).add( - 'Component', - withPlayground(({ container, dropdownContainer }) => { - render( - { - return [ - { - getInputValue({ suggestion }) { - return suggestion.query; +storiesOf('React', module) + .add( + 'Component', + withPlayground(({ container, dropdownContainer }) => { + render( + { + return [ + { + getInputValue({ suggestion }) { + return suggestion.query; + }, + getSuggestions({ query }) { + return getAlgoliaHits({ + searchClient, + queries: [ + { + indexName: 'instant_search_demo_query_suggestions', + query, + params: { + hitsPerPage: 4, + }, + }, + ], + }); + }, }, - getSuggestions({ query }) { - return getAlgoliaHits({ - searchClient, - queries: [ - { - indexName: 'instant_search_demo_query_suggestions', - query, - params: { - hitsPerPage: 4, + ]; + }} + />, + container + ); + + return container; + }) + ) + .add( + 'Dropdown stays open onSelect', + withPlayground(({ container, dropdownContainer }) => { + render( + { + return [ + { + getInputValue({ suggestion }) { + return suggestion.query; + }, + onSelect(props) { + props.setIsOpen(true); + }, + getSuggestions({ query }) { + return getAlgoliaHits({ + searchClient, + queries: [ + { + indexName: 'instant_search_demo_query_suggestions', + query, + params: { + hitsPerPage: 4, + }, }, - }, - ], - }); + ], + }); + }, }, - }, - ]; - }} - />, - container - ); + ]; + }} + />, + container + ); - return container; - }) -); + return container; + }) + );