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

Remove @wordpress/api-fetch usage from the block editor module #14527

Merged
merged 2 commits into from
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
76 changes: 41 additions & 35 deletions packages/block-editor/src/components/url-input/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ import scrollIntoView from 'dom-scroll-into-view';
*/
import { __, sprintf, _n } from '@wordpress/i18n';
import { Component, createRef } from '@wordpress/element';
import { decodeEntities } from '@wordpress/html-entities';
import { UP, DOWN, ENTER, TAB } from '@wordpress/keycodes';
import { Spinner, withSpokenMessages, Popover } from '@wordpress/components';
import { withInstanceId } from '@wordpress/compose';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import { withInstanceId, compose } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';

// Since URLInput is rendered in the context of other inputs, but should be
// considered a separate modal node, prevent keyboard events from propagating
Expand All @@ -35,7 +33,7 @@ class URLInput extends Component {
this.suggestionNodes = [];

this.state = {
posts: [],
suggestions: [],
showSuggestions: false,
selectedSuggestion: null,
};
Expand Down Expand Up @@ -68,6 +66,11 @@ class URLInput extends Component {
}

updateSuggestions( value ) {
const { fetchLinkSuggestions } = this.props;
if ( ! fetchLinkSuggestions ) {
return;
}

// Show the suggestions after typing at least 2 characters
// and also for URLs
if ( value.length < 2 || /^https?:/.test( value ) ) {
Expand All @@ -86,15 +89,9 @@ class URLInput extends Component {
loading: true,
} );

const request = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search: value,
per_page: 20,
type: 'post',
} ),
} );
const request = fetchLinkSuggestions( value );

