Conversation
…ith id Replace fragile dynamic property lookup (`get_queried_object()->$kind`) with explicit `instanceof WP_Post` / `instanceof WP_Term` checks. This fixes `current-menu-item` and `aria-current="page"` never being applied to links stored with `kind: "custom"` that carry a numeric `id` (e.g. legacy links and CPT links), while retaining post/term ID collision protection. Add 13 PHP unit tests covering active state for posts (page, post, CPT, legacy without kind, and the confirmed custom-kind bug), active state for terms (category, tag), inactive states (non-matching page, absent/zero/string id), and post/term ID collision guards.
When `kind` is not stored in the block attributes (which can happen for built-in types like 'category' and 'tag' in older blocks or links created via the Custom Link variation), fall back to `taxonomy_exists()` on the stored `type` to determine if the link is a taxonomy link. Also map the JS-normalised 'tag' type back to 'post_tag' so the lookup works correctly. Add three tests: page-type without kind (post-type fallback), category-type without kind (taxonomy inferred), and tag-type without kind (post_tag alias).
…lock / compat context
…vigation_link_is_active() Moves the active-state determination (Branch B entity ID match + Branch C post-type archive match) out of the render function into a dedicated helper. Cleans up the render function and prepares the logic for reuse in the navigation-submenu block (follow-up).
|
Flaky tests detected in a182987. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/23528854523
|
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @Copilot, @chuckpearson, @jeroensmeets, @voyager131, @wdburgdorf. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect “current” state detection for core/navigation-link server-side rendering by replacing a fragile queried-object dynamic property check with explicit object type checks and taxonomy inference, restoring current-menu-item and aria-current="page" behavior for legacy/custom scenarios.
Changes:
- Add
block_core_navigation_link_is_active()to determine active state viainstanceof WP_Post/instanceof WP_Termandtaxonomy_exists()inference whenkindis missing. - Replace inline active-state logic in
render_block_core_navigation_link()with the new helper. - Add comprehensive PHP unit tests covering active/inactive cases (custom kind, missing kind, category/tag without kind, collision guards, and post type archives).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
packages/block-library/src/navigation-link/index.php |
Introduces and uses a dedicated active-state helper with explicit query-object type guards and taxonomy inference. |
phpunit/blocks/class-block-library-navigation-link-test.php |
Adds many new unit tests validating current-menu-item / aria-current behavior across post, term, legacy, and archive scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| public static function wpTearDownAfterClass() { | ||
| unregister_post_type( 'dogs' ); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…oss-test conflicts Co-authored-by: getdave <444434+getdave@users.noreply.github.com> Agent-Logs-Url: https://github.com/WordPress/gutenberg/sessions/fe4ef4be-552a-4651-ab48-5500c3fc8bfa
MaggieCabrera
left a comment
There was a problem hiding this comment.
This looks good to me and works as expected
|
Should we make the same change in navigation-submenu? |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
What
Fixes #61266
Fixes
current-menu-itemandaria-current="page"not being applied to Navigation Link blocks in several scenarios that should produce an active state.Why
The PHP render function used a fragile dynamic property lookup to determine whether a nav link is active:
This converts
kindfrom kebab-case to snake-case and then reads a property of that name off the queried object. It works for"post-type"(→WP_Post->post_type) and"taxonomy"(→WP_Term->taxonomy), but silently fails for any other value:kind: "custom"— links saved before the current entity-link conventions, or links where the editor resolved a URL to a post ID, storeidalongsidekind: "custom". The propertyget_queried_object()->customnever exists, so$is_activeis alwaysfalse.kindabsent,type: "category"ortype: "tag"— the JS editor omitskindfrom the stored attributes whennewKindis empty and the type is a built-in (seeupdate-attributes.js). Defaulting to"post-type"causes aWP_Postcheck against aWP_Termarchive page.How
During development, the classic WordPress menu system (
_wp_menu_item_classes_by_context()inwp-includes/nav-menu-template.php) was reviewed as a reference for how active-state detection should work and how post/term ID collisions should be prevented. That analysis directly informed the approach taken here and identified the follow-up gaps noted at the bottom of this PR.Replaces the property-lookup approach with explicit
instanceofchecks, mirroring how the classic menu system avoids post/term ID collisions:$stored_kindand$stored_typeare read from attributeskindis absent,taxonomy_exists( $resolved_type )infers whether the link targets a taxonomy — handlescategory,tag(mapped topost_tag), and any registered custom taxonomy$is_taxonomy_linkdrives aninstanceof WP_Termvsinstanceof WP_Postcheck, replacing the unreliable property lookup$link_idusesis_numeric()to guard against the historical case whereidcould be a URL stringTesting Instructions
idis stored)current-menu-itemandaria-current="page"✅Backwards Compatibility
kind"post-type"(page/post/any CPT)WP_Post"taxonomy"(category/tag/custom taxonomy)WP_Term"custom"with id (legacy links)WP_PostWP_Postkindabsent,type: "category"or"tag"WP_Termtaxonomy_exists()fallback)"post-type"link on term page with same IDWP_Term"taxonomy"link on post page with same IDWP_PostTests
24 PHP unit tests added to
phpunit/blocks/class-block-library-navigation-link-test.phpcovering:kind, andkind: "custom"links with id (the confirmed bug)kindis absent buttypeidentifies a taxonomyKnown gaps — follow-up issues
As part of this work the classic WordPress menu system (
_wp_menu_item_classes_by_context()) was reviewed in detail. It handles four distinct match branches (A–D); this PR addresses Branch B (direct ID + type match). The following gaps should each be tackled as a separate follow-up issue:core/navigation-submenuhas the same bug — the submenu block'sindex.phpcontains an identical copy of the broken->$kindproperty lookup. The same fix applies. There is also an opportunity to extract the shared$is_activelogic into a single utility function rather than maintaining it in two places.No URL-based matching for links without an
id(Branch D) — the classic system matchescustomtype items by comparing the stored URL againstREQUEST_URI. Akind: "custom"link with noid(severed entity links, manually-typed URLs) will never receivecurrent-menu-itemeven when the URL exactly matches the current page.No taxonomy ancestor propagation (Branch A) — when viewing a singular post, the classic system flags any category/tag the post belongs to as an active parent, walking the full ancestor chain. The block system has no equivalent.
No ancestor/parent classes —
current-menu-ancestor,current-menu-parent,current-{object}-parent, and related classes from the classic system's second pass are largely absent from the block implementation (the submenu block has partial support via an inner-block scan, but nothing systematic).