Skip to content

Fix: Refless Navigation Block does not show block appender - #77688

Open
hbhalodia wants to merge 4 commits into
WordPress:trunkfrom
hbhalodia:fix/issue-75215
Open

Fix: Refless Navigation Block does not show block appender#77688
hbhalodia wants to merge 4 commits into
WordPress:trunkfrom
hbhalodia:fix/issue-75215

Conversation

@hbhalodia

@hbhalodia hbhalodia commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

What?

Closes #75215

Why?

  • PR aims to resolve the issue, where block appender button is not being rendered on no-ref navigations.

How?

  • PR uses the same technique used by <NavigationInnerBlocks component to add the appender.

Testing Instructions

  1. Open post/page.
  2. Add the below snippet.
<!-- wp:navigation {"overlayMenu":"never","layout":{"type":"flex","orientation":"vertical"}} -->
<!-- wp:navigation-link {"label":"Events","url":"#"} /-->

<!-- wp:navigation-link {"label":"Shop","url":"#"} /-->
<!-- /wp:navigation -->
  1. Click on events and see the appender button is visible.

Testing Instructions for Keyboard

  • None

Screenshots or screencast

Screen.Recording.2026-04-27.at.5.02.44.PM.mov

Use of AI Tools

  • GitHub Copliot.
  • Claude Opus 4.6

AI summary and root cause

Issue: Refless Navigation Block Does Not Show Block Appender

Summary

When a navigation block has no ref attribute (i.e., its inner blocks are defined directly in the post content rather than referencing a wp_navigation entity), clicking a navigation link does not show the block appender (the "+" button for adding new items). In contrast, a ref-based navigation block correctly shows the appender.


Root Cause (Two Interrelated Issues)

The bug stems from a behavioral mismatch between two separate components that render the Navigation block's inner blocks:

Scenario Component Inner Blocks Block Overlay Appender Logic
Ref-based (has ref) NavigationInnerBlocks Controlled (via useEntityBlockEditor) Active (first click selects parent) InnerBlocks.ButtonBlockAppender shown when selected, child selected, or any descendant selected
Refless (no ref, unsaved) UnsavedInnerBlocks Uncontrolled (direct innerBlocks) Not active (first click passes through to child) Default appender (undefined) shown only when hasSelection is truthy

Issue 1: No Block Overlay on Refless Navigation

In the block editor store, the function __unstableHasActiveBlockOverlayActive determines whether a block gets a content overlay (intercepting clicks so the first click selects the parent block). The critical condition is:

const shouldEnableIfUnselected = blockSupportDisable
    ? false
    : areInnerBlocksControlled(state, clientId);

For ref-based navigation, inner blocks are controlled (managed by EntityProvider + useEntityBlockEditor), so areInnerBlocksControlled returns true → overlay is active → first click selects the parent navigation block.

For refless navigation, inner blocks are uncontrolled (stored directly in the block tree), so areInnerBlocksControlled returns falseno overlay → first click goes directly to the child navigation link.

Issue 2: Incorrect renderAppender in UnsavedInnerBlocks

In unsaved-inner-blocks.js:

const innerBlocksProps = useInnerBlocksProps(
    { className: 'wp-block-navigation__container' },
    {
        renderAppender: hasSelection ? undefined : false,  // <-- THE BUG
        defaultBlock: DEFAULT_BLOCK,
        directInsert: true,
    }
);

When hasSelection is true (which equals isSelected || isInnerBlockSelected), renderAppender is set to undefined. The value undefined means "use the default appender", which only renders when the block itself is selected — not when an inner block is selected.

Compare this with inner-blocks.js (used for ref-based navigation):

renderAppender:
    isSelected ||
    (isImmediateParentOfSelectedBlock && !selectedBlockHasChildren) ||
    hasSelectedDescendant ||
    parentOrChildHasSelection
        ? InnerBlocks.ButtonBlockAppender
        : false,

This explicitly uses InnerBlocks.ButtonBlockAppender, which renders whenever the condition is met — including when any descendant is selected.

Combined Effect

  1. User clicks a navigation link in a refless navigation block
  2. No block overlay → the child link gets selected directly (not the parent)
  3. hasSelection = true (because isInnerBlockSelected is true)
  4. renderAppender = undefined (default appender)
  5. Default appender only shows when the navigation block itself is selected
  6. Since only the child is selected, appender does not render