request.then( ( posts ) => {
request.then( ( suggestions ) => {
// A fetch Promise doesn't have an abort option. It's mimicked by
// comparing the request reference in on the instance, which is
// reset or deleted on subsequent requests or unmounting.
Expand All @@ -103,16 +100,16 @@ class URLInput extends Component {
}

this.setState( {
posts,
suggestions,
loading: false,
} );

if ( !! posts.length ) {
if ( !! suggestions.length ) {
this.props.debouncedSpeak( sprintf( _n(
'%d result found, use up and down arrow keys to navigate.',
'%d results found, use up and down arrow keys to navigate.',
posts.length
), posts.length ), 'assertive' );
suggestions.length
), suggestions.length ), 'assertive' );
} else {
this.props.debouncedSpeak( __( 'No results.' ), 'assertive' );
}
Expand All @@ -134,10 +131,10 @@ class URLInput extends Component {
}

onKeyDown( event ) {
const { showSuggestions, selectedSuggestion, posts, loading } = this.state;
const { showSuggestions, selectedSuggestion, suggestions, loading } = this.state;
// If the suggestions are not shown or loading, we shouldn't handle the arrow keys
// We shouldn't preventDefault to allow block arrow keys navigation
if ( ! showSuggestions || ! posts.length || loading ) {
if ( ! showSuggestions || ! suggestions.length || loading ) {
// In the Windows version of Firefox the up and down arrows don't move the caret
// within an input field like they do for Mac Firefox/Chrome/Safari. This causes
// a form of focus trapping that is disruptive to the user experience. This disruption
Expand Down Expand Up @@ -173,13 +170,13 @@ class URLInput extends Component {
return;
}

const post = this.state.posts[ this.state.selectedSuggestion ];
const suggestion = this.state.suggestions[ this.state.selectedSuggestion ];

switch ( event.keyCode ) {
case UP: {
event.stopPropagation();
event.preventDefault();
const previousIndex = ! selectedSuggestion ? posts.length - 1 : selectedSuggestion - 1;
const previousIndex = ! selectedSuggestion ? suggestions.length - 1 : selectedSuggestion - 1;
this.setState( {
selectedSuggestion: previousIndex,
} );
Expand All @@ -188,15 +185,15 @@ class URLInput extends Component {
case DOWN: {
event.stopPropagation();
event.preventDefault();
const nextIndex = selectedSuggestion === null || ( selectedSuggestion === posts.length - 1 ) ? 0 : selectedSuggestion + 1;
const nextIndex = selectedSuggestion === null || ( selectedSuggestion === suggestions.length - 1 ) ? 0 : selectedSuggestion + 1;
this.setState( {
selectedSuggestion: nextIndex,
} );
break;
}
case TAB: {
if ( this.state.selectedSuggestion !== null ) {
this.selectLink( post );
this.selectLink( suggestion );
// Announce a link has been selected when tabbing away from the input field.
this.props.speak( __( 'Link selected.' ) );
}
Expand All @@ -205,30 +202,30 @@ class URLInput extends Component {
case ENTER: {
if ( this.state.selectedSuggestion !== null ) {
event.stopPropagation();
this.selectLink( post );
this.selectLink( suggestion );
}
break;
}
}
}

selectLink( post ) {
this.props.onChange( post.url, post );
selectLink( suggestion ) {
this.props.onChange( suggestion.url, suggestion );
this.setState( {
selectedSuggestion: null,
showSuggestions: false,
} );
}

handleOnClick( post ) {
this.selectLink( post );
handleOnClick( suggestion ) {
this.selectLink( suggestion );
// Move focus to the input field when a link suggestion is clicked.
this.inputRef.current.focus();
}

render() {
const { value = '', autoFocus = true, instanceId, className } = this.props;
const { showSuggestions, posts, selectedSuggestion, loading } = this.state;
const { showSuggestions, suggestions, selectedSuggestion, loading } = this.state;
/* eslint-disable jsx-a11y/no-autofocus */
return (
<div className={ classnames( 'editor-url-input block-editor-url-input', className ) }>
Expand All @@ -252,28 +249,28 @@ class URLInput extends Component {

{ ( loading ) && <Spinner /> }

{ showSuggestions && !! posts.length &&
{ showSuggestions && !! suggestions.length &&
<Popover position="bottom" noArrow focusOnMount={ false }>
<div
className="editor-url-input__suggestions block-editor-url-input__suggestions"
id={ `editor-url-input-suggestions-${ instanceId }` }
ref={ this.autocompleteRef }
role="listbox"
>
{ posts.map( ( post, index ) => (
{ suggestions.map( ( suggestion, index ) => (
<button
key={ post.id }
key={ suggestion.id }
role="option"
tabIndex="-1"
id={ `block-editor-url-input-suggestion-${ instanceId }-${ index }` }
ref={ this.bindSuggestionNode( index ) }
className={ classnames( 'editor-url-input__suggestion block-editor-url-input__suggestion', {
'is-selected': index === selectedSuggestion,
} ) }
onClick={ () => this.handleOnClick( post ) }
onClick={ () => this.handleOnClick( suggestion ) }
aria-selected={ index === selectedSuggestion }
>
{ decodeEntities( post.title ) || __( '(no title)' ) }
{ suggestion.title }
</button>
) ) }
</div>
Expand All @@ -285,4 +282,13 @@ class URLInput extends Component {
}
}

export default withSpokenMessages( withInstanceId( URLInput ) );
export default compose(
withSpokenMessages,
withInstanceId,
withSelect( ( select ) => {
const { getSettings } = select( 'core/block-editor' );
return {
fetchLinkSuggestions: getSettings().__experimentalFetchLinkSuggestions,
};
} )
)( URLInput );
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/url-input/test/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import ReactDOM from 'react-dom';
import URLInput from '../';
import URLInputButton from '../button';

import '../../../store';

describe( 'URLInputButton', () => {
const clickEditLink = ( wrapper ) => wrapper.find( 'ForwardRef(IconButton).components-toolbar__control' ).simulate( 'click' );

Expand Down
20 changes: 20 additions & 0 deletions packages/editor/src/components/provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,32 @@ import { Component } from '@wordpress/element';
import { withDispatch, withSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { BlockEditorProvider } from '@wordpress/block-editor';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import { decodeEntities } from '@wordpress/html-entities';

/**
* Internal dependencies
*/
import transformStyles from '../../editor-styles';
import { mediaUpload } from '../../utils';

const fetchLinkSuggestions = async ( search ) => {
const posts = await apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: 20,
type: 'post',
} ),
} );

return map( posts, ( post ) => ( {
id: post.id,
url: post.url,
title: decodeEntities( post.title ) || __( '(no title)' ),
} ) );
};

class EditorProvider extends Component {
constructor( props ) {
super( ...arguments );
Expand Down Expand Up @@ -78,6 +97,7 @@ class EditorProvider extends Component {
},
__experimentalReusableBlocks: reusableBlocks,
__experimentalMediaUpload: mediaUpload,
__experimentalFetchLinkSuggestions: fetchLinkSuggestions,
};
}

Expand Down