Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0d1af90
move edit component and controls into edit folder
cr0ybot Oct 7, 2025
e3c03e7
remove hierarchical layout control
cr0ybot Oct 7, 2025
d4c73cc
remove hierarchical layout
cr0ybot Oct 7, 2025
ce4c9f0
Revert "remove hierarchical layout control"
cr0ybot Oct 7, 2025
7928067
fix max terms control all value
cr0ybot Oct 7, 2025
cf83912
break up "terms to show" into hierarchical and inherit controls, remo…
cr0ybot Oct 7, 2025
4eca79a
only include parent in query if not 0
cr0ybot Oct 7, 2025
9dccaf1
handle parent and hierarchical as mutually exclusive
cr0ybot Oct 7, 2025
f043e52
turn off hierarchical with inherit
cr0ybot Oct 7, 2025
bf37d81
Merge branch 'master' into update/terms-query-cleanup
cr0ybot Oct 7, 2025
a50a051
use setAttributes updater function
cr0ybot Oct 7, 2025
fb73926
move usePublicTaxonomies to shared utils file
cr0ybot Oct 7, 2025
9e29e72
taxonomy control single responsibility
cr0ybot Oct 7, 2025
b450519
order control single responsibility
cr0ybot Oct 7, 2025
7d5dcbb
empty terms control single responsibility
cr0ybot Oct 7, 2025
abf4d90
pass onChange directly when possible
cr0ybot Oct 7, 2025
d568af4
inherit control single responsibility
cr0ybot Oct 7, 2025
39f54af
hierarchy control single responsibility
cr0ybot Oct 7, 2025
1cd1049
rename hierarchy control to nested terms control to match empty terms…
cr0ybot Oct 7, 2025
904e70e
max terms control single responsibility
cr0ybot Oct 7, 2025
2f0af12
don't repeat labels
cr0ybot Oct 7, 2025
a96fffa
use "value" prop where appropriate
cr0ybot Oct 7, 2025
dd050ec
fix hierarchical=false on front end
cr0ybot Oct 7, 2025
0e35655
more verbose comment about setting parent vs nested terms
cr0ybot Oct 7, 2025
896e368
Revert changes to edit component
cr0ybot Oct 7, 2025
c1cd91f
add inherit=false to default termQuery
cr0ybot Oct 7, 2025
4d92669
simplify current_term_id conditional
cr0ybot Oct 7, 2025
491b66a
better comment about is_tax() check
cr0ybot Oct 7, 2025
a01713c
cleanup unnecessary comment
cr0ybot Oct 7, 2025
a4c4303
set parent from current term id regardless of whether it might be 0
cr0ybot Oct 7, 2025
fa4d46b
explicitly fall back to parent=0
cr0ybot Oct 7, 2025
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
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ An advanced block that allows displaying taxonomy terms based on different query
- **Category:** theme
- **Allowed Blocks:** core/term-template
- **Supports:** align (full, wide), interactivity, ~~html~~
- **Attributes:** tagName, termQuery, termsToShow
- **Attributes:** tagName, termQuery

