diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js
index decc8953ec9c55..d7a7290df1d8be 100644
--- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js
+++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js
@@ -18,7 +18,7 @@ import {
*/
import { __, _x, sprintf } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
-import { FormTokenField, withFilters } from '@wordpress/components';
+import { FormTokenField, withFilters, Button } from '@wordpress/components';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import apiFetch from '@wordpress/api-fetch';
@@ -28,11 +28,12 @@ import { addQueryArgs } from '@wordpress/url';
* Module constants
*/
const MAX_TERMS_SUGGESTIONS = 20;
+const MAX_RELATED_TERMS_SUGGESTIONS = 20;
const DEFAULT_QUERY = {
per_page: MAX_TERMS_SUGGESTIONS,
orderby: 'count',
order: 'desc',
- _fields: 'id,name',
+ _fields: 'id,name,count',
};
// Lodash unescape function handles ' but not ' which may be return in some API requests.
@@ -76,10 +77,15 @@ class FlatTermSelector extends Component {
this.onChange = this.onChange.bind( this );
this.searchTerms = throttle( this.searchTerms.bind( this ), 500 );
this.findOrCreateTerm = this.findOrCreateTerm.bind( this );
+ this.appendTerm = this.appendTerm.bind( this );
+ this.toggleRelatedTerms = this.toggleRelatedTerms.bind( this );
+ this.fetchRelatedTerms = this.fetchRelatedTerms.bind( this );
this.state = {
loading: ! isEmpty( this.props.terms ),
availableTerms: [],
selectedTerms: [],
+ relatedTerms: [],
+ areRelatedTermsHidden: true,
};
}
@@ -89,20 +95,31 @@ class FlatTermSelector extends Component {
include: this.props.terms.join( ',' ),
per_page: -1,
} );
- this.initRequest.then(
- () => {
+ } else {
+ this.initRequest = Promise.resolve( [] );
+ }
+
+ this.initRequest.then(
+ ( response ) => {
+ this.relatedTermsRequest = this.fetchRelatedTerms( {
+ order: 'desc',
+ orderby: 'count',
+ per_page: MAX_RELATED_TERMS_SUGGESTIONS + response.length,
+ } );
+
+ this.relatedTermsRequest.then( () => {
this.setState( { loading: false } );
- },
- ( xhr ) => {
- if ( xhr.statusText === 'abort' ) {
- return;
- }
- this.setState( {
- loading: false,
- } );
+ } );
+ },
+ ( xhr ) => {
+ if ( xhr.statusText === 'abort' ) {
+ return;
}
- );
- }
+ this.setState( {
+ loading: false,
+ } );
+ }
+ );
}
componentWillUnmount() {
@@ -141,6 +158,37 @@ class FlatTermSelector extends Component {
return request;
}
+ fetchRelatedTerms( params = {} ) {
+ const { taxonomy } = this.props;
+ const query = { ...DEFAULT_QUERY, ...params };
+ const request = apiFetch( {
+ path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, query ),
+ } );
+ request.then( unescapeTerms ).then( ( terms ) => {
+ let newRelatedTerms = terms.filter(
+ ( term ) =>
+ ! find(
+ this.state.selectedTerms,
+ ( updatedTerm ) => updatedTerm === term.name
+ )
+ );
+ const newAvailableTerms = this.state.availableTerms.concat(
+ newRelatedTerms
+ );
+
+ newRelatedTerms = newRelatedTerms.splice(
+ 0,
+ MAX_RELATED_TERMS_SUGGESTIONS
+ );
+ this.setState( {
+ relatedTerms: newRelatedTerms,
+ availableTerms: newAvailableTerms,
+ } );
+ } );
+
+ return request;
+ }
+
updateSelectedTerms( terms = [] ) {
const selectedTerms = terms.reduce( ( accumulator, termId ) => {
const termObject = find(
@@ -233,6 +281,32 @@ class FlatTermSelector extends Component {
}
}
+ appendTerm( newTerm ) {
+ if (
+ find( this.state.selectedTerms, ( selectedTerm ) => {
+ isSameTermName( selectedTerm, newTerm.name );
+ } )
+ )
+ return;
+
+ const newSelectedTerms = this.state.selectedTerms.concat( [
+ newTerm.name,
+ ] );
+
+ this.onChange( newSelectedTerms );
+ }
+
+ toggleRelatedTerms() {
+ this.setState( ( state ) => ( {
+ areRelatedTermsHidden: ! state.areRelatedTermsHidden,
+ } ) );
+ }
+
+ // Screen Reader is an extension for modern browsers which helps visually handicapped users to interact with websites.
+ getTermScreenReaderString( tag, count ) {
+ return `${ tag } (${ count } items)`;
+ }
+
render() {
const { slug, taxonomy, hasAssignAction } = this.props;
@@ -252,6 +326,7 @@ class FlatTermSelector extends Component {
[ 'labels', 'singular_name' ],
slug === 'post_tag' ? __( 'Tag' ) : __( 'Term' )
);
+ const name = get( taxonomy, [ 'labels', 'name' ] );
const termAddedLabel = sprintf(
/* translators: %s: term name. */
_x( '%s added', 'term' ),
@@ -267,22 +342,66 @@ class FlatTermSelector extends Component {
_x( 'Remove %s', 'term' ),
singularName
);
+ const tagsButtonLabel = sprintf(
+ /* translators: %s: term name. */
+ _x( 'Choose from the most used %s', 'terms' ),
+ name
+ );
return (
-