From 132a01c63e79bfe4514d857f59ed6c39204381f2 Mon Sep 17 00:00:00 2001 From: Jacopo Tomasone Date: Wed, 26 Aug 2020 15:19:33 +0100 Subject: [PATCH] Post Hierarchical Terms Block (#24091) Add a new Post Hierarchical Terms Block block with a single variation that displays the current post's categories, if any, in a list of links separated by a vertical bar. The block already supports: text, background, link colors; font size, line height, and text alignment. --- lib/blocks.php | 39 ++--- packages/block-library/src/index.js | 2 + .../src/post-hierarchical-terms/block.json | 26 ++++ .../src/post-hierarchical-terms/edit.js | 143 ++++++++++++++++++ .../src/post-hierarchical-terms/index.js | 20 +++ .../src/post-hierarchical-terms/index.php | 56 +++++++ .../use-hierarchical-term-links.js | 51 +++++++ .../src/post-hierarchical-terms/variations.js | 16 ++ .../blocks/core__post-hierarchical-terms.html | 1 + .../blocks/core__post-hierarchical-terms.json | 10 ++ .../core__post-hierarchical-terms.parsed.json | 18 +++ ...e__post-hierarchical-terms.serialized.html | 1 + 12 files changed, 364 insertions(+), 19 deletions(-) create mode 100644 packages/block-library/src/post-hierarchical-terms/block.json create mode 100644 packages/block-library/src/post-hierarchical-terms/edit.js create mode 100644 packages/block-library/src/post-hierarchical-terms/index.js create mode 100644 packages/block-library/src/post-hierarchical-terms/index.php create mode 100644 packages/block-library/src/post-hierarchical-terms/use-hierarchical-term-links.js create mode 100644 packages/block-library/src/post-hierarchical-terms/variations.js create mode 100644 packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.html create mode 100644 packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.json create mode 100644 packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.parsed.json create mode 100644 packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.serialized.html diff --git a/lib/blocks.php b/lib/blocks.php index 2d0b969cc6b193..7bd7763e7b4976 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -73,25 +73,26 @@ function gutenberg_reregister_core_block_types() { $block_names = array_merge( $block_names, array( - 'post-author.php' => 'core/post-author', - 'post-comment.php' => 'core/post-comment', - 'post-comment-content.php' => 'core/post-comment-content', - 'post-comments.php' => 'core/post-comments', - 'post-comments-count.php' => 'core/post-comments-count', - 'post-comments-form.php' => 'core/post-comments-form', - 'post-content.php' => 'core/post-content', - 'post-date.php' => 'core/post-date', - 'post-excerpt.php' => 'core/post-excerpt', - 'post-featured-image.php' => 'core/post-featured-image', - 'post-tags.php' => 'core/post-tags', - 'post-title.php' => 'core/post-title', - 'query.php' => 'core/query', - 'query-loop.php' => 'core/query-loop', - 'query-pagination.php' => 'core/query-pagination', - 'site-logo.php' => 'core/site-logo', - 'site-tagline.php' => 'core/site-tagline', - 'site-title.php' => 'core/site-title', - 'template-part.php' => 'core/template-part', + 'post-author.php' => 'core/post-author', + 'post-comment.php' => 'core/post-comment', + 'post-comment-content.php' => 'core/post-comment-content', + 'post-comments.php' => 'core/post-comments', + 'post-comments-count.php' => 'core/post-comments-count', + 'post-comments-form.php' => 'core/post-comments-form', + 'post-content.php' => 'core/post-content', + 'post-date.php' => 'core/post-date', + 'post-excerpt.php' => 'core/post-excerpt', + 'post-featured-image.php' => 'core/post-featured-image', + 'post-hierarchical-terms.php' => 'core/post-hierarchical-terms', + 'post-tags.php' => 'core/post-tags', + 'post-title.php' => 'core/post-title', + 'query.php' => 'core/query', + 'query-loop.php' => 'core/query-loop', + 'query-pagination.php' => 'core/query-pagination', + 'site-logo.php' => 'core/site-logo', + 'site-tagline.php' => 'core/site-tagline', + 'site-title.php' => 'core/site-title', + 'template-part.php' => 'core/template-part', ) ); } diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index e0733e8dc12311..ac2ae4d759d564 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -83,6 +83,7 @@ import * as postCommentsForm from './post-comments-form'; import * as postDate from './post-date'; import * as postExcerpt from './post-excerpt'; import * as postFeaturedImage from './post-featured-image'; +import * as postHierarchicalTerms from './post-hierarchical-terms'; import * as postTags from './post-tags'; /** @@ -218,6 +219,7 @@ export const __experimentalRegisterExperimentalCoreBlocks = postDate, postExcerpt, postFeaturedImage, + postHierarchicalTerms, postTags, ] : [] ), diff --git a/packages/block-library/src/post-hierarchical-terms/block.json b/packages/block-library/src/post-hierarchical-terms/block.json new file mode 100644 index 00000000000000..0afec0b02857af --- /dev/null +++ b/packages/block-library/src/post-hierarchical-terms/block.json @@ -0,0 +1,26 @@ +{ + "name": "core/post-hierarchical-terms", + "category": "design", + "attributes": { + "term": { + "type": "string" + }, + "textAlign": { + "type": "string" + } + }, + "usesContext": [ + "postId", + "postType" + ], + "supports": { + "html": false, + "lightBlockWrapper": true, + "__experimentalFontSize": true, + "__experimentalColor": { + "gradients": true, + "linkColor": true + }, + "__experimentalLineHeight": true + } +} diff --git a/packages/block-library/src/post-hierarchical-terms/edit.js b/packages/block-library/src/post-hierarchical-terms/edit.js new file mode 100644 index 00000000000000..dbc4da3060844e --- /dev/null +++ b/packages/block-library/src/post-hierarchical-terms/edit.js @@ -0,0 +1,143 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { find } from 'lodash'; + +/** + * WordPress dependencies + */ +import { + AlignmentToolbar, + BlockControls, + Warning, + __experimentalBlock as Block, + __experimentalBlockVariationPicker as BlockVariationPicker, +} from '@wordpress/block-editor'; +import { Spinner } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useHierarchicalTermLinks from './use-hierarchical-term-links'; + +export default function PostHierarchicalTermsEdit( { + attributes, + clientId, + context, + name, + setAttributes, +} ) { + const { term, textAlign } = attributes; + const { postId, postType } = context; + + const { blockType, defaultVariation, variations } = useSelect( + ( select ) => { + const { + getBlockVariations, + getBlockType, + getDefaultBlockVariation, + } = select( 'core/blocks' ); + + return { + blockType: getBlockType( name ), + defaultVariation: getDefaultBlockVariation( name, 'block' ), + variations: getBlockVariations( name, 'block' ), + }; + }, + [ clientId, name ] + ); + + const selectedTerm = useSelect( + ( select ) => { + if ( ! term ) return {}; + const taxonomies = select( 'core' ).getTaxonomies( { + per_page: -1, + } ); + return ( + find( + taxonomies, + ( taxonomy ) => + taxonomy.slug === term && + taxonomy.hierarchical && + taxonomy.visibility.show_ui + ) || {} + ); + }, + [ term ] + ); + + const { + hierarchicalTermLinks, + isLoadingHierarchicalTermLinks, + } = useHierarchicalTermLinks( { + postId, + postType, + term: selectedTerm, + } ); + + const hasPost = postId && postType; + const hasHierarchicalTermLinks = + hierarchicalTermLinks && hierarchicalTermLinks.length > 0; + + if ( ! hasPost ) { + return ( + + + { __( 'Post Hierarchical Terms block: post not found.' ) } + + + ); + } + + if ( ! term ) { + return ( + + { + setAttributes( variation.attributes ); + } } + variations={ variations } + /> + + ); + } + + return ( + <> + + { + setAttributes( { textAlign: nextAlign } ); + } } + /> + + + { isLoadingHierarchicalTermLinks && } + + { hasHierarchicalTermLinks && + ! isLoadingHierarchicalTermLinks && + hierarchicalTermLinks.reduce( ( prev, curr ) => [ + prev, + ' | ', + curr, + ] ) } + + { ! isLoadingHierarchicalTermLinks && + ! hasHierarchicalTermLinks && + // eslint-disable-next-line camelcase + ( selectedTerm?.labels?.no_terms || + __( 'Term items not found.' ) ) } + + + ); +} diff --git a/packages/block-library/src/post-hierarchical-terms/index.js b/packages/block-library/src/post-hierarchical-terms/index.js new file mode 100644 index 00000000000000..b244ad54ff2875 --- /dev/null +++ b/packages/block-library/src/post-hierarchical-terms/index.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import edit from './edit'; +import variations from './variations'; + +const { name } = metadata; +export { metadata, name }; + +export const settings = { + title: __( 'Post Hierarchical Terms' ), + variations, + edit, +}; diff --git a/packages/block-library/src/post-hierarchical-terms/index.php b/packages/block-library/src/post-hierarchical-terms/index.php new file mode 100644 index 00000000000000..7f04a8da3a87bb --- /dev/null +++ b/packages/block-library/src/post-hierarchical-terms/index.php @@ -0,0 +1,56 @@ +context['postId'] ) || ! isset( $attributes['term'] ) ) { + return ''; + } + + $post_hierarchical_terms = get_the_terms( $block->context['postId'], $attributes['term'] ); + if ( empty( $post_hierarchical_terms ) ) { + return ''; + } + + $align_class_name = empty( $attributes['textAlign'] ) ? '' : ' ' . "has-text-align-{$attributes['textAlign']}"; + + $terms_links = ''; + foreach ( $post_hierarchical_terms as $term ) { + $terms_links .= sprintf( + '%2$s | ', + get_term_link( $term->term_id ), + esc_html( $term->name ) + ); + } + $terms_links = trim( $terms_links, ' | ' ); + + return sprintf( + '
%2$s
', + esc_attr( $align_class_name ), + $terms_links + ); +} + +/** + * Registers the `core/post-hierarchical-terms` block on the server. + */ +function register_block_core_post_hierarchical_terms() { + register_block_type_from_metadata( + __DIR__ . '/post-hierarchical-terms', + array( + 'render_callback' => 'render_block_core_post_hierarchical_terms', + ) + ); +} +add_action( 'init', 'register_block_core_post_hierarchical_terms' ); diff --git a/packages/block-library/src/post-hierarchical-terms/use-hierarchical-term-links.js b/packages/block-library/src/post-hierarchical-terms/use-hierarchical-term-links.js new file mode 100644 index 00000000000000..abf2af7426cd3f --- /dev/null +++ b/packages/block-library/src/post-hierarchical-terms/use-hierarchical-term-links.js @@ -0,0 +1,51 @@ +/** + * External dependencies + */ +import { map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useEntityProp } from '@wordpress/core-data'; +import { useSelect } from '@wordpress/data'; + +export default function useHierarchicalTermLinks( { postId, postType, term } ) { + const { rest_base: restBase, slug } = term; + + const [ hierarchicalTermItems ] = useEntityProp( + 'postType', + postType, + restBase, + postId + ); + + const { hierarchicalTermLinks, isLoadingHierarchicalTermLinks } = useSelect( + ( select ) => { + const { getEntityRecord } = select( 'core' ); + + let loaded = true; + + const links = map( hierarchicalTermItems, ( itemId ) => { + const item = getEntityRecord( 'taxonomy', slug, itemId ); + + if ( ! item ) { + return ( loaded = false ); + } + + return ( + + { item.name } + + ); + } ); + + return { + hierarchicalTermLinks: links, + isLoadingHierarchicalTermLinks: ! loaded, + }; + }, + [ hierarchicalTermItems ] + ); + + return { hierarchicalTermLinks, isLoadingHierarchicalTermLinks }; +} diff --git a/packages/block-library/src/post-hierarchical-terms/variations.js b/packages/block-library/src/post-hierarchical-terms/variations.js new file mode 100644 index 00000000000000..8ff6dcc07f910f --- /dev/null +++ b/packages/block-library/src/post-hierarchical-terms/variations.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +const variations = [ + { + name: 'category', + title: __( 'Post Categories' ), + icon: 'category', + is_default: true, + attributes: { term: 'category' }, + }, +]; + +export default variations; diff --git a/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.html b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.html new file mode 100644 index 00000000000000..7614fa26add90c --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.html @@ -0,0 +1 @@ + diff --git a/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.json b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.json new file mode 100644 index 00000000000000..fd864d95242e37 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.json @@ -0,0 +1,10 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/post-hierarchical-terms", + "isValid": true, + "attributes": {}, + "innerBlocks": [], + "originalContent": "" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.parsed.json b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.parsed.json new file mode 100644 index 00000000000000..468496fa95bc65 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.parsed.json @@ -0,0 +1,18 @@ +[ + { + "blockName": "core/post-hierarchical-terms", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.serialized.html b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.serialized.html new file mode 100644 index 00000000000000..7614fa26add90c --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__post-hierarchical-terms.serialized.html @@ -0,0 +1 @@ +