Skip to content

Widget Primitives: document actions as verbs, not links - #80974

Merged
retrofox merged 4 commits into
trunkfrom
update/widget-actions-docs
Jul 31, 2026
Merged

Widget Primitives: document actions as verbs, not links#80974
retrofox merged 4 commits into
trunkfrom
update/widget-actions-docs

Conversation

@retrofox

@retrofox retrofox commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What?

Consolidates how widget actions are described across the docs, the anatomy story, the architecture doc, and the WidgetAction type. No runtime change.

Four of the five places that describe an action defined it as a link:

anatomy.md               "an id, a label, and a link target"
dashboard-widgets.md     "actions: declarative links"
types.ts                 "The action points at a link target"
actions.md               "a target the host renders as an anchor"

Only actions.md kept the model the field was designed around, an envelope plus one fulfillment, and even there the example contradicted it.

Part of #77616.

Why?

actions shipped in #80363 with a single fulfillment, a link, and the wording followed the implementation instead of the contract. That reads fine while there is one fulfillment and stops being true the moment there is a second one. The PR that introduced the field already lists two as follow-ups.

Two sentences were also wrong as written, not just narrow:

anatomy.md said "Today the only target is a link, so navigation and download are the browser's". Being a link does not imply the browser handles navigation. A target inside the host's own routes is still a link and is handled by the host's router.

Both docs said the host renders the action as an anchor, while the same paragraphs said the host decides how the action materializes. actions.md names the command palette as a possible surface, where an anchor does not exist.

How?

Separates three decisions that were described as one, and gives each a name the rest of the docs can use:

Fulfillment, what triggering the action means. Named by the key that carries it rather than by a discriminator, exactly one per action. Today the only key is href.

Materialization, which primitive the host mounts. This is where the link obligation now lives, with its reason: where the surface allows a link primitive the host must mount one, because a real link keeps middle-click, copy address, and the anchor role. It also records that the widget declares where to go while the host decides how to get there, since whether a target is reachable in-page depends on the routes a host registered.

Placement, where the affordance goes. Unchanged; it was already correct.

Two diagrams in the existing house style: action-axes.svg for the three decisions, action-surfaces.svg for one declaration materialized by three surfaces, which is also where the command palette degradation is written down. actions.mdx now maps both through the bundler like the other story wrappers do.

Smaller fixes along the way. download and openInNewTab now say they belong to the link rather than the envelope. three-layers.svg listed Representation as "attributes · example · render" and omitted actions.

Testing

npm run storybook:dev

Open Widget Primitives / Actions and Widget Primitives / Anatomy: both diagrams render, and the three sections read in order.

No runtime code changed, so no behavior to verify.

image

Follow-ups

  • WidgetDashboard.Actions is an unrelated compound component for dashboard chrome (edit toggle, reset, add widget). The two meanings sit in different layers today and stay apart. If widget actions ever reach host-level surfaces, they share one and the collision becomes visible. Renaming a public compound is breaking, so it is worth deciding on its own.
  • scope: 'local' | 'global', a reach axis orthogonal to relevance, so a host can surface an action beyond the widget.
  • callback and steps fulfillments, from Dashboard Widgets: declarative actions in the widget schema #80363.

separate fulfillment, materialization and placement; add diagrams
@retrofox retrofox added [Type] Developer Documentation Documentation for developers [Feature] Dashboard WordPress admin dashboard, widget framework, and layout customization labels Jul 30, 2026
@retrofox retrofox self-assigned this Jul 30, 2026
@retrofox
retrofox requested review from chihsuan and simison July 30, 2026 14:20
@github-actions

github-actions Bot commented Jul 30, 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: retrofox <retrofox@git.wordpress.org>
Co-authored-by: chihsuan <chihsuan@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

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 7.76 MB

compressed-size-action

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall, this looks good! It makes the contract much clearer. 👍

I left a few comments about ownership wording. Also, would it be worth updating the remaining public-facing docs as part of this consolidation?

  • packages/widget-primitives/README.md still says the host renders every action as an anchor
  • the WithActions story in packages/widget-primitives/src/components/widget-render/stories/index.story.tsx retains the old link-focused framing, including “navigation and download are the browser’s.”

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This diagram says the host decide fulfillment. Is that correct? My understanding is the widget selects the fulfillment through its key, while the host decides materialization and placement.

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, Chi. Updating the doc


![The same action declaring a link target, materialized by three surfaces: a More menu and a footer mount a real anchor and keep link semantics, while a command palette has no anchor to mount and degrades to a command.](./assets/action-surfaces.svg)

A link fulfillment carries one obligation: **where the surface allows a link primitive, the host must mount one**. A real link keeps middle-click, copy address, and the anchor's accessible role. Routing the same target through a click handler destroys all three, so a link is a first-class fulfillment rather than a shorthand for one.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A real link keeps middle-click, copy address, and the anchor's accessible role.

Could we perhaps narrow this slightly? In the More menu, the anchor receives role="menuitem", so middle-click and copy address survive, but the implicit link role does not.

narrow the a11y claim; extend the sweep to README, story and host
@retrofox

Copy link
Copy Markdown
Contributor Author

Both were right, and the first one went past wording.

The diagram had the host deciding the fulfillment, when the widget fixes it by writing the key. So the host is left with two decisions, not three. The host column now carries materialization and placement, and the intro follows.

On the accessible role, the claim was too wide. Middle-click and copy address belong to the element; the role follows the surface. Those are two separate statements now, and the More menu case is noted in actions.md and in the component itself.

The two docs you pointed at are updated. Looking for them turned up a third: widget-actions.tsx carried the same wording.

Ready for another look.

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @retrofox LGTM 🚢

@retrofox
retrofox enabled auto-merge (squash) July 31, 2026 08:53
@retrofox
retrofox merged commit 0acc649 into trunk Jul 31, 2026
58 checks passed
@retrofox
retrofox deleted the update/widget-actions-docs branch July 31, 2026 08:58
@github-actions github-actions Bot added this to the Gutenberg 23.8 milestone Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Dashboard WordPress admin dashboard, widget framework, and layout customization [Type] Developer Documentation Documentation for developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants