Skip to content

Add/button dynamic url#74814

Open
Vrishabhsk wants to merge 3 commits intoWordPress:trunkfrom
Vrishabhsk:add/button-dynamic-url
Open

Add/button dynamic url#74814
Vrishabhsk wants to merge 3 commits intoWordPress:trunkfrom
Vrishabhsk:add/button-dynamic-url

Conversation

@Vrishabhsk
Copy link
Contributor

What?

Why?

  • The Button block currently treats “select a page/post” links as static URLs, so if the linked entity’s permalink changes later (e.g., slug update), existing buttons can silently become outdated/broken.
  • This PR makes entity-selected Button links resilient by keeping them synced to the selected entity instead of the current editor context.

How?

  • Introduces a generic core/entity block bindings source (JS + PHP) that resolves an entity’s url from explicit binding args (kind, type, id) and is not tied to the current post/page context.
  • Updates the Button block so when a user selects a post/page/term from the existing link picker:
  • It creates a metadata.bindings.url binding using core/entity.
  • It does not persist a static url attribute for entity links (URL is dynamically resolved from the entity).
  • It clears the binding when switching back to a manual URL or unlinking.
  • Includes cleanup to remove any legacy contextual bindings that could incorrectly resolve to the current post.
  • Enhances the Button inspector “Settings” panel to use the same LinkPicker preview UI pattern as Navigation Link (badges + synced help text), while keeping existing toolbar link/unlink behavior intact.
  • Hardens ToolsPanelItem callback handling to avoid crashes when onDeselect is not a function.

Testing Instructions

  1. Open a post or page in the editor.
  2. Insert a Buttons block and add a Button block.
  3. In the Button toolbar, click Link (or use Cmd/Ctrl+K).
  4. Search for and select an existing Page (or CPT) suggestion.
  5. Confirm:
    • The Button shows the entity preview in the sidebar “Settings” panel (e.g., “Page”, “Published”) and “Synced with the selected …”.
    • The toolbar Unlink control remains available.
  6. Save the post/page.
  7. Change the linked entity’s permalink (e.g., edit the page slug) and save it.
  8. Return to the original post/page and confirm:
    • The Button link updates to the entity’s new URL (no stale URL stored).
  9. Unlink the Button and confirm:
    • No editor crash occurs.
    • The Button returns to an unlinked state and can be linked again.

(Optional) Paste/type a custom external URL and confirm:
The entity binding is removed and the Button uses the typed URL.

Screenshots or screencast

Screenshot 2026-01-21 at 6 09 50 PM Screenshot 2026-01-21 at 6 24 53 PM

@github-actions
Copy link

github-actions bot commented Jan 21, 2026

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: [Package] Components, [Package] Editor, [Package] Block library, [Block] Buttons.

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

@github-actions
Copy link

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: .

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

@github-actions
Copy link

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: Vrishabhsk <vrishabhsk@git.wordpress.org>
Co-authored-by: getdave <get_dave@git.wordpress.org>

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

@github-actions github-actions bot added [Package] Components /packages/components [Package] Editor /packages/editor [Package] Block library /packages/block-library labels Jan 21, 2026
@mtias mtias added the [Block] Buttons Affects the Buttons Block label Feb 15, 2026
Copy link
Member

@ramonjd ramonjd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR tests well manually, but there are some items that make me doubt it could be ready for WordPress 7.0 beta.

For a more professional opinion, it'd be good to have @getdave run an eye over things in light of the work done in #71630

linkPanel={ isLinkTag ? linkPanel : null }
/>
</InspectorControls>
<InspectorControls group="advanced">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the reason to remove this control? Is it because there'll always be an href now?

It means users can no longer switch between <a> and <button> tags, which seems on the surface to be a regression to me.

* - type: post type slug or taxonomy slug
* - id: entity ID
*
* @since 6.9.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you've been waiting for a while for reviews, thanks for your patience!

I don't know if this will make the next WordPress release, but the next version is now 7.0.0

* @param WP_Block $block_instance The block instance.
* @return mixed The value computed for the source.
*/
function gutenberg_block_bindings_entity_get_value( array $source_args, $block_instance ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to get test coverage here, e.g.,

  • Valid post-type and taxonomy URL resolution
  • Non-public post denial
  • Password-protected post denial
  • Invalid/missing args
  • Deleted entity handling (?)

}

$kind = (string) $source_args['kind'];
$type = (string) $source_args['type'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if the post type exists? E.g.,

if ( ! post_type_exists( $type ) ) {
    return null;
}

Comment on lines +559 to +560
clearEntityUrlBinding,
createEntityUrlBinding,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these (and unlink()) should be wrapped in a useCallback().

They are plain closures recreated every render, which will make the useMemo recompute here.

Either that or restructure the logic somehow.

urlBinding?.source === 'core/term-data';
const boundEntityArgs = isEntityUrlBinding ? urlBinding?.args : null;

const resolvedEntityUrl = useSelect(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this useSelect be combined with the one below? They both call getEntityRecord.

preview,
suggestionsQuery: getSuggestionsQuery( boundType, boundKind ),
help,
onSelect: ( updatedLink ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This callback seems very similar to LinkControl.onChange - could be extracted to a shared helper?

@@ -150,11 +150,15 @@ export function useToolsPanelItem(
}

if ( isMenuItemChecked && ! isValueSet && ! wasMenuItemChecked ) {
onSelect?.();
if ( typeof onSelect === 'function' ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation behind this change?

There's a subtle difference: optional chaining would throw on non-function truthy values (surfacing bugs), while typeof silently skips them.

I only raise it because it's a shared component used in many places. If there's a good reason, this change would be better suited to its own PR.

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

Labels

[Block] Buttons Affects the Buttons Block [Package] Block library /packages/block-library [Package] Components /packages/components [Package] Editor /packages/editor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add dynamic URL resolution to Button Block

3 participants