From 3779e052dbd5975538ee4e642b657e837a368c24 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 26 Jul 2021 17:21:50 +0400 Subject: [PATCH 1/9] Editor: Refactor Post Author component --- .../src/components/post-author/check.js | 3 ++- .../src/components/post-author/combobox.js | 22 ++++++++++--------- .../src/components/post-author/constants.js | 6 +++++ .../src/components/post-author/index.js | 5 +++-- .../src/components/post-author/select.js | 4 +++- 5 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 packages/editor/src/components/post-author/constants.js diff --git a/packages/editor/src/components/post-author/check.js b/packages/editor/src/components/post-author/check.js index 41ec7d61f27a06..02e0f5cb3db532 100644 --- a/packages/editor/src/components/post-author/check.js +++ b/packages/editor/src/components/post-author/check.js @@ -13,6 +13,7 @@ import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ +import { AUTHORS_QUERY } from './constants'; import PostTypeSupportCheck from '../post-type-support-check'; import { store as editorStore } from '../../store'; @@ -42,7 +43,7 @@ export default compose( [ false ), postType: select( editorStore ).getCurrentPostType(), - authors: select( coreStore ).getAuthors(), + authors: select( coreStore ).getUsers( AUTHORS_QUERY ), }; } ), withInstanceId, diff --git a/packages/editor/src/components/post-author/combobox.js b/packages/editor/src/components/post-author/combobox.js index 1f15e8c4ddac2b..985471c621a862 100644 --- a/packages/editor/src/components/post-author/combobox.js +++ b/packages/editor/src/components/post-author/combobox.js @@ -15,6 +15,7 @@ import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ +import { AUTHORS_QUERY } from './constants'; import { store as editorStore } from '../../store'; function PostAuthorCombobox() { @@ -22,20 +23,21 @@ function PostAuthorCombobox() { const { authorId, isLoading, authors, postAuthor } = useSelect( ( select ) => { - const { __unstableGetAuthor, getAuthors, isResolving } = select( - coreStore - ); + const { getUser, getUsers, isResolving } = select( coreStore ); const { getEditedPostAttribute } = select( editorStore ); - const author = __unstableGetAuthor( - getEditedPostAttribute( 'author' ) - ); - const query = - ! fieldValue || '' === fieldValue ? {} : { search: fieldValue }; + const author = getUser( getEditedPostAttribute( 'author' ), { + context: 'view', + } ); + const query = { + search: ! fieldValue ? undefined : fieldValue, + ...AUTHORS_QUERY, + }; + return { authorId: getEditedPostAttribute( 'author' ), postAuthor: author, - authors: getAuthors( query ), - isLoading: isResolving( 'core', 'getAuthors', [ query ] ), + authors: getUsers( query ), + isLoading: isResolving( 'core', 'getUsers', [ query ] ), }; }, [ fieldValue ] diff --git a/packages/editor/src/components/post-author/constants.js b/packages/editor/src/components/post-author/constants.js new file mode 100644 index 00000000000000..91fb425156a802 --- /dev/null +++ b/packages/editor/src/components/post-author/constants.js @@ -0,0 +1,6 @@ +export const AUTHORS_QUERY = { + who: 'authors', + per_page: 50, + _fields: 'id,name', + context: 'view', +}; diff --git a/packages/editor/src/components/post-author/index.js b/packages/editor/src/components/post-author/index.js index 0739d75e5e9ed9..7e734379f64f64 100644 --- a/packages/editor/src/components/post-author/index.js +++ b/packages/editor/src/components/post-author/index.js @@ -9,13 +9,14 @@ import { store as coreStore } from '@wordpress/core-data'; */ import PostAuthorCombobox from './combobox'; import PostAuthorSelect from './select'; +import { AUTHORS_QUERY } from './constants'; const minimumUsersForCombobox = 25; function PostAuthor() { const showCombobox = useSelect( ( select ) => { - // Not using `getUsers()` because it requires `list_users` capability. - const authors = select( coreStore ).getAuthors(); + const authors = select( coreStore ).getUsers( AUTHORS_QUERY ); + return authors?.length >= minimumUsersForCombobox; }, [] ); diff --git a/packages/editor/src/components/post-author/select.js b/packages/editor/src/components/post-author/select.js index f0935ad86c8de2..115d8b1a466abe 100644 --- a/packages/editor/src/components/post-author/select.js +++ b/packages/editor/src/components/post-author/select.js @@ -11,11 +11,13 @@ import { store as coreStore } from '@wordpress/core-data'; * Internal dependencies */ import { store as editorStore } from '../../store'; +import { AUTHORS_QUERY } from './constants'; function PostAuthorSelect() { const { editPost } = useDispatch( editorStore ); const { postAuthor, authors } = useSelect( ( select ) => { - const authorsFromAPI = select( coreStore ).getAuthors(); + const authorsFromAPI = select( coreStore ).getUsers( AUTHORS_QUERY ); + return { postAuthor: select( editorStore ).getEditedPostAttribute( 'author' From 99c19eaa6792578f65b40f8493d3650dc8432725 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 26 Jul 2021 18:40:14 +0400 Subject: [PATCH 2/9] Cleanup PostAuthorCheck component --- packages/editor/src/components/post-author/check.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/components/post-author/check.js b/packages/editor/src/components/post-author/check.js index 02e0f5cb3db532..a078dfaddefdd2 100644 --- a/packages/editor/src/components/post-author/check.js +++ b/packages/editor/src/components/post-author/check.js @@ -19,10 +19,10 @@ import { store as editorStore } from '../../store'; export function PostAuthorCheck( { hasAssignAuthorAction, - authors, + hasAuthors, children, } ) { - if ( ! hasAssignAuthorAction || ! authors || 1 >= authors.length ) { + if ( ! hasAssignAuthorAction || ! hasAuthors ) { return null; } @@ -36,14 +36,14 @@ export function PostAuthorCheck( { export default compose( [ withSelect( ( select ) => { const post = select( editorStore ).getCurrentPost(); + const authors = select( coreStore ).getUsers( AUTHORS_QUERY ); return { hasAssignAuthorAction: get( post, [ '_links', 'wp:action-assign-author' ], false ), - postType: select( editorStore ).getCurrentPostType(), - authors: select( coreStore ).getUsers( AUTHORS_QUERY ), + hasAuthors: authors?.length >= 1, }; } ), withInstanceId, From 29cd70b15f32457d61af20977659516a5071475a Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 11:54:51 +0400 Subject: [PATCH 3/9] Only set search query argument when we have filter value --- packages/editor/src/components/post-author/combobox.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/components/post-author/combobox.js b/packages/editor/src/components/post-author/combobox.js index 985471c621a862..fc7bda62ff7dc7 100644 --- a/packages/editor/src/components/post-author/combobox.js +++ b/packages/editor/src/components/post-author/combobox.js @@ -28,10 +28,11 @@ function PostAuthorCombobox() { const author = getUser( getEditedPostAttribute( 'author' ), { context: 'view', } ); - const query = { - search: ! fieldValue ? undefined : fieldValue, - ...AUTHORS_QUERY, - }; + const query = { ...AUTHORS_QUERY }; + + if ( fieldValue ) { + query.search = fieldValue; + } return { authorId: getEditedPostAttribute( 'author' ), From 0c185169c52463092e273ebc488d40f5dce4dac0 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 11:59:09 +0400 Subject: [PATCH 4/9] Update check component unit tests --- .../src/components/post-author/test/check.js | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/packages/editor/src/components/post-author/test/check.js b/packages/editor/src/components/post-author/test/check.js index c39d83ef50afc9..d3e6bd2ed229c6 100644 --- a/packages/editor/src/components/post-author/test/check.js +++ b/packages/editor/src/components/post-author/test/check.js @@ -9,45 +9,10 @@ import { shallow } from 'enzyme'; import { PostAuthorCheck } from '../check'; describe( 'PostAuthorCheck', () => { - const users = { - data: [ - { - id: 1, - name: 'admin', - capabilities: { - level_1: true, - }, - }, - { - id: 2, - name: 'subscriber', - capabilities: { - level_0: true, - }, - }, - { - id: 3, - name: 'andrew', - capabilities: { - level_1: true, - }, - }, - ], - }; - - it( 'should not render anything if users unknown', () => { - const wrapper = shallow( - - authors - - ); - expect( wrapper.type() ).toBe( null ); - } ); - - it( 'should not render anything if single user', () => { + it( 'should not render anything if has no authors', () => { const wrapper = shallow( authors @@ -58,7 +23,10 @@ describe( 'PostAuthorCheck', () => { it( "should not render anything if doesn't have author action", () => { const wrapper = shallow( - + authors ); @@ -67,7 +35,7 @@ describe( 'PostAuthorCheck', () => { it( 'should render control', () => { const wrapper = shallow( - + authors ); From 3f06b0f724005df9836d00882d10e69d7909e3aa Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 12:18:34 +0400 Subject: [PATCH 5/9] Don't set post author name as search value on mount --- packages/editor/src/components/post-author/combobox.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/editor/src/components/post-author/combobox.js b/packages/editor/src/components/post-author/combobox.js index fc7bda62ff7dc7..d9862952377783 100644 --- a/packages/editor/src/components/post-author/combobox.js +++ b/packages/editor/src/components/post-author/combobox.js @@ -6,7 +6,7 @@ import { debounce } from 'lodash'; /** * WordPress dependencies */ -import { useState, useMemo, useEffect } from '@wordpress/element'; +import { useState, useMemo } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { ComboboxControl } from '@wordpress/components'; @@ -68,14 +68,6 @@ function PostAuthorCombobox() { return fetchedAuthors; }, [ authors, postAuthor ] ); - // Initializes the post author properly - // Also ensures external changes are reflected. - useEffect( () => { - if ( postAuthor ) { - setFieldValue( postAuthor.name ); - } - }, [ postAuthor ] ); - /** * Handle author selection. * From 5ce07f2f3aab0bbf80839cfdd783b9c288b9eaa4 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 12:20:57 +0400 Subject: [PATCH 6/9] Decode entities in author names --- packages/editor/src/components/post-author/check.js | 2 +- packages/editor/src/components/post-author/combobox.js | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/components/post-author/check.js b/packages/editor/src/components/post-author/check.js index a078dfaddefdd2..ee66a80cadea50 100644 --- a/packages/editor/src/components/post-author/check.js +++ b/packages/editor/src/components/post-author/check.js @@ -13,9 +13,9 @@ import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ -import { AUTHORS_QUERY } from './constants'; import PostTypeSupportCheck from '../post-type-support-check'; import { store as editorStore } from '../../store'; +import { AUTHORS_QUERY } from './constants'; export function PostAuthorCheck( { hasAssignAuthorAction, diff --git a/packages/editor/src/components/post-author/combobox.js b/packages/editor/src/components/post-author/combobox.js index d9862952377783..daf310b2b1c541 100644 --- a/packages/editor/src/components/post-author/combobox.js +++ b/packages/editor/src/components/post-author/combobox.js @@ -10,13 +10,14 @@ import { useState, useMemo } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { ComboboxControl } from '@wordpress/components'; +import { decodeEntities } from '@wordpress/html-entities'; import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ -import { AUTHORS_QUERY } from './constants'; import { store as editorStore } from '../../store'; +import { AUTHORS_QUERY } from './constants'; function PostAuthorCombobox() { const [ fieldValue, setFieldValue ] = useState(); @@ -49,7 +50,7 @@ function PostAuthorCombobox() { const fetchedAuthors = ( authors ?? [] ).map( ( author ) => { return { value: author.id, - label: author.name, + label: decodeEntities( author.name ), }; } ); @@ -60,7 +61,10 @@ function PostAuthorCombobox() { if ( foundAuthor < 0 && postAuthor ) { return [ - { value: postAuthor.id, label: postAuthor.name }, + { + value: postAuthor.id, + label: decodeEntities( postAuthor.name ), + }, ...fetchedAuthors, ]; } From 36e7d2c5144e463d5a0e01020388325e753addc3 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 16:23:01 +0400 Subject: [PATCH 7/9] Add explaining comment for the need of context 'view' --- packages/editor/src/components/post-author/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/components/post-author/constants.js b/packages/editor/src/components/post-author/constants.js index 91fb425156a802..d29995e27f4e5a 100644 --- a/packages/editor/src/components/post-author/constants.js +++ b/packages/editor/src/components/post-author/constants.js @@ -2,5 +2,5 @@ export const AUTHORS_QUERY = { who: 'authors', per_page: 50, _fields: 'id,name', - context: 'view', + context: 'view', // Allows non-admins to perform requests. }; From c505b46b0e51b4ac2ecef220e7203f8a08fc6155 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 16:47:57 +0400 Subject: [PATCH 8/9] Refactor PostAuthorCheck to use hooks --- .../src/components/post-author/check.js | 38 ++++++-------- .../src/components/post-author/test/check.js | 51 +++++++++++-------- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/packages/editor/src/components/post-author/check.js b/packages/editor/src/components/post-author/check.js index ee66a80cadea50..13a270bd0e3e6f 100644 --- a/packages/editor/src/components/post-author/check.js +++ b/packages/editor/src/components/post-author/check.js @@ -6,8 +6,7 @@ import { get } from 'lodash'; /** * WordPress dependencies */ -import { withInstanceId, compose } from '@wordpress/compose'; -import { withSelect } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; /** @@ -17,24 +16,8 @@ import PostTypeSupportCheck from '../post-type-support-check'; import { store as editorStore } from '../../store'; import { AUTHORS_QUERY } from './constants'; -export function PostAuthorCheck( { - hasAssignAuthorAction, - hasAuthors, - children, -} ) { - if ( ! hasAssignAuthorAction || ! hasAuthors ) { - return null; - } - - return ( - - { children } - - ); -} - -export default compose( [ - withSelect( ( select ) => { +export default function PostAuthorCheck( { children } ) { + const { hasAssignAuthorAction, hasAuthors } = useSelect( ( select ) => { const post = select( editorStore ).getCurrentPost(); const authors = select( coreStore ).getUsers( AUTHORS_QUERY ); return { @@ -45,6 +28,15 @@ export default compose( [ ), hasAuthors: authors?.length >= 1, }; - } ), - withInstanceId, -] )( PostAuthorCheck ); + }, [] ); + + if ( ! hasAssignAuthorAction || ! hasAuthors ) { + return null; + } + + return ( + + { children } + + ); +} diff --git a/packages/editor/src/components/post-author/test/check.js b/packages/editor/src/components/post-author/test/check.js index d3e6bd2ed229c6..70be9e1766c204 100644 --- a/packages/editor/src/components/post-author/test/check.js +++ b/packages/editor/src/components/post-author/test/check.js @@ -3,43 +3,50 @@ */ import { shallow } from 'enzyme'; +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; + /** * Internal dependencies */ -import { PostAuthorCheck } from '../check'; +import PostAuthorCheck from '../check'; + +jest.mock( '@wordpress/data/src/components/use-select', () => { + // This allows us to tweak the returned value on each test + const mock = jest.fn(); + return mock; +} ); describe( 'PostAuthorCheck', () => { it( 'should not render anything if has no authors', () => { - const wrapper = shallow( - - authors - - ); + useSelect.mockImplementation( () => ( { + hasAuthors: false, + hasAssignAuthorAction: true, + } ) ); + + const wrapper = shallow( authors ); expect( wrapper.type() ).toBe( null ); } ); it( "should not render anything if doesn't have author action", () => { - const wrapper = shallow( - - authors - - ); + useSelect.mockImplementation( () => ( { + hasAuthors: true, + hasAssignAuthorAction: false, + } ) ); + + const wrapper = shallow( authors ); expect( wrapper.type() ).toBe( null ); } ); it( 'should render control', () => { - const wrapper = shallow( - - authors - - ); + useSelect.mockImplementation( () => ( { + hasAuthors: true, + hasAssignAuthorAction: true, + } ) ); + const wrapper = shallow( authors ); expect( wrapper.type() ).not.toBe( null ); } ); } ); From 04c1addbc48a9a9e71a28865e0054e0c8fce6fb4 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 27 Jul 2021 17:07:15 +0400 Subject: [PATCH 9/9] Don't map values inside useSelect to avoid rerendering --- .../src/components/post-author/select.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/editor/src/components/post-author/select.js b/packages/editor/src/components/post-author/select.js index 115d8b1a466abe..fee86bf491f720 100644 --- a/packages/editor/src/components/post-author/select.js +++ b/packages/editor/src/components/post-author/select.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; +import { useMemo } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { decodeEntities } from '@wordpress/html-entities'; import { SelectControl } from '@wordpress/components'; @@ -16,19 +17,23 @@ import { AUTHORS_QUERY } from './constants'; function PostAuthorSelect() { const { editPost } = useDispatch( editorStore ); const { postAuthor, authors } = useSelect( ( select ) => { - const authorsFromAPI = select( coreStore ).getUsers( AUTHORS_QUERY ); - return { postAuthor: select( editorStore ).getEditedPostAttribute( 'author' ), - authors: authorsFromAPI.map( ( author ) => ( { - label: decodeEntities( author.name ), - value: author.id, - } ) ), + authors: select( coreStore ).getUsers( AUTHORS_QUERY ), }; }, [] ); + const authorOptions = useMemo( () => { + return ( authors ?? [] ).map( ( author ) => { + return { + value: author.id, + label: decodeEntities( author.name ), + }; + } ); + }, [ authors ] ); + const setAuthorId = ( value ) => { const author = Number( value ); editPost( { author } ); @@ -38,7 +43,7 @@ function PostAuthorSelect() {