From 7213b6b4f2ebe35845168e8bf8c6be501ac846fd Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 21 Jun 2018 13:56:59 -0400 Subject: [PATCH 1/6] Downshift based author selector, first pass --- editor/components/post-author/index.js | 69 +++++++++++++++++++++----- package-lock.json | 21 ++++++++ package.json | 1 + 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 46e3b4b3fe220a..2528acf2287270 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -11,6 +11,11 @@ import { withSelect, withDispatch } from '@wordpress/data'; */ import PostAuthorCheck from './check'; +/** + * External dependencies + */ +import Downshift from 'downshift' + export class PostAuthor extends Component { constructor() { super( ...arguments ); @@ -18,32 +23,70 @@ export class PostAuthor extends Component { this.setAuthorId = this.setAuthorId.bind( this ); } - setAuthorId( event ) { + setAuthorId( selected ) { + console.log( selected ); const { onUpdateAuthor } = this.props; - const { value } = event.target; - onUpdateAuthor( Number( value ) ); + const { id } = selected; + onUpdateAuthor( Number( id ) ); } render() { const { postAuthor, instanceId, authors } = this.props; const selectId = 'post-author-selector-' + instanceId; - // Disable reason: A select with an onchange throws a warning + const authorName = authors + .filter( author => author.id === postAuthor )[0] + .name /* eslint-disable jsx-a11y/no-onchange */ return ( - + {({ + getInputProps, + getItemProps, + getLabelProps, + getMenuProps, + isOpen, + inputValue, + highlightedIndex, + selectedItem, + }) => ( +
+ +
    + {isOpen + ? authors + .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 */ 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", From dec6f464f0926d94ff96e0844af7b86e452adb15 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 21 Jun 2018 17:27:52 -0400 Subject: [PATCH 2/6] add searching --- editor/components/post-author/index.js | 31 ++++++++++++++++++-------- packages/core-data/src/resolvers.js | 10 ++++++++- packages/core-data/src/selectors.js | 11 +++++++++ 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 2528acf2287270..f81c9014ed8ddc 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -5,6 +5,7 @@ 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 @@ -14,38 +15,49 @@ import PostAuthorCheck from './check'; /** * External dependencies */ -import Downshift from 'downshift' +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( selected ) { - console.log( selected ); const { onUpdateAuthor } = this.props; const { id } = selected; onUpdateAuthor( Number( id ) ); } + suggestAuthors( query, populateResults ) { + if ( query.length < 2 ) { + return; + } + const payload = '?search=' + encodeURIComponent( query ); + apiRequest( { path: '/wp/v2/users' + payload } ).done( ( results ) => { + this.setState( results.map( ( author ) => ( author.name ) ) ); + } ); + } + render() { - const { postAuthor, instanceId, authors } = this.props; + const { postAuthor, instanceId, authors, author } = this.props; const selectId = 'post-author-selector-' + instanceId; - - const authorName = authors - .filter( author => author.id === postAuthor )[0] - .name + const currentPostAuthor = author.length > 0 ? author[0].name : ''; + const allAuthors = this.state && this.state.authors ? this.state.authors : authors; /* eslint-disable jsx-a11y/no-onchange */ return ( + currentPostAuthor && (author ? author.value : '')} - defaultInputValue={ authorName } + defaultInputValue={ currentPostAuthor } + onInputValueChange={ debounce( this.suggestAuthors, 300 ) } > {({ getInputProps, @@ -61,7 +73,7 @@ export class PostAuthor extends Component {
    {isOpen - ? authors + ? allAuthors .map( author => ( { id: author.id, value: author.name} ) ) .filter( author => ! inputValue || @@ -98,6 +110,7 @@ export default compose( [ return { postAuthor: select( 'core/editor' ).getEditedPostAttribute( 'author' ), authors: select( 'core' ).getAuthors(), + author: select( 'core' ).getAuthor( select( 'core/editor' ).getEditedPostAttribute( 'author' ) ), }; } ), withDispatch( ( dispatch ) => ( { diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index b35844b6bd78ea..1917508111d6de 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -32,10 +32,18 @@ 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. + */ +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. * From bcb97de68a8da60bd7413d592a14c737eceb45a6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 21 Jun 2018 23:28:05 -0400 Subject: [PATCH 3/6] add a search active spinner --- editor/components/post-author/index.js | 69 ++++++++++++++------------ 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index f81c9014ed8ddc..5a43fabdd38b15 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -33,12 +33,11 @@ export class PostAuthor extends Component { } suggestAuthors( query, populateResults ) { - if ( query.length < 2 ) { - return; - } const payload = '?search=' + encodeURIComponent( query ); + this.setState( { searching: true } ); apiRequest( { path: '/wp/v2/users' + payload } ).done( ( results ) => { - this.setState( results.map( ( author ) => ( author.name ) ) ); + this.setState( { searching: false } ); + this.setState( { authors: results.map( author => ( { id: author.id, name: author.name} ) ) } ); } ); } @@ -47,10 +46,11 @@ export class PostAuthor extends Component { const selectId = 'post-author-selector-' + instanceId; const currentPostAuthor = author.length > 0 ? author[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 && + currentPostAuthor && allAuthors && (
    - -
      - {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} -
    + +
    + + +
    +
      + {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} +
    ) }
    From b898830ac40775a62a1659492073d62c10592dc5 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 21 Jun 2018 23:46:03 -0400 Subject: [PATCH 4/6] fixes for eslint --- editor/components/post-author/index.js | 73 +++++++++++++------------- packages/core-data/src/resolvers.js | 3 ++ 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 5a43fabdd38b15..89e5534edcb384 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -32,19 +32,19 @@ export class PostAuthor extends Component { onUpdateAuthor( Number( id ) ); } - suggestAuthors( query, populateResults ) { + suggestAuthors( query ) { 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} ) ) } ); + this.setState( { authors: results.map( ( author ) => ( { id: author.id, name: author.name } ) ) } ); } ); - } + } render() { - const { postAuthor, instanceId, authors, author } = this.props; + const { instanceId, authors, postAuthor } = this.props; const selectId = 'post-author-selector-' + instanceId; - const currentPostAuthor = author.length > 0 ? author[0].name : ''; + 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; @@ -54,54 +54,54 @@ export class PostAuthor extends Component { (author ? author.value : '')} + onChange={ this.setAuthorId } + itemToString={ ( author ) => ( author ? author.value : '' ) } defaultInputValue={ currentPostAuthor } onInputValueChange={ debounce( this.suggestAuthors, 300 ) } > - {({ + { ( { 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} +
        + { 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 }
    ) } @@ -115,9 +115,8 @@ 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(), - author: select( 'core' ).getAuthor( select( 'core/editor' ).getEditedPostAttribute( 'author' ) ), }; } ), withDispatch( ( dispatch ) => ( { diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 1917508111d6de..ae9c7e4be71062 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -38,6 +38,9 @@ export async function* getAuthors() { /** * 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 }` } ); From df22448889164301160d1b36c3cdceeecde5183f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 22 Jun 2018 08:54:47 -0400 Subject: [PATCH 5/6] Only search authors if dropdown open, prevents search after selection --- editor/components/post-author/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 89e5534edcb384..5982dbcf566a4e 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -32,7 +32,10 @@ export class PostAuthor extends Component { onUpdateAuthor( Number( id ) ); } - suggestAuthors( query ) { + suggestAuthors( query, args ) { + if ( ! args.isOpen ) { + return; + } const payload = '?search=' + encodeURIComponent( query ); this.setState( { searching: true } ); apiRequest( { path: '/wp/v2/users' + payload } ).done( ( results ) => { From 9dcc5ad56c11335c596e698d676484fe5718de03 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 22 Jun 2018 09:31:33 -0400 Subject: [PATCH 6/6] refinements for voiceover --- editor/components/post-author/index.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/editor/components/post-author/index.js b/editor/components/post-author/index.js index 5982dbcf566a4e..58add86bee139f 100644 --- a/editor/components/post-author/index.js +++ b/editor/components/post-author/index.js @@ -55,7 +55,6 @@ export class PostAuthor extends Component { return ( currentPostAuthor && allAuthors && - ( author ? author.value : '' ) } @@ -65,6 +64,7 @@ export class PostAuthor extends Component { { ( { getInputProps, getItemProps, + getLabelProps, getMenuProps, isOpen, inputValue, @@ -72,15 +72,13 @@ export class PostAuthor extends Component { selectedItem, } ) => (
    - -
    - - -
    -
      + + + +
        { isOpen ? allAuthors .map( ( author ) => ( { id: author.id, value: author.name } ) ) @@ -89,7 +87,6 @@ export class PostAuthor extends Component { author.value.toLowerCase().includes( inputValue.toLowerCase() ) ) .map( ( author, index ) => (