Skip to content
12 changes: 8 additions & 4 deletions components/autocomplete/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,9 @@ export class Autocomplete extends Component {
}

select( option ) {
const { onReplace } = this.props;
const { onReplace, onSelect } = this.props;
const { open, range, query } = this.state;
const { getOptionCompletion } = open || {};

this.reset();

if ( getOptionCompletion ) {
Expand All @@ -258,13 +257,18 @@ export class Autocomplete extends Component {
this.insertCompletion( range, value );
} else if ( 'backcompat' === action ) {
// NOTE: This block should be removed once we no longer support the old completer interface.
const onSelect = value;
const onSelectValue = value;
const deprecatedOptionObject = option.value;
const selectionResult = onSelect( deprecatedOptionObject.value, range, query );
const selectionResult = onSelectValue( deprecatedOptionObject.value, range, query );
if ( selectionResult !== undefined ) {
this.insertCompletion( range, selectionResult );
}
}

// Call the Autocomplete component's onSelect method if available.
if ( onSelect ) {
onSelect( completion );
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions edit-post/components/sidebar/document-sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { __ } from '@wordpress/i18n';
* Internal Dependencies
*/
import PostStatus from '../post-status';
import PostAuthor from '../post-author';
import PostExcerpt from '../post-excerpt';
import PostTaxonomies from '../post-taxonomies';
import FeaturedImage from '../featured-image';
Expand All @@ -28,6 +29,7 @@ const DocumentSidebar = () => (
<SettingsHeader sidebarName={ SIDEBAR_NAME } />
<Panel>
<PostStatus />
<PostAuthor />
<LastRevision />
<PostTaxonomies />
<FeaturedImage />
Expand Down
36 changes: 36 additions & 0 deletions edit-post/components/sidebar/post-author/check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* External dependencies
*/
import { get } from 'lodash';

/**
* WordPress dependencies
*/
import { withInstanceId } from '@wordpress/components';
import { compose } from '@wordpress/element';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import PostTypeSupportCheck from '../../../../editor/components/post-type-support-check';

export function PostAuthorCheck( { hasAssignAuthorAction, authors, children } ) {
if ( ! hasAssignAuthorAction || authors.length < 1 ) {
return null;
}

return <PostTypeSupportCheck supportKeys="author">{ children }</PostTypeSupportCheck>;
}

export default compose( [
withSelect( ( select ) => {
const post = select( 'core/editor' ).getCurrentPost();
return {
hasAssignAuthorAction: get( post, [ '_links', 'wp:action-assign-author' ], false ),
postType: select( 'core/editor' ).getCurrentPostType(),
authors: select( 'core' ).getAuthors(),
};
} ),
withInstanceId,
] )( PostAuthorCheck );
100 changes: 89 additions & 11 deletions edit-post/components/sidebar/post-author/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,100 @@
/**
* WordPress dependencies
*/
import { PanelRow } from '@wordpress/components';
import { PostAuthor as PostAuthorForm, PostAuthorCheck } from '@wordpress/editor';
import { __ } from '@wordpress/i18n';
import { PanelBody, PanelRow, withInstanceId } from '@wordpress/components';
import { Component, compose } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { authorAutocompleter } from '../../../../editor/components/autocompleters';
import RichText from '../../../../editor/components/rich-text';

/**
* Internal dependencies
*/
import PostAuthorCheck from './check';
import './style.scss';
const PANEL_NAME = 'author-panel';

export function PostAuthor() {
return (
<PostAuthorCheck>
<PanelRow>
<PostAuthorForm />
</PanelRow>
</PostAuthorCheck>
);
export class PostAuthor extends Component {
constructor() {
super( ...arguments );
this.onSelect = this.onSelect.bind( this );
this.onFocus = this.onFocus.bind( this );
this.state = {
theAuthor: false,
};
const { postAuthor } = this.props;
wp.apiRequest( { path: '/wp/v2/users/' + postAuthor + '?context=edit' } )
.then( ( response ) => {
this.setState( { theAuthor: response } );
} );
}

// When an author is selected, set the post author.
onSelect( value ) {
if ( ! value ) {
return;
}
const { onUpdateAuthor } = this.props;
onUpdateAuthor( Number( value.id ) );
}

onFocus( editor ) {
if ( ! editor.dom ) {
return;
}
const range = editor.dom.createRng();
range.selectNodeContents( editor.getBody() );
editor.selection.setRng( range );
}

render() {
const { isOpened, onTogglePanel } = this.props;
const theAuthor = this.state.theAuthor;

/* eslint-disable jsx-a11y/no-onchange */
return (
<PostAuthorCheck>
<PanelBody title={ __( 'Author' ) } opened={ isOpened } onToggle={ onTogglePanel }>
<PanelRow>
<div className="components-form-token-field">
<div className="components-form-token-field__input-container">
<RichText
tagName="p"
className="editor-post-author__select wp-block-paragraph"
value={ theAuthor ? theAuthor.name : '' }
aria-autocomplete="list"
onSelect={ this.onSelect }
onChange={ () => {} }
autocompleters={ [ authorAutocompleter ] }
onFocus={ this.onFocus }
/>
</div>
</div>
</PanelRow>
</PanelBody>
</PostAuthorCheck>
);
/* eslint-enable jsx-a11y/no-onchange */
}
}

export default PostAuthor;
export default compose( [
withSelect( ( select ) => {
return {
postAuthor: select( 'core/editor' ).getEditedPostAttribute( 'author' ),
authors: select( 'core' ).getAuthors(),
isOpened: select( 'core/edit-post' ).isEditorSidebarPanelOpened( PANEL_NAME ),

};
} ),
withDispatch( ( dispatch ) => ( {
onUpdateAuthor( author ) {
dispatch( 'core/editor' ).editPost( { author } );
},
onTogglePanel() {
return dispatch( 'core/edit-post' ).toggleGeneralSidebarEditorPanel( PANEL_NAME );
},
} ) ),
withInstanceId,
] )( PostAuthor );
11 changes: 11 additions & 0 deletions edit-post/components/sidebar/post-author/style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
.editor-post-author__select {
margin: -5px 0;
border: none;
width: 150px;
padding: 4px;
display: inline-block;
width: auto;
max-width: 100%;
line-height: 24px;
background: inherit;
font-size: 13px;
color: #23282d;
box-shadow: none;
}
2 changes: 0 additions & 2 deletions edit-post/components/sidebar/post-status/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import PostVisibility from '../post-visibility';
import PostTrash from '../post-trash';
import PostSchedule from '../post-schedule';
import PostSticky from '../post-sticky';
import PostAuthor from '../post-author';
import PostFormat from '../post-format';
import PostPendingStatus from '../post-pending-status';
import PluginPostStatusInfo from '../plugin-post-status-info';
Expand All @@ -32,7 +31,6 @@ function PostStatus( { isOpened, onTogglePanel } ) {
<PostFormat />
<PostSticky />
<PostPendingStatus />
<PostAuthor />
<PluginPostStatusInfo.Slot />
<PostTrash />
</PanelBody>
Expand Down
37 changes: 37 additions & 0 deletions editor/components/autocompleters/author.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* An author autocompleter.
*
* @type {Completer}
*/
export default {
name: 'users',
className: 'blocks-autocompleters__user',
triggerPrefix: '',
options( search ) {
let payload = '';
if ( search ) {
payload = '?search=' + encodeURIComponent( search );
}
return wp.apiRequest( { path: '/wp/v2/users' + payload } );
},
isDebounced: true,
getOptionKeywords( user ) {
return [ user.slug, user.name ];
},
getOptionLabel( user ) {
return [
<span key="name" className="blocks-autocompleters__user-name">{ user.name }</span>,
];
},
allowNode() {
return true;
},

getOptionCompletion( user ) {
return {
action: 'insert-at-caret',
value: `${ user.name }`,
id: user.id,
};
},
};
1 change: 1 addition & 0 deletions editor/components/autocompleters/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ import './style.scss';

export { default as blockAutocompleter } from './block';
export { default as userAutocompleter } from './user';
export { default as authorAutocompleter } from './author';
41 changes: 26 additions & 15 deletions editor/components/post-author/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { __ } from '@wordpress/i18n';
import { withInstanceId } from '@wordpress/components';
import { Component, compose } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { authorAutocompleter } from '../../../editor/components/autocompleters';
import RichText from '../../../editor/components/rich-text';

/**
* Internal dependencies
Expand All @@ -14,36 +16,45 @@ import PostAuthorCheck from './check';
export class PostAuthor extends Component {
constructor() {
super( ...arguments );

this.setAuthorId = this.setAuthorId.bind( this );
this.state = {
theAuthor: false,
};
const { postAuthor } = this.props;
wp.apiRequest( { path: '/wp/v2/users/' + postAuthor + '?context=edit' } )
.then( ( response ) => {
this.setState( { theAuthor: response } );
} );
}

setAuthorId( event ) {
// When an author is selected, set the post author.
setAuthorId( value ) {
if ( ! value ) {
return;
}
const { onUpdateAuthor } = this.props;
const { value } = event.target;
onUpdateAuthor( Number( value ) );
onUpdateAuthor( Number( value.id ) );
}

render() {
const { postAuthor, instanceId, authors } = this.props;
const { instanceId } = this.props;
const selectId = 'post-author-selector-' + instanceId;
const theAuthor = this.state.theAuthor;

// Disable reason: A select with an onchange throws a warning

/* eslint-disable jsx-a11y/no-onchange */
return (
<PostAuthorCheck>
<label htmlFor={ selectId }>{ __( 'Author' ) }</label>
<select
id={ selectId }
value={ postAuthor }
<RichText
tagName="p"
className="editor-post-author__select wp-block-paragraph"
value={ theAuthor ? theAuthor.name : '' }
aria-autocomplete="list"
onChange={ this.setAuthorId }
className="editor-post-author__select"
>
{ authors.map( ( author ) => (
<option key={ author.id } value={ author.id }>{ author.name }</option>
) ) }
</select>
autocompleters={ [ authorAutocompleter ] }
/>

</PostAuthorCheck>
);
/* eslint-enable jsx-a11y/no-onchange */
Expand Down
7 changes: 6 additions & 1 deletion editor/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ export class RichText extends Component {
if ( this.props.setFocusedElement ) {
this.props.setFocusedElement( this.props.instanceId );
}

if ( this.props.onFocus ) {
this.props.onFocus( this.editor );
}
}

/**
Expand Down Expand Up @@ -858,6 +862,7 @@ export class RichText extends Component {
formatters,
autocompleters,
format,
onSelect,
} = this.props;

const ariaProps = { ...pickAriaProps( this.props ), 'aria-multiline': !! MultilineTag };
Expand Down Expand Up @@ -895,7 +900,7 @@ export class RichText extends Component {
{ formatToolbar }
</div>
) }
<Autocomplete onReplace={ this.props.onReplace } completers={ autocompleters }>
<Autocomplete onReplace={ this.props.onReplace } completers={ autocompleters } onSelect={ onSelect }>
{ ( { isExpanded, listBoxId, activeId } ) => (
<Fragment>
<TinyMCE
Expand Down