Hi,
Problem/Motivation
When mapping a menu (e.g. main or footer) to a Links-type prop in a UI Patterns pattern, Drupal allows the use of special tokens in the menu link URLs:
-
<front>: works as expected <nolink>: works as expected<button>: the link is completely removed from the output
This behavior is problematic, as Drupal's admin UI explicitly allows adding button-type links to menus. However, the UI Patterns module does not currently support this case properly, which prevents the button from being rendered.
Steps to reproduce
- Drupal core version: 10.4
- UI Patterns module version: 2.x
- Menu items are MenuLinkContent entities.
Links are mapped to a prop of type Links, using a schema defined by LinksPropType, which expects the following structure:
[
'type' => 'object',
'properties' => [
'title' => ['type' => 'string'],
'url' => ['$ref' => 'ui-patterns://url'],
'attributes' => ['$ref' => 'ui-patterns://attributes'],
'link_attributes' => ['$ref' => 'ui-patterns://attributes'],
'below' => [
'type' => 'array',
'items' => [
'type' => 'object',
],
],
],
]
However, when a link uses the <button> token in the Link field, the preprocessing handled by the normalize() method in UrlPropType returns an empty string ('') for the URL.
As a result, the schema validation for LinksPropType either rejects or silently ignores the item, because it no longer matches the expected format for a valid link.
This use case can be essential, particularly for accessibility reasons, such as:
- A parent menu link used only to trigger expand/collapse behavior
- A menu item that opens a consent modal
- A purely interactive navigation element that doesn’t need an actual URL
Proposed resolution
Menu items with a <button> URL should be included in the Links prop, with an identifiable format for example, a type: 'button' field or a boolean is_button allowing the front-end integrator to distinguish and render the element appropriately (e.g., using a <button> instead of an <a> tag).
Marc
Issue fork ui_patterns-3524637
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
martygraphie commentedHi,
This patch adds support for the is_button indicator on link (links) objects.
The goal is to allow templates especially within navigation components to easily detect when a
<button>type route should be rendered as a<button>HTML element.It might not be the most elegant solution, but it's simple and functional.
Example usage in a TWIG template of component:
Marc
Comment #4
pdureau commentedI am not very comfortable with this proposal. Not because of Marc's work. It seems he did a great job. But because of some weird stuff on Drupal Core side.
I will take the time to review it properly.
Comment #5
just_like_good_vibeshello,
yes we should support those special routes in some ways.
to tackle this, we need to carefully normalizeUrls and add new automatic tests to verify the support.
i will continue the job.
Comment #10
just_like_good_vibesHello, and sorry for taking so much time to come back to that issue.
i see that it gains some recent interest.
i just gave some time and effort on this use case to try to propose something.
but first, I used some basic questions to help me evaluates the proposition :
- is
route:<button>a real Drupal core concept? Yes, since Drupal 9.0 (Change record "Menu links can be buttons", from core issue #2999549: Allow button tag in LinkGenerator for better accessibility)- Is the use case legitimate? Yes. Menu items that toggle mega-menus/dropdowns must be for accessibility (a link that navigates nowhere is an a11y failure). Editors model this with a route: menu item in Drupal.
- Is the reported problem real? Yes, but let me complete the explanation.
LinksPropType::normalizeUrl()is already unsetting the url for the caseroute:<button>(same behavior asroute:<nolink>), but no marker is sets. After normalization, impossible to know if it was the route or the route . One can just remark that url is empty. button-ness is lost...about the is_button addition :
- links prop type is actually a collection of navigation items, from the UI perspective. every key is a pure UI concept.
- the addition of is_button is not a backend information (as would be the introduction of a property route_name for example) : it is an UI concept, because a navigation item in HTML and in design systems usually fall back into one of the three following cases : an anchor (for navigation), a button (to trigger a behavior), or plain text (for labels and headings).
- the current schema can encode: url present = anchor; url absent = text. Only two of the three states described above. The button state is simply unrepresentable and is_button is the minimal addition to encode that third state.
- introducting is_button is an addition and it provides some advantages : url stays unset in the button case (backward compatible, old templates behaves and degrades exactly as today), new templates can opt in with
{% if item.is_button %}i re-opened a fresh branch and pushed some codes, following the "is_button" idea.
@pdureau would you review?
Comment #11
pdureau commentedThat's right and that's why I was uncomfortable with this change
The last proposal is adding
is_buttonproperty to link items, is it orthogonal with theurlproperty?I move to RTBC anyway, because the change is simple and the feature is expected, but I am available to chit-chat if needed.
Comment #12
just_like_good_vibes@pdureau, you are totally right about the fact that, if both url is filled and is_button is true, then it is up to the component author to resolve this ambiguity and decide how to render the corresponding navigation item.
thank you for reviewing.
Comment #14
just_like_good_vibes