feat(breadcrumbs): add native breadcrumbs dynamic block#16
feat(breadcrumbs): add native breadcrumbs dynamic block#16brandonmarshal wants to merge 3 commits into
Conversation
- Register ls-plugin/breadcrumbs, a dynamic block rendering nav.ls-crumbs - Build the trail entirely from core WordPress data, no plugin dependency - Walk nested ancestors for pages, posts, and categories/tags/taxonomies - Handle front page, blog home, search, and 404 cases - Add editor placeholder preview and front-end styling
|
Tick the box to add this pull request to the merge queue (same as
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new dynamic Breadcrumbs block (ls-plugin/breadcrumbs) that builds its navigation trail entirely from core WordPress data without any third-party dependencies. It includes block registration, editor components, styling, and a PHP rendering template. The review feedback highlights a critical issue in render.php where get_term_link() is called without checking for a WP_Error return value. If a WP_Error is returned, passing it directly to esc_url() will trigger a fatal TypeError in PHP 8.0+. The reviewer recommends using is_wp_error() checks to safely default the URL to null in these instances.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
Warning Review limit reached
Next review available in: 39 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
Note
|
| Layer / File(s) | Summary |
|---|---|
Block registration and editor wiring src/blocks/breadcrumbs/block.json, build/blocks/breadcrumbs/*, inc/class-breadcrumbs.php, ls-plugin.php, src/blocks/breadcrumbs/index.js, src/blocks/breadcrumbs/edit.js, src/blocks/breadcrumbs/save.js |
Defines the block, loads and registers it during plugin initialization, and provides a dynamic editor placeholder with no saved markup. |
Contextual breadcrumb rendering src/blocks/breadcrumbs/render.php, build/blocks/breadcrumbs/render.php |
Builds breadcrumb trails for core WordPress contexts and renders escaped links, separators, and the current-page item. |
Breadcrumb presentation and release metadata src/blocks/breadcrumbs/style.scss, build/blocks/breadcrumbs/style-index.css, build/blocks/breadcrumbs/style-index-rtl.css, CHANGELOG.md |
Adds breadcrumb and placeholder styling for LTR and RTL output and documents the new block under Unreleased additions. |
Estimated code review effort: 3 (Moderate) | ~20 minutes
Sequence Diagram(s)
sequenceDiagram
participant Editor
participant WordPress
participant render_php
participant QueryContext
participant BreadcrumbNav
Editor->>WordPress: insert ls-plugin/breadcrumbs block
WordPress->>render_php: render dynamic block
render_php->>QueryContext: inspect current request context
QueryContext-->>render_php: provide breadcrumb trail data
render_php->>BreadcrumbNav: output navigation markup
BreadcrumbNav-->>Editor: display breadcrumb trail
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and concisely describes the main change: adding a native dynamic breadcrumbs block. |
| Docstring Coverage | ✅ Passed | Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
feature-breadcrumbs-dynamic-block
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
…s-dynamic-block # Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/blocks/breadcrumbs/render.php (1)
51-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated ancestor-walking logic across two branch pairs.
The taxonomy-ancestor loop (lines 51-66) is re-implemented for categories inside
is_single()(lines 94-109) with 'category' hardcoded instead of the term's taxonomy, and the post-ancestor loop (lines 79-87) is duplicated verbatim inis_page()(lines 127-134). Any future change to how ancestor crumbs are built (e.g. adding arelattribute or filter hook) needs to be applied in two places each.Extract two small static helpers on
LS_Plugin_Breadcrumbs(already loaded once viarequire_once) rather than declaring bare functions insiderender.php—render.phpbacking a block.jsonrenderfield can be executed more than once per page load if the block appears twice, and top-level function declarations there risk a "Cannot redeclare function" fatal.♻️ Proposed refactor (helper methods in inc/class-breadcrumbs.php, called from render.php)
// inc/class-breadcrumbs.php (inside LS_Plugin_Breadcrumbs) public static function add_taxonomy_ancestors( array &$trail, WP_Term $term ) { if ( ! is_taxonomy_hierarchical( $term->taxonomy ) ) { return; } foreach ( array_reverse( get_ancestors( $term->term_id, $term->taxonomy, 'taxonomy' ) ) as $ancestor_id ) { $ancestor_term = get_term( $ancestor_id, $term->taxonomy ); if ( $ancestor_term instanceof WP_Term ) { $trail[] = array( 'label' => $ancestor_term->name, 'url' => get_term_link( $ancestor_term ), ); } } } public static function add_post_ancestors( array &$trail, WP_Post $post ) { foreach ( array_reverse( get_post_ancestors( $post ) ) as $ancestor_id ) { $trail[] = array( 'label' => get_the_title( $ancestor_id ), 'url' => get_permalink( $ancestor_id ), ); } }- if ( is_taxonomy_hierarchical( $ls_breadcrumbs_term->taxonomy ) ) { - $ls_breadcrumbs_ancestor_ids = array_reverse( - get_ancestors( $ls_breadcrumbs_term->term_id, $ls_breadcrumbs_term->taxonomy, 'taxonomy' ) - ); - - foreach ( $ls_breadcrumbs_ancestor_ids as $ls_breadcrumbs_ancestor_id ) { - $ls_breadcrumbs_ancestor_term = get_term( $ls_breadcrumbs_ancestor_id, $ls_breadcrumbs_term->taxonomy ); - - if ( $ls_breadcrumbs_ancestor_term instanceof WP_Term ) { - $ls_breadcrumbs_trail[] = array( - 'label' => $ls_breadcrumbs_ancestor_term->name, - 'url' => get_term_link( $ls_breadcrumbs_ancestor_term ), - ); - } - } - } + LS_Plugin_Breadcrumbs::add_taxonomy_ancestors( $ls_breadcrumbs_trail, $ls_breadcrumbs_term );Apply the same replacement at lines 79-87 and 127-134 with
add_post_ancestors(), and at lines 94-109 withadd_taxonomy_ancestors().Also applies to: 79-87, 94-109, 127-134
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/blocks/breadcrumbs/render.php` around lines 51 - 66, Extract the duplicated ancestor-building logic into public static LS_Plugin_Breadcrumbs helpers: add_taxonomy_ancestors(array &$trail, WP_Term $term) and add_post_ancestors(array &$trail, WP_Post $post). Move the existing taxonomy and post ancestor behavior into these helpers, using each term’s taxonomy, then replace the taxonomy blocks at the current-term and single-post branches and both post ancestor loops with helper calls; do not add bare functions to render.php.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/blocks/breadcrumbs/render.php`:
- Around line 51-66: Extract the duplicated ancestor-building logic into public
static LS_Plugin_Breadcrumbs helpers: add_taxonomy_ancestors(array &$trail,
WP_Term $term) and add_post_ancestors(array &$trail, WP_Post $post). Move the
existing taxonomy and post ancestor behavior into these helpers, using each
term’s taxonomy, then replace the taxonomy blocks at the current-term and
single-post branches and both post ancestor loops with helper calls; do not add
bare functions to render.php.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 3980f656-7a7b-43a9-aa37-dfc97e6789b2
📒 Files selected for processing (15)
CHANGELOG.mdbuild/blocks/breadcrumbs/block.jsonbuild/blocks/breadcrumbs/index.asset.phpbuild/blocks/breadcrumbs/index.jsbuild/blocks/breadcrumbs/render.phpbuild/blocks/breadcrumbs/style-index-rtl.cssbuild/blocks/breadcrumbs/style-index.cssinc/class-breadcrumbs.phpls-plugin.phpsrc/blocks/breadcrumbs/block.jsonsrc/blocks/breadcrumbs/edit.jssrc/blocks/breadcrumbs/index.jssrc/blocks/breadcrumbs/render.phpsrc/blocks/breadcrumbs/save.jssrc/blocks/breadcrumbs/style.scss
…stor logic - Extract taxonomy/post ancestor-walking into static helpers on LS_Plugin_Breadcrumbs (add_taxonomy_ancestors, add_post_ancestors, get_term_url), removing 3x duplicated logic across render.php branches - Fix a real PHP 8 TypeError risk: get_term_link() can return WP_Error, which was being passed straight into esc_url() unguarded - Addresses Gemini Code Assist and CodeRabbit review feedback on PR #16
Closing Without Merging — Superseded by Yoast's Native Breadcrumbs BlockThis PR built a fully custom Context in LS-1228The original plan was to call Testing surfaced a real Yoast caching quirk: Indexables ancestor data was not refreshing for programmatically updated page parents. Instead of investigating that further, this PR removed the Yoast integration entirely and rebuilt the breadcrumbs functionality from scratch. However, Yoast SEO already provides its own dedicated Corrected ApproachThe corrected approach, documented in the course-correction and Part 2 re-approach plans, wires Yoast's native block directly into:
That work is currently in progress on:
in the Net Result
This PR and its branch, |
Description
Adds a new
ls-plugin/breadcrumbsdynamic block that renders a breadcrumb navigation trail (nav.ls-crumbs) built entirely from core WordPress data — no third-party plugin dependency. This is Part 1 of LS-1228; Part 2 (wiring the block intols-theme'spatterns/breadcrumbs.phpand templates) is tracked separately in that repo.Changes
ls-plugin/breadcrumbs— dynamic block following this plugin's existing block conventions (search-filter/carousel/style-switcher)inc/class-breadcrumbs.php— registers the block frombuild/blocks/breadcrumbsLS_Plugin_Breadcrumbsintols_plugin_init()inls-plugin.php[Unreleased]Files Modified
Source Files:
src/blocks/breadcrumbs/block.json— block metadata (dynamic,render.php, categorytheme)src/blocks/breadcrumbs/edit.js— static "Breadcrumbs (dynamic)" editor placeholdersrc/blocks/breadcrumbs/save.js— returnsnull(dynamic block)src/blocks/breadcrumbs/index.js— registers the block in JSsrc/blocks/breadcrumbs/render.php— PHP render callback, the actual trail-building logicsrc/blocks/breadcrumbs/style.scss— front-endnav.ls-crumbsstyling + editor placeholder stylingbuild/blocks/breadcrumbs/*— compiled output of the above (6 files)inc/class-breadcrumbs.php— new file, block registrationls-plugin.php— +2 lines (require + instantiate)CHANGELOG.md— +1 line (Unreleased entry)Key Improvements
get_post_ancestors(), hierarchical CPTs the same way, and categories/tags/taxonomies viaget_ancestors()— so a post in a child category rendersHome > Parent Category > Child Category > Post, not just the immediate term<span aria-current="page">esc_url()/esc_html()var(--wp--custom--...)with fallbacks)Decisions
yoast_breadcrumb()when active, with a native fallback otherwise. During testing this surfaced a real blocker: Yoast SEO caches breadcrumb ancestor chains in its own Indexables tables, built on a normal editorsave_postcycle — pages created/updated via non-editor paths didn't reliably trigger Yoast's hierarchy rebuild, so Yoast's own output silently dropped ancestor crumbs while everything else was correct (a Yoast caching quirk, not a bug in this code). Combined with a preference for zero third-party coupling, theyoast_breadcrumb()branch was removed — the block now always uses the native trail-building logic (originally written as the fallback). Full context in the LS-1228 comment thread.Related Issues
Closes Part 1 of LS-1228 — build breadcrumb dynamic block (
ls-plugin) + wire into breadcrumbs pattern (ls-theme).Part 2 (wiring the block into
patterns/breadcrumbs.php/ theme templates) is tracked in the same issue and does not touch this repo.Testing
localhostMCP connector + manual browser checksHome > Parent PageHome > Parent Page > Child Page, incl. crumb links navigating correctlyHome > Parent Category > Child Category > PostHome > Portfolio Itemphp -lclean on all changed files;phpcsshows only pre-existing tabs-vs-spaces findings identical to already-merged files, not introduced by this changels-themeDeployment Notes
npm run build(block auto-discovered by existingwp-scriptsconfig, no webpack changes needed)Checklist
developbranchphp -l)npm run build)Summary by CodeRabbit