Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User autocomplete - use debounced search request #4406

Merged
merged 71 commits into from
Apr 4, 2018
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
52dc331
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 8, 2018
f357fce
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 11, 2018
6875efa
Autocomplete search while typing, first pass
Jan 11, 2018
7afff73
remove unrelated changes
Jan 11, 2018
badca63
remove some unused experimental code
Jan 11, 2018
6eb4432
minimize changes
Jan 11, 2018
8f57e6e
whitespace, eslint
Jan 11, 2018
9dc23b9
replace a missing closing tag
Jan 11, 2018
ab425af
undo package-lock changes
Jan 11, 2018
c67ef0d
debounce the search handler
Jan 11, 2018
00f9f0f
debounce at 250 ms, cleanup for eslint
Jan 11, 2018
0edc3ff
only re-search if popover open
Jan 11, 2018
c1e831c
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 12, 2018
bf356c0
hoist search container to this.searchContainer; avoid event.persist()
Jan 12, 2018
1a6c124
Don’t pass getOptions to search
Jan 12, 2018
874dce5
rename openFilteredOptions, switch to const
Jan 12, 2018
5f4206e
remove setSearch, handle in getOptions
Jan 12, 2018
62ca388
pass the query to loadOptions
Jan 12, 2018
e0d288f
call getOptions again if query not blank
Jan 12, 2018
510f642
remove unused currentCompleter.setSearch
Jan 12, 2018
135dc77
use slug for option key if available
Jan 12, 2018
0806d95
use ES6 notation for search
Jan 12, 2018
e3d3f12
filter userAutocompleter.onSelect
Jan 12, 2018
3cb5971
filter userAutocompleter.onSelect
Jan 12, 2018
e9f76a1
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 13, 2018
dac63c0
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 15, 2018
6e3609b
Merge branch 'master' into feature/autocomplete-user-searching
Jan 15, 2018
2d703b5
restore autocomplete component, remove debounce
Jan 15, 2018
98161e5
search again when query has changed
Jan 15, 2018
1dbba93
debounce the user search
Jan 15, 2018
72486f5
remove filtering for now
Jan 15, 2018
36af45b
Fixes for eslint
Jan 15, 2018
e4281a3
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 17, 2018
a14cbb0
Merge branch 'master' into feature/autocomplete-user-searching
Jan 17, 2018
eee4ce3
add an autocomplete `this.debouncedLoadOptions` option and move debou…
Jan 17, 2018
77dfe09
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 18, 2018
5e80481
Merge branch 'master' of github.com:WordPress/gutenberg
Jan 26, 2018
895a5a4
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 1, 2018
654e7db
Merge branch 'master' into feature/autocomplete-user-searching
Feb 1, 2018
3202fb7
when unmounting autocomplete, be certain to cancel any scheduled load…
Feb 1, 2018
4649a30
fix jsdocs
Feb 1, 2018
2572de6
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 2, 2018
edb38af
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 6, 2018
70a4c1c
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 8, 2018
0bd2245
Merge branch 'master' into feature/autocomplete-user-searching
Feb 8, 2018
887b091
Break up longer code line for improved readability
Feb 8, 2018
b9fd29a
rename completer property isDebouncedCompleter -> isDebounced
Feb 8, 2018
b3b97ee
remove some extra whitespace
Feb 8, 2018
0432e65
more whitespace cleanup
Feb 8, 2018
ef79a0f
Break up longer code line for improved readability
Feb 8, 2018
ac62c00
improve readability by breaking out trinery payload setup
Feb 8, 2018
b15ab99
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 12, 2018
e216da5
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 18, 2018
2151b13
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 20, 2018
a0eedce
Merge branch 'master' of github.com:WordPress/gutenberg
Feb 22, 2018
ea50097
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 2, 2018
8124f42
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 5, 2018
22bec2f
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 8, 2018
4be41af
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 13, 2018
d4f7a4c
Merge branch 'master' into feature/autocomplete-user-searching
Mar 13, 2018
ba2dcd3
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 21, 2018
2ecb819
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 26, 2018
ec7e31c
Merge branch 'master' into feature/autocomplete-user-searching
Mar 26, 2018
8d50225
restore use of search payload
Mar 26, 2018
4771c3e
clean up search string build
Mar 26, 2018
a60ad7b
Merge branch 'master' of github.com:WordPress/gutenberg
Mar 29, 2018
0023c9a
Merge branch 'master' into feature/autocomplete-user-searching
Mar 29, 2018
d4e063e
revert unrelated changes in loadOptions
Mar 29, 2018
7b6904b
rename index autocompleterIndex
Mar 29, 2018
4f98c79
last renaming index -> autocompleterIndex
Mar 29, 2018
cdba191
wrap search in encodeURIComponent
Mar 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions blocks/autocompleters/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { sortBy } from 'lodash';
import { sortBy, debounce } from 'lodash';

/**
* Internal dependencies
Expand Down Expand Up @@ -102,9 +102,9 @@ export function blockAutocompleter( { onReplace } ) {
* @returns {Completer} Completer object used by the Autocomplete component.
*/
export function userAutocompleter() {
const getOptions = () => {
return ( new wp.api.collections.Users() ).fetch().then( ( users ) => {
return users.map( ( user ) => {
const performSearch = debounce( function( resolve, searchData ) {
new wp.api.collections.Users().fetch( searchData ).then( ( users ) => {
users = users.map( ( user ) => {
return {
value: user,
label: [
Expand All @@ -115,6 +115,14 @@ export function userAutocompleter() {
keywords: [ user.slug, user.name ],
};
} );
resolve( users );
} );
}, 250 );

const getOptions = ( search = false ) => {
const searchData = search ? { data: { search } } : null;
return new Promise( ( resolve ) => {
performSearch( resolve, searchData );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a promise that may never resolve makes me uncomfortable as I am unsure if it will cause memory to leak (it probably won't but it doesn't hurt to avoid the situation). I suggest that you could cause the promise to reject with a specific error (just use a string) that you can check for in loadOptions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, i have to agree - I feel the same way. unfortunately, this was the only way I could get the debouncing to work and part of why i had moved the debouncing up a level. debouncing a function that returns a promise is a bit problematic.

ideally, we should debounce the function that calls the promise returning function - that way no extra promises are created. let me revisit the idea of moving this "up" again is a way that doesn't affect other autocompleters.

Copy link
Member Author

@adamsilverstein adamsilverstein Jan 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EphoxJames I tried a different approach in eee4ce3

I've added an attribute isDebouncedCompleter: true to the object returned by userAutocompleter. The in the autocomplete component, when this is set for an autocompleter, I call debouncedLoadOptions to debounce the async callbacks. This lets any autocompleter opt in to debouncing.

Let me know what you think of this approach.

} );
};

Expand Down
18 changes: 12 additions & 6 deletions components/autocomplete/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,15 @@ export class Autocomplete extends Component {
}
}

loadOptions( index ) {
this.props.completers[ index ].getOptions().then( ( options ) => {
const keyedOptions = map( options, ( option, i ) => ( { ...option, key: index + '-' + i } ) );
/**
* Load options for an autocompleter.
*
* @param {int} index The autocompleter index.
* @param {string} query The query, if any.
*/
loadOptions( index, query ) {
this.props.completers[ index ].getOptions( query ).then( ( options ) => {
const keyedOptions = map( options, ( option, i ) => ( { ...option, key: ( option.value.slug ? option.value.slug : index ) + '-' + i } ) );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you might have the index and i the wrong way around, I think it should be:

key: index + '-' + ( option.value.slug ? option.value.slug : i )

This is because index should be constant for the completer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, ok thanks for catching that - will correct

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed this in eee4ce3

const filteredOptions = filterOptions( this.state.search, keyedOptions );
const selectedIndex = filteredOptions.length === this.state.filteredOptions.length ? this.state.selectedIndex : 0;
this.setState( {
Expand Down Expand Up @@ -312,7 +318,7 @@ export class Autocomplete extends Component {
}

search( event ) {
const { open: wasOpen, suppress: wasSuppress } = this.state;
const { open: wasOpen, suppress: wasSuppress, query: wasQuery } = this.state;
const { completers } = this.props;
const container = event.target;
// ensure that the cursor location is unambiguous
Expand All @@ -324,8 +330,8 @@ export class Autocomplete extends Component {
const match = this.findMatch( container, cursor, completers, wasOpen );
const { open, query, range } = match || {};
// asynchronously load the options for the open completer
if ( open && ( ! wasOpen || open.idx !== wasOpen.idx ) ) {
this.loadOptions( open.idx );
if ( open && ( ! wasOpen || open.idx !== wasOpen.idx || query !== wasQuery ) ) {
this.loadOptions( open.idx, query );
}
// create a regular expression to filter the options
const search = open ? new RegExp( '(?:\\b|\\s|^)' + escapeRegExp( query ), 'i' ) : /./;
Expand Down