Widget Primitives: document actions as verbs, not links - #80974
Conversation
separate fulfillment, materialization and placement; add diagrams
|
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 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. |
|
Size Change: 0 B Total Size: 7.76 MB |
chihsuan
left a comment
There was a problem hiding this comment.
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.mdstill says the host renders every action as an anchor- the
WithActionsstory inpackages/widget-primitives/src/components/widget-render/stories/index.story.tsxretains the old link-focused framing, including “navigation and download are the browser’s.”
There was a problem hiding this comment.
Thanks, Chi. Updating the doc
|
|
||
|  | ||
|
|
||
| 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. |
There was a problem hiding this comment.
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
|
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 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 The two docs you pointed at are updated. Looking for them turned up a third: Ready for another look. |

What?
Consolidates how widget
actionsare described across the docs, the anatomy story, the architecture doc, and theWidgetActiontype. No runtime change.Four of the five places that describe an action defined it as a link:
Only
actions.mdkept the model the field was designed around, an envelope plus one fulfillment, and even there the example contradicted it.Part of #77616.
Why?
actionsshipped 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.mdsaid "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.mdnames 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.svgfor the three decisions,action-surfaces.svgfor one declaration materialized by three surfaces, which is also where the command palette degradation is written down.actions.mdxnow maps both through the bundler like the other story wrappers do.Smaller fixes along the way.
downloadandopenInNewTabnow say they belong to the link rather than the envelope.three-layers.svglisted Representation as "attributes · example · render" and omittedactions.Testing
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.
Follow-ups
WidgetDashboard.Actionsis 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 torelevance, so a host can surface an action beyond the widget.callbackandstepsfulfillments, from Dashboard Widgets: declarative actions in the widget schema #80363.