Skip to content

Commit

Permalink
Add post author selector enhanced with ComboboxControl (#23237)
Browse files Browse the repository at this point in the history
Co-authored-by: epiqueras <epiquerass@gmail.com>
Co-authored-by: Enrique Piqueras <epiqueras@users.noreply.github.com>
Co-authored-by: Noah Allen <noahtallen@gmail.com>
Co-authored-by: Riad Benguella <benguella@gmail.com>
Co-authored-by: jasmussen <joen@automattic.com>
  • Loading branch information
6 people committed Sep 28, 2020
1 parent 6325f76 commit 78dde89
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 144 deletions.
4 changes: 2 additions & 2 deletions packages/block-library/src/post-author/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function PostAuthorEdit( { isSelected, context, attributes, setAttributes } ) {

const { authorId, authorDetails, authors } = useSelect(
( select ) => {
const { getEditedEntityRecord, getUser, getAuthors } = select(
const { getEditedEntityRecord, getUser, getUsers } = select(
'core'
);
const _authorId = getEditedEntityRecord(
Expand All @@ -35,7 +35,7 @@ function PostAuthorEdit( { isSelected, context, attributes, setAttributes } ) {
return {
authorId: _authorId,
authorDetails: _authorId ? getUser( _authorId ) : null,
authors: getAuthors(),
authors: getUsers( { who: 'authors' } ),
};
},
[ postType, postId ]
Expand Down
28 changes: 7 additions & 21 deletions packages/components/src/combobox-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function ComboboxControl( {
label,
options,
onChange,
onInputChange: onInputChangeProp = () => {},
onFilterValueChange,
hideLabelFromVision,
help,
messages = {
Expand All @@ -31,11 +31,10 @@ function ComboboxControl( {
const [ isExpanded, setIsExpanded ] = useState( false );
const [ inputValue, setInputValue ] = useState( '' );
const inputContainer = useRef();
const currentOption = options.find( ( option ) => option.value === value );
const currentLabel = currentOption?.label ?? '';

const matchingSuggestions = useMemo( () => {
if ( ! inputValue || inputValue.length === 0 ) {
return options.filter( ( option ) => option.value !== value );
}
const startsWithMatch = [];
const containsMatch = [];
const match = inputValue.toLocaleLowerCase();
Expand All @@ -55,7 +54,7 @@ function ComboboxControl( {
onChange( newSelectedSuggestion.value );
speak( messages.selected, 'assertive' );
setSelectedSuggestion( newSelectedSuggestion );
setInputValue( selectedSuggestion.label );
setInputValue( '' );
setIsExpanded( false );
};

Expand Down Expand Up @@ -109,33 +108,20 @@ function ComboboxControl( {
// TODO: TokenInput should preferably forward ref
inputContainer.current.input.focus();
setIsExpanded( true );
onFilterValueChange( '' );
};

const onBlur = () => {
const currentOption = options.find(
( option ) => option.value === value
);
setInputValue( currentOption?.label ?? '' );
setIsExpanded( false );
};

const onInputChange = ( event ) => {
const text = event.value;
setInputValue( text );
onInputChangeProp( text );
onFilterValueChange( text );
setIsExpanded( true );
};

// Reset the value on change
useEffect( () => {
if ( matchingSuggestions.indexOf( selectedSuggestion ) === -1 ) {
setSelectedSuggestion( null );
}
if ( ! inputValue || matchingSuggestions.length === 0 ) {
onChange( null );
}
}, [ matchingSuggestions, inputValue, value ] );

// Announcements
useEffect( () => {
const hasMatchingSuggestions = matchingSuggestions.length > 0;
Expand Down Expand Up @@ -179,7 +165,7 @@ function ComboboxControl( {
className="components-combobox-control__input"
instanceId={ instanceId }
ref={ inputContainer }
value={ inputValue }
value={ isExpanded ? inputValue : currentLabel }
onBlur={ onBlur }
isExpanded={ isExpanded }
selectedSuggestionIndex={ matchingSuggestions.indexOf(
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/combobox-control/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ function ComboboxControlWithState() {
onChange={ setValue }
label="Select a country"
options={ filteredOptions }
onInputChange={ ( filter ) =>
onFilterValueChange={ ( filter ) =>
setFilteredOptions(
countries
.filter( ( country ) =>
Expand Down
6 changes: 5 additions & 1 deletion packages/components/src/combobox-control/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
width: 100%;
}

.components-combobox-control__input {
input.components-combobox-control__input[type="text"] {
width: 100%;
border: none;
box-shadow: none;
padding: 2px;
margin: 0;
line-height: inherit;
min-height: auto;

&:focus {
outline: none;
Expand Down
3 changes: 3 additions & 0 deletions packages/core-data/src/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export const isRequestingEmbedPreview = createRegistrySelector(
* @return {Array} Authors list.
*/
export function getAuthors( state ) {
deprecated( "select( 'core' ).getAuthors()", {
alternative: "select( 'core' ).getUsers({ who: 'authors' })",
} );
return getUserQueryResults( state, 'authors' );
}

Expand Down
4 changes: 2 additions & 2 deletions packages/editor/src/components/post-author/check.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function PostAuthorCheck( {
authors,
children,
} ) {
if ( ! hasAssignAuthorAction || authors.length < 2 ) {
if ( ! hasAssignAuthorAction || ! authors || authors.length < 2 ) {
return null;
}

Expand All @@ -40,7 +40,7 @@ export default compose( [
false
),
postType: select( 'core/editor' ).getCurrentPostType(),
authors: select( 'core' ).getAuthors(),
authors: select( 'core' ).getUsers( { who: 'authors' } ),
};
} ),
withInstanceId,
Expand Down
147 changes: 94 additions & 53 deletions packages/editor/src/components/post-author/index.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,112 @@
/**
* External dependencies
*/
import { debounce } from 'lodash';

/**
* WordPress dependencies
*/
import { useState, useMemo, useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { withInstanceId, compose } from '@wordpress/compose';
import { Component } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { decodeEntities } from '@wordpress/html-entities';
import { ComboboxControl } from '@wordpress/components';

/**
* Internal dependencies
*/
import PostAuthorCheck from './check';

export class PostAuthor extends Component {
constructor() {
super( ...arguments );
function PostAuthor() {
const [ fieldValue, setFieldValue ] = useState();

this.setAuthorId = this.setAuthorId.bind( this );
}
const { authorId, isLoading, authors, postAuthor } = useSelect(
( select ) => {
const { getUser, getUsers, isResolving } = select( 'core' );
const { getEditedPostAttribute } = select( 'core/editor' );
const author = getUser( getEditedPostAttribute( 'author' ) );
const query =
! fieldValue || '' === fieldValue ? {} : { search: fieldValue };
return {
authorId: getEditedPostAttribute( 'author' ),
postAuthor: author,
authors: getUsers( { who: 'authors', ...query } ),
isLoading: isResolving( 'core', 'getUsers', [
{ search: fieldValue, who: 'authors' },
] ),
};
},
[ fieldValue ]
);
const { editPost } = useDispatch( 'core/editor' );

setAuthorId( event ) {
const { onUpdateAuthor } = this.props;
const { value } = event.target;
onUpdateAuthor( Number( value ) );
}
const authorOptions = useMemo( () => {
const fetchedAuthors = ( authors ?? [] ).map( ( author ) => {
return {
value: author.id,
label: author.name,
};
} );

// Ensure the current author is included in the dropdown list.
const foundAuthor = fetchedAuthors.findIndex(
( { value } ) => postAuthor?.id === value
);

render() {
const { postAuthor, instanceId, authors } = this.props;
const selectId = 'post-author-selector-' + instanceId;
if ( foundAuthor < 0 && postAuthor ) {
return [
{ value: postAuthor.id, label: postAuthor.name },
...fetchedAuthors,
];
}

// Disable reason: A select with an onchange throws a warning
return fetchedAuthors;
}, [ authors, postAuthor ] );

/* eslint-disable jsx-a11y/no-onchange */
return (
<PostAuthorCheck>
<label htmlFor={ selectId }>{ __( 'Author' ) }</label>
<select
id={ selectId }
value={ postAuthor }
onChange={ this.setAuthorId }
className="editor-post-author__select"
>
{ authors.map( ( author ) => (
<option key={ author.id } value={ author.id }>
{ decodeEntities( author.name ) }
</option>
) ) }
</select>
</PostAuthorCheck>
);
/* eslint-enable jsx-a11y/no-onchange */
// Initializes the post author properly
// Also ensures external changes are reflected.
useEffect( () => {
if ( postAuthor ) {
setFieldValue( postAuthor.name );
}
}, [ postAuthor ] );

/**
* Handle author selection.
*
* @param {number} postAuthorId The selected Author.
*/
const handleSelect = ( postAuthorId ) => {
if ( ! postAuthorId ) {
return;
}
editPost( { author: postAuthorId } );
};

/**
* Handle user input.
*
* @param {string} inputValue The current value of the input field.
*/
const handleKeydown = ( inputValue ) => {
setFieldValue( inputValue );
};

if ( ! postAuthor ) {
return null;
}

return (
<PostAuthorCheck>
<ComboboxControl
label={ __( 'Author' ) }
options={ authorOptions }
value={ authorId }
onFilterValueChange={ debounce( handleKeydown, 300 ) }
onChange={ handleSelect }
isLoading={ isLoading }
/>
</PostAuthorCheck>
);
}

export default compose( [
withSelect( ( select ) => {
return {
postAuthor: select( 'core/editor' ).getEditedPostAttribute(
'author'
),
authors: select( 'core' ).getAuthors(),
};
} ),
withDispatch( ( dispatch ) => ( {
onUpdateAuthor( author ) {
dispatch( 'core/editor' ).editPost( { author } );
},
} ) ),
withInstanceId,
] )( PostAuthor );
export default PostAuthor;
64 changes: 0 additions & 64 deletions packages/editor/src/components/post-author/test/index.js

This file was deleted.

0 comments on commit 78dde89

Please sign in to comment.