The Fix

Update UnsavedInnerBlocks to use InnerBlocks.ButtonBlockAppender explicitly, matching the behavior of NavigationInnerBlocks. The component also needs to compute more granular selection state.

File: packages/block-library/src/navigation/edit/unsaved-inner-blocks.js

+ import {
+     useInnerBlocksProps,
+     InnerBlocks,
+     store as blockEditorStore,
+ } from '@wordpress/block-editor';
+ import { useSelect } from '@wordpress/data';

  export default function UnsavedInnerBlocks({
      blocks,
      createNavigationMenu,
      hasSelection,
+     clientId,
  }) {
+     const {
+         isSelected,
+         isImmediateParentOfSelectedBlock,
+         selectedBlockHasChildren,
+         hasSelectedDescendant,
+     } = useSelect(
+         (select) => {
+             const {
+                 getBlockCount,
+                 hasSelectedInnerBlock,
+                 getSelectedBlockClientId,
+             } = select(blockEditorStore);
+             const selectedBlockId = getSelectedBlockClientId();
+             return {
+                 isSelected: selectedBlockId === clientId,
+                 isImmediateParentOfSelectedBlock: hasSelectedInnerBlock(clientId, false),
+                 selectedBlockHasChildren: !!getBlockCount(selectedBlockId),
+                 hasSelectedDescendant: hasSelectedInnerBlock(clientId, true),
+             };
+         },
+         [clientId]
+     );

      const innerBlocksProps = useInnerBlocksProps(
          { className: 'wp-block-navigation__container' },
          {
-             renderAppender: hasSelection ? undefined : false,
+             renderAppender:
+                 isSelected ||
+                 (isImmediateParentOfSelectedBlock && !selectedBlockHasChildren) ||
+                 hasSelectedDescendant
+                     ? InnerBlocks.ButtonBlockAppender
+                     : false,
              defaultBlock: DEFAULT_BLOCK,
              directInsert: true,
          }
      );

And in the parent index.js, pass clientId to UnsavedInnerBlocks:

  <UnsavedInnerBlocks
      createNavigationMenu={createNavigationMenu}
      blocks={uncontrolledInnerBlocks}
      hasSelection={isSelected || isInnerBlockSelected}
+     clientId={clientId}
  />

Why This Fixes It

  • InnerBlocks.ButtonBlockAppender explicitly renders the "+" button regardless of which block is selected, as long as the condition evaluates to true
  • hasSelectedDescendant captures the case where any nested block is selected (not just immediate children)
  • This mirrors the exact logic in NavigationInnerBlocks, making the behavior consistent between ref-based and refless navigation blocks

Alternative Simpler Fix

If you want a minimal change without adding new useSelect hooks:

- renderAppender: hasSelection ? undefined : false,
+ renderAppender: hasSelection ? InnerBlocks.ButtonBlockAppender : false,

This alone would fix the appender visibility because hasSelection already includes isInnerBlockSelected. The only difference from the full fix is that it doesn't have the fine-grained "don't show appender if selected child has its own children" logic, but for navigation blocks this is unlikely to matter.


I would raise the PR with a fix and will discuss the approaches there. At a first glance it has mimiced what is being used by the ref navigation. Ideally this should be the acceptable.

@hbhalodia
hbhalodia requested a review from jeryj April 27, 2026 11:34
@hbhalodia hbhalodia self-assigned this Apr 27, 2026
@hbhalodia hbhalodia added [Type] Bug An existing feature does not function as intended [Block] Navigation Affects the Navigation Block [Block] Navigation Link Affects the Navigation Link Block labels Apr 27, 2026
@github-actions github-actions Bot added the [Package] Block library /packages/block-library label Apr 27, 2026
@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown

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 props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: hbhalodia <hbhalodia@git.wordpress.org>
Co-authored-by: youknowriad <youknowriad@git.wordpress.org>
Co-authored-by: jeryj <jeryj@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@youknowriad

Copy link
Copy Markdown
Contributor

This seems to address your issue @jeryj no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Block] Navigation Link Affects the Navigation Link Block [Block] Navigation Affects the Navigation Block [Package] Block library /packages/block-library [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refless Navigation Block does not show block appender

2 participants