From 553ea68950bfc94eb8588a71dd5580db4682931c Mon Sep 17 00:00:00 2001 From: Eunjae Lee Date: Tue, 25 Feb 2020 15:59:02 +0100 Subject: [PATCH] feat: add openOnFocus and remove minLength (#31) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add openOnFocus and remove minLength * fix: adjust dropdown behavior after resetting based on openOnFocus prop * Update README.md Co-Authored-By: François Chalifour * fix: reset the state on empty query * fix: update conditions to check query length * Update packages/autocomplete-core/src/types/api.ts Co-Authored-By: François Chalifour * fix: remove unnecessary condition Co-authored-by: François Chalifour --- README.md | 6 +-- examples/autocomplete.js/index.tsx | 2 +- .../autocomplete-core/src/defaultProps.ts | 2 +- packages/autocomplete-core/src/onInput.ts | 4 +- packages/autocomplete-core/src/propGetters.ts | 12 +++--- .../autocomplete-core/src/stateReducer.ts | 12 ++++-- packages/autocomplete-core/src/types/api.ts | 8 ++-- stories/react.stories.tsx | 40 +++++++++++++++++++ 8 files changed, 65 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 324e532eb..abbb7586c 100644 --- a/README.md +++ b/README.md @@ -209,11 +209,11 @@ Whether to show the highlighted suggestion as completion in the input. ![`showCompletion` preview](https://user-images.githubusercontent.com/6137112/68124812-7e989800-ff10-11e9-88a5-f28c1466b665.png) -#### `minLength` +#### `openOnFocus` -> `number` | defaults to `1` +> `boolean` | defaults to `false` -The minimum number of characters long the autocomplete opens. +Whether to open the dropdown on focus when there's no query. #### `autoFocus` diff --git a/examples/autocomplete.js/index.tsx b/examples/autocomplete.js/index.tsx index fe306265f..ad75ecf90 100644 --- a/examples/autocomplete.js/index.tsx +++ b/examples/autocomplete.js/index.tsx @@ -17,7 +17,7 @@ const searchClient = algoliasearch( autocomplete({ container: '#autocomplete', placeholder: 'Search…', - minLength: 0, + openOnFocus: true, showCompletion: true, defaultHighlightedIndex: -1, shouldDropdownOpen({ state }) { diff --git a/packages/autocomplete-core/src/defaultProps.ts b/packages/autocomplete-core/src/defaultProps.ts index 9a89f494c..f1ca2938c 100644 --- a/packages/autocomplete-core/src/defaultProps.ts +++ b/packages/autocomplete-core/src/defaultProps.ts @@ -15,7 +15,7 @@ export function getDefaultProps( : {}) as typeof window; return { - minLength: 1, + openOnFocus: false, placeholder: '', autoFocus: false, defaultHighlightedIndex: null, diff --git a/packages/autocomplete-core/src/onInput.ts b/packages/autocomplete-core/src/onInput.ts index 16441c102..d1af4c53f 100644 --- a/packages/autocomplete-core/src/onInput.ts +++ b/packages/autocomplete-core/src/onInput.ts @@ -55,7 +55,7 @@ export function onInput({ setHighlightedIndex(props.defaultHighlightedIndex); setQuery(query); - if (query.length < props.minLength) { + if (query.length === 0 && props.openOnFocus === false) { setStatus('idle'); setSuggestions( store.getState().suggestions.map(suggestion => ({ @@ -117,7 +117,7 @@ export function onInput({ setSuggestions(suggestions as any); setIsOpen( nextState.isOpen ?? - (query.length >= props.minLength && + ((query.length === 0 && props.openOnFocus) || props.shouldDropdownShow({ state: store.getState() })) ); }) diff --git a/packages/autocomplete-core/src/propGetters.ts b/packages/autocomplete-core/src/propGetters.ts index e465e4c1a..5bcf60904 100644 --- a/packages/autocomplete-core/src/propGetters.ts +++ b/packages/autocomplete-core/src/propGetters.ts @@ -117,7 +117,7 @@ export function getPropGetters({ onReset: event => { event.preventDefault(); - if (props.minLength === 0) { + if (props.openOnFocus) { onInput({ query: '', store, @@ -130,7 +130,6 @@ export function getPropGetters({ setContext, }); } - store.send('reset', null); if (providedProps.inputElement) { @@ -143,9 +142,9 @@ export function getPropGetters({ const getInputProps: GetInputProps = providedProps => { function onFocus() { - // We want to trigger a query when `minLength` is reached because the - // dropdown should open with the current query. - if (store.getState().query.length >= props.minLength) { + // We want to trigger a query when `openOnFocus` is true + // because the dropdown should open with the current query. + if (props.openOnFocus || store.getState().query.length > 0) { onInput({ query: store.getState().query, store, @@ -227,8 +226,7 @@ export function getPropGetters({ if ( providedProps.inputElement === props.environment.document.activeElement && - !store.getState().isOpen && - store.getState().query.length >= props.minLength + !store.getState().isOpen ) { onFocus(); } diff --git a/packages/autocomplete-core/src/stateReducer.ts b/packages/autocomplete-core/src/stateReducer.ts index 60ba1488b..7d5ba225b 100644 --- a/packages/autocomplete-core/src/stateReducer.ts +++ b/packages/autocomplete-core/src/stateReducer.ts @@ -103,8 +103,14 @@ export const stateReducer: Reducer = (action, state, props) => { case 'reset': { return { ...state, - highlightedIndex: null, - isOpen: false, + highlightedIndex: + // Since we open the menu on reset when openOnFocus=true + // we need to restore the highlighted index to the defaultHighlightedIndex. (DocSearch use-case) + + // Since we close the menu when openOnFocus=false + // we lose track of the highlighted index. (Query-suggestions use-case) + props.openOnFocus === true ? props.defaultHighlightedIndex : null, + isOpen: props.openOnFocus, // @TODO: Check with UX team if we want to close the menu on reset. status: 'idle', statusContext: {}, query: '', @@ -115,7 +121,7 @@ export const stateReducer: Reducer = (action, state, props) => { return { ...state, highlightedIndex: props.defaultHighlightedIndex, - isOpen: state.query.length >= props.minLength, + isOpen: props.openOnFocus || state.query.length > 0, }; } diff --git a/packages/autocomplete-core/src/types/api.ts b/packages/autocomplete-core/src/types/api.ts index 3b6a53967..cf817a768 100644 --- a/packages/autocomplete-core/src/types/api.ts +++ b/packages/autocomplete-core/src/types/api.ts @@ -168,11 +168,11 @@ export interface PublicAutocompleteOptions { */ showCompletion?: boolean; /** - * The minimum number of characters long the autocomplete opens. + * Whether to open the dropdown on focus when there's no query. * - * @default 1 + * @default false */ - minLength?: number; + openOnFocus?: boolean; /** * The number of milliseconds that must elapse before the autocomplete * experience is stalled. @@ -229,7 +229,7 @@ export interface AutocompleteOptions { autoFocus: boolean; defaultHighlightedIndex: number | null; showCompletion: boolean; - minLength: number; + openOnFocus: boolean; stallThreshold: number; initialState: AutocompleteState; getSources: GetSources; diff --git a/stories/react.stories.tsx b/stories/react.stories.tsx index 8b27e122b..e2ae66994 100644 --- a/stories/react.stories.tsx +++ b/stories/react.stories.tsx @@ -240,6 +240,46 @@ storiesOf('React', module) container ); + return container; + }) + ) + .add( + 'Open on focus', + 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, + }, + }, + ], + }); + }, + }, + ]; + }} + />, + container + ); + return container; }) );