diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 46e3b4b3fe220a..58add86bee139f 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -5,45 +5,107 @@ import { __ } from '@wordpress/i18n'; import { withInstanceId } from '@wordpress/components'; import { Component, compose } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; +import apiRequest from '@wordpress/api-request'; /** * Internal dependencies */ import PostAuthorCheck from './check'; +/** + * External dependencies + */ +import Downshift from 'downshift'; +import { debounce } from 'underscore'; + export class PostAuthor extends Component { constructor() { super( ...arguments ); this.setAuthorId = this.setAuthorId.bind( this ); + this.suggestAuthors = this.suggestAuthors.bind( this ); } - setAuthorId( event ) { + setAuthorId( selected ) { const { onUpdateAuthor } = this.props; - const { value } = event.target; - onUpdateAuthor( Number( value ) ); + const { id } = selected; + onUpdateAuthor( Number( id ) ); + } + + suggestAuthors( query, args ) { + if ( ! args.isOpen ) { + return; + } + const payload = '?search=' + encodeURIComponent( query ); + this.setState( { searching: true } ); + apiRequest( { path: '/wp/v2/users' + payload } ).done( ( results ) => { + this.setState( { searching: false } ); + this.setState( { authors: results.map( ( author ) => ( { id: author.id, name: author.name } ) ) } ); + } ); } render() { - const { postAuthor, instanceId, authors } = this.props; + const { instanceId, authors, postAuthor } = this.props; const selectId = 'post-author-selector-' + instanceId; - - // Disable reason: A select with an onchange throws a warning + const currentPostAuthor = postAuthor.length > 0 ? postAuthor[ 0 ].name : ''; + const allAuthors = this.state && this.state.authors ? this.state.authors : authors; + const isSearching = this.state && this.state.searching; /* eslint-disable jsx-a11y/no-onchange */ return ( + currentPostAuthor && allAuthors && - - + { ( { + getInputProps, + getItemProps, + getLabelProps, + getMenuProps, + isOpen, + inputValue, + highlightedIndex, + selectedItem, + } ) => ( +
+ + + +
    + { isOpen ? + allAuthors + .map( ( author ) => ( { id: author.id, value: author.name } ) ) + .filter( ( author ) => + ! inputValue || + author.value.toLowerCase().includes( inputValue.toLowerCase() ) ) + .map( ( author, index ) => ( +
  • + { author.value } +
  • + ) ) : + null } +
+
+ ) } +
); /* eslint-enable jsx-a11y/no-onchange */ @@ -53,7 +115,7 @@ export class PostAuthor extends Component { export default compose( [ withSelect( ( select ) => { return { - postAuthor: select( 'core/editor' ).getEditedPostAttribute( 'author' ), + postAuthor: select( 'core' ).getAuthor( select( 'core/editor' ).getEditedPostAttribute( 'author' ) ), authors: select( 'core' ).getAuthors(), }; } ), diff --git a/package-lock.json b/package-lock.json index 8a432d50d705ad..eb15e8294f1834 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4387,6 +4387,27 @@ "is-obj": "^1.0.0" } }, + "downshift": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-2.0.7.tgz", + "integrity": "sha512-JMRUWhArX8QQZD63tZRNCayVQ4XjsnU4THcoM+kkHjKZ5Ce0dd1tXD+/oI1V5w6hRixcrS2Y9R/3CGvaBxcvIQ==", + "dev": true, + "requires": { + "prop-types": "^15.6.0" + }, + "dependencies": { + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "dev": true, + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + } + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", diff --git a/package.json b/package.json index 358ba58d6c8fcb..abfe597d549743 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "cross-env": "3.2.4", "deep-freeze": "0.0.1", "doctrine": "2.1.0", + "downshift": "2.0.7", "eslint": "4.16.0", "eslint-config-wordpress": "2.0.0", "eslint-plugin-jest": "21.5.0", diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index b35844b6bd78ea..ae9c7e4be71062 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -32,10 +32,21 @@ export async function* getCategories() { * Requests authors from the REST API. */ export async function* getAuthors() { - const users = await apiRequest( { path: '/wp/v2/users/?who=authors&per_page=-1' } ); + const users = await apiRequest( { path: '/wp/v2/users/?who=authors&per_page=100' } ); yield receiveUserQuery( 'authors', users ); } +/** + * Requests author details from the REST API. + * + * @param {Object} state State tree + * @param {number} id Author id. + */ +export async function* getAuthor( state, id ) { + const author = await apiRequest( { path: `/wp/v2/users/${ id }` } ); + yield receiveUserQuery( 'author', author ); +} + /** * Requests an entity's record from the REST API. * diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 7d36a4c65558a5..1455fa459e7744 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -86,6 +86,17 @@ export function getAuthors( state ) { return getUserQueryResults( state, 'authors' ); } +/** + * Returns all the post author. + * + * @param {Object} state Data state. + * + * @return {Array} Authors list. + */ +export function getAuthor( state ) { + return getUserQueryResults( state, 'author' ); +} + /** * Returns all the users returned by a query ID. *