## Text Columns (deprecated)

Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/term-template/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"ancestor": [ "core/terms-query" ],
"description": "Contains the block elements used to render a taxonomy term, like the name, description, and more.",
"textdomain": "default",
"usesContext": [ "termQuery", "termsToShow" ],
"usesContext": [ "termQuery" ],
"supports": {
"reusable": false,
"html": false,
Expand Down
178 changes: 38 additions & 140 deletions packages/block-library/src/term-template/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,79 +125,17 @@ function TermTemplateBlockPreview( {
// Prevent re-rendering of the block preview when the terms data changes.
const MemoizedTermTemplateBlockPreview = memo( TermTemplateBlockPreview );

/**
* Builds a hierarchical tree structure from flat terms array.
*
* @param {Array} terms Array of term objects.
* @return {Array} Tree structure with parent/child relationships.
*/
function buildTermsTree( terms ) {
const termsById = {};
const rootTerms = [];

terms.forEach( ( term ) => {
termsById[ term.id ] = {
term,
children: [],
};
} );

terms.forEach( ( term ) => {
if ( term.parent && termsById[ term.parent ] ) {
termsById[ term.parent ].children.push( termsById[ term.id ] );
} else {
rootTerms.push( termsById[ term.id ] );
}
} );

return rootTerms;
}

/**
* Renders a single term node and its children recursively.
*
* @param {Object} termNode Term node with term object and children.
* @param {Function} renderTerm Function to render individual terms.
* @return {JSX.Element} Rendered term node with children.
*/
function renderTermNode( termNode, renderTerm ) {
return (
<li className="wp-block-term">
{ renderTerm( termNode.term ) }
{ termNode.children.length > 0 && (
<ul>
{ termNode.children.map( ( child ) =>
renderTermNode( child, renderTerm )
) }
</ul>
) }
</li>
);
}

/**
* Checks if a term is the currently active term.
*
* @param {number} termId The term ID to check.
* @param {number} activeBlockContextId The currently active block context ID.
* @param {Array} blockContexts Array of block contexts.
* @return {boolean} True if the term is active, false otherwise.
*/
function isActiveTerm( termId, activeBlockContextId, blockContexts ) {
return termId === ( activeBlockContextId || blockContexts[ 0 ]?.termId );
}

export default function TermTemplateEdit( {
clientId,
setAttributes,
context: {
termsToShow,
termQuery: {
taxonomy,
order,
orderBy,
hideEmpty,
hierarchical,
hierarchical = false,
parent = 0,
perPage = 10,
} = {},
},
Expand All @@ -207,15 +145,20 @@ export default function TermTemplateEdit( {
const { replaceInnerBlocks } = useDispatch( blockEditorStore );

const queryArgs = {
hide_empty: hideEmpty,
order,
orderby: orderBy,
hide_empty: hideEmpty,
// To preview the data the closest to the frontend, we fetch the largest number of terms
// and limit them during rendering. This is because WP_Term_Query fetches data in hierarchical manner,
// while in editor we build the hierarchy manually. It also allows us to avoid re-fetching data when max terms changes.
per_page: 100,
};

// Hierarchical is default from REST API as long as parent is not set.
if ( parent || ! hierarchical ) {
queryArgs.parent = parent;
}

const { records: terms, isResolving } = useEntityRecords(
'taxonomy',
taxonomy,
Expand All @@ -226,14 +169,9 @@ export default function TermTemplateEdit( {
if ( ! terms ) {
return [];
}
if ( termsToShow === 'top-level' ) {
return terms.filter( ( term ) => ! term.parent );
}
if ( termsToShow === 'subterms' ) {
return terms.filter( ( term ) => term.parent );
}
return terms;
}, [ terms, termsToShow ] );
// Limit to the number of terms defined by perPage.
return perPage === 0 ? terms : terms.slice( 0, perPage );
}, [ terms, perPage ] );

const { blocks, variations, defaultVariation } = useSelect(
( select ) => {
Expand Down Expand Up @@ -319,77 +257,37 @@ export default function TermTemplateEdit( {
return <p { ...blockProps }> { __( 'No terms found.' ) }</p>;
}

const renderTerm = ( term ) => {
const blockContext = {
taxonomy,
termId: term.id,
classList: `term-${ term.id }`,
termData: term,
};

return (
<BlockContextProvider key={ term.id } value={ blockContext }>
{ isActiveTerm(
term.id,
activeBlockContextId,
blockContexts
) ? (
<TermTemplateInnerBlocks
classList={ blockContext.classList }
/>
) : null }
<MemoizedTermTemplateBlockPreview
blocks={ blocks }
blockContextId={ term.id }
classList={ blockContext.classList }
setActiveBlockContextId={ setActiveBlockContextId }
isHidden={ isActiveTerm(
term.id,
activeBlockContextId,
blockContexts
) }
/>
</BlockContextProvider>
);
};

return (
<>
<ul { ...blockProps }>
{ hierarchical
? buildTermsTree( filteredTerms ).map( ( termNode ) =>
renderTermNode( termNode, renderTerm )
)
: blockContexts &&
blockContexts
.slice( 0, perPage )
.map( ( blockContext ) => (
<BlockContextProvider
key={ blockContext.termId }
value={ blockContext }
>
{ blockContext.termId ===
{ blockContexts &&
blockContexts.map( ( blockContext ) => (
<BlockContextProvider
key={ blockContext.termId }
value={ blockContext }
>
{ blockContext.termId ===
( activeBlockContextId ||
blockContexts[ 0 ]?.termId ) ? (
<TermTemplateInnerBlocks
classList={ blockContext.classList }
/>
) : null }
<MemoizedTermTemplateBlockPreview
blocks={ blocks }
blockContextId={ blockContext.termId }
classList={ blockContext.classList }
setActiveBlockContextId={
setActiveBlockContextId
}
isHidden={
blockContext.termId ===
( activeBlockContextId ||
blockContexts[ 0 ]?.termId ) ? (
<TermTemplateInnerBlocks
classList={ blockContext.classList }
/>
) : null }
<MemoizedTermTemplateBlockPreview
blocks={ blocks }
blockContextId={ blockContext.termId }
classList={ blockContext.classList }
setActiveBlockContextId={
setActiveBlockContextId
}
isHidden={
blockContext.termId ===
( activeBlockContextId ||
blockContexts[ 0 ]?.termId )
}
/>
</BlockContextProvider>
) ) }
blockContexts[ 0 ]?.termId )
}
/>
</BlockContextProvider>
) ) }
</ul>
</>
);
Expand Down
Loading
Loading