Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eriktdesign add/tags in latest posts block #20785

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/block-library/src/latest-posts/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
"categories": {
"type": "array"
},
"tags": {
"type": "array"
},
"postsToShow": {
"type": "number",
"default": 5
Expand Down
156 changes: 153 additions & 3 deletions packages/block-library/src/latest-posts/edit.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
/**
* External dependencies
*/
import { get, invoke, isUndefined, pickBy } from 'lodash';
import {
compact,
concat,
find,
get,
invoke,
isUndefined,
pickBy,
throttle,
map,
unescape as unescapeString,
} from 'lodash';
import classnames from 'classnames';

/**
Expand Down Expand Up @@ -47,16 +58,142 @@ import {
const CATEGORIES_LIST_QUERY = {
per_page: -1,
};
const TAG_LIST_QUERY = {
per_page: -1,
orderby: 'count',
order: 'desc',
_fields: 'id,name',
};

const isSameTermName = ( termA, termB ) =>
termA.toLowerCase() === termB.toLowerCase();

/**
* Returns a term object with name unescaped.
* The unescape of the name property is done using lodash unescape function.
*
* @param {Object} term The term object to unescape.
*
* @return {Object} Term object with name property unescaped.
*/
const unescapeTerm = ( term ) => {
return {
...term,
name: unescapeString( term.name ),
};
};

/**
* Returns an array of term objects with names unescaped.
* The unescape of each term is performed using the unescapeTerm function.
*
* @param {Object[]} terms Array of term objects to unescape.
*
* @return {Object[]} Array of term objects unescaped.
*/
const unescapeTerms = ( terms ) => {
return map( terms, unescapeTerm );
};

class LatestPostsEdit extends Component {
constructor() {
super( ...arguments );
this.searchTerms = throttle( this.searchTerms.bind( this ), 500 );
this.updateTags = this.updateTags.bind( this );
this.tokenizeTags = this.tokenizeTags.bind( this );
this.getTagsDetails = this.getTagsDetails.bind( this );
this.getCategoriesDetails = this.getCategoriesDetails.bind( this );
this.state = {
categoriesList: [],
tagsList: [],
tokenizedTags: [],
suggestedTags: [],
};
}

componentDidMount() {
tokenizeTags( tagsList ) {
let tokenizedTags = [];
if ( tagsList.length > 0 ) {
tokenizedTags = tagsList.reduce( ( accumulator, tag ) => {
accumulator.push( tag.name );
return accumulator;
}, [] );
}
this.setState( { tokenizedTags } );
}

updateTags( termNames ) {
const { setAttributes } = this.props;
const newTags = compact(
concat(
termNames.map( ( termName ) => {
return find( this.state.suggestedTags, ( result ) => {
return isSameTermName( result.name, termName );
} );
} ),
termNames.map( ( termName ) => {
return find( this.state.tagsList, ( result ) => {
return isSameTermName( result.name, termName );
} );
} )
)
);
const tagsIds = newTags.map( ( { id } ) => id );
setAttributes( { tags: tagsIds } );
this.getTagsDetails( tagsIds );
this.tokenizeTags( newTags );
}

searchTerms( search = '' ) {
invoke( this.searchRequest, [ 'abort' ] );
this.searchRequest = this.fetchTerms( { search } );
}

fetchTerms( params = {} ) {
const query = { ...TAG_LIST_QUERY, ...params };
const request = apiFetch( {
path: addQueryArgs( `/wp/v2/tags`, query ),
} );
request.then( unescapeTerms ).then( ( terms ) => {
this.setState( ( state ) => ( {
suggestedTags: terms.filter(
( term ) =>
! find( state.tagsList, ( tag ) => tag.id === term.id )
),
} ) );
} );

return request;
}

getTagsDetails( newTags = false ) {
this.isStillMounted = true;
let { tags } = this.props.attributes;
if ( newTags ) {
tags = newTags;
}
if ( tags && tags.length > 0 ) {
this.fetchRequest = apiFetch( {
path: addQueryArgs( `/wp/v2/tags`, {
include: tags.join( ',' ),
} ),
} )
.then( ( tagsList ) => {
if ( this.isStillMounted ) {
this.setState( { tagsList } );
this.tokenizeTags( tagsList );
}
} )
.catch( () => {
if ( this.isStillMounted ) {
this.setState( { tagsList: [] } );
this.tokenizeTags( [] );
}
} );
}
}

getCategoriesDetails() {
this.isStillMounted = true;
this.fetchRequest = apiFetch( {
path: addQueryArgs( `/wp/v2/categories`, CATEGORIES_LIST_QUERY ),
Expand All @@ -73,6 +210,11 @@ class LatestPostsEdit extends Component {
} );
}

componentDidMount() {
this.getCategoriesDetails();
this.getTagsDetails();
}

componentWillUnmount() {
this.isStillMounted = false;
}
Expand All @@ -86,7 +228,7 @@ class LatestPostsEdit extends Component {
defaultImageWidth,
defaultImageHeight,
} = this.props;
const { categoriesList } = this.state;
const { categoriesList, tokenizedTags, suggestedTags } = this.state;
const {
displayFeaturedImage,
displayPostContentRadio,
Expand Down Expand Up @@ -251,6 +393,12 @@ class LatestPostsEdit extends Component {
categorySuggestions={ categorySuggestions }
onCategoryChange={ selectCategories }
selectedCategories={ categories }
selectedTags={ tokenizedTags }
onTagsChange={ this.updateTags }
onTagInputChage={ this.searchTerms }
suggestedTags={ suggestedTags.map(
( tag ) => tag.name
) }
/>

{ postLayout === 'grid' && (
Expand Down Expand Up @@ -440,6 +588,7 @@ export default withSelect( ( select, props ) => {
order,
orderBy,
categories,
tags,
} = props.attributes;
const { getEntityRecords, getMedia } = select( 'core' );
const { getSettings } = select( 'core/block-editor' );
Expand All @@ -451,6 +600,7 @@ export default withSelect( ( select, props ) => {
const latestPostsQuery = pickBy(
{
categories: catIds,
tags,
order,
orderby: orderBy,
per_page: postsToShow,
Expand Down
20 changes: 18 additions & 2 deletions packages/components/src/query-controls/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@ import { RangeControl, SelectControl, FormTokenField } from '../';
const DEFAULT_MIN_ITEMS = 1;
const DEFAULT_MAX_ITEMS = 100;
const MAX_CATEGORIES_SUGGESTIONS = 20;
const MAX_TERMS_SUGGESTIONS = 20;

export default function QueryControls( {
categorySuggestions,
selectedCategories,
onCategoryChange,
selectedTags,
suggestedTags,
onTagInputChage,
tagsLoading,
onTagsChange,
numberOfItems,
order,
orderBy,
maxItems = DEFAULT_MAX_ITEMS,
minItems = DEFAULT_MIN_ITEMS,
onCategoryChange,
onNumberOfItemsChange,
onOrderChange,
onOrderByChange,
Expand Down Expand Up @@ -77,7 +83,17 @@ export default function QueryControls( {
maxSuggestions={ MAX_CATEGORIES_SUGGESTIONS }
/>
),

onTagsChange && (
<FormTokenField
value={ selectedTags }
suggestions={ suggestedTags }
onChange={ onTagsChange }
onInputChange={ onTagInputChage }
maxSuggestions={ MAX_TERMS_SUGGESTIONS }
disabled={ tagsLoading }
label={ __( 'Filter by tags' ) }
/>
),
onNumberOfItemsChange && (
<RangeControl
key="query-controls-range-control"
Expand Down