diff --git a/components/autocomplete/index.js b/components/autocomplete/index.js index 85e07f4e8d9668..ee3014683bd2ef 100644 --- a/components/autocomplete/index.js +++ b/components/autocomplete/index.js @@ -3,6 +3,7 @@ */ import classnames from 'classnames'; import { escapeRegExp, find, filter, map, debounce } from 'lodash'; +import 'element-closest'; /** * WordPress dependencies @@ -243,6 +244,16 @@ export class Autocomplete extends Component { range.setStartAfter( child ); } range.deleteContents(); + + let inputEvent; + if ( undefined !== window.InputEvent ) { + inputEvent = new window.InputEvent( 'input', { bubbles: true, cancelable: false } ); + } else { + // IE11 doesn't provide an InputEvent constructor. + inputEvent = document.createEvent( 'UIEvent' ); + inputEvent.initEvent( 'input', true /* bubbles */, false /* cancelable */ ); + } + range.commonAncestorContainer.closest( '[contenteditable=true]' ).dispatchEvent( inputEvent ); } select( option ) { @@ -254,8 +265,6 @@ export class Autocomplete extends Component { return; } - this.reset(); - if ( getOptionCompletion ) { const completion = getOptionCompletion( option.value, range, query ); @@ -278,10 +287,20 @@ export class Autocomplete extends Component { } } } + + // Reset autocomplete state after insertion rather than before + // so insertion events don't cause the completion menu to redisplay. + this.reset(); } reset() { - this.setState( this.constructor.getInitialState() ); + const isMounted = !! this.node; + + // Autocompletions may replace the block containing this component, + // so we make sure it is mounted before resetting the state. + if ( isMounted ) { + this.setState( this.constructor.getInitialState() ); + } } resetWhenSuppressed() {