Skip to content

[Feature] Allow DropdownMenuItem to render as a div via as:#427

Merged
cirdes merged 1 commit into
ruby-ui:mainfrom
jacksonpires:feature/dropdown-menu-item-as-div
Jun 15, 2026
Merged

[Feature] Allow DropdownMenuItem to render as a div via as:#427
cirdes merged 1 commit into
ruby-ui:mainfrom
jacksonpires:feature/dropdown-menu-item-as-div

Conversation

@jacksonpires

@jacksonpires jacksonpires commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an as: option to RubyUI::DropdownMenuItem (default :a, option :div).

By default a DropdownMenuItem renders as <a href>. When an item needs to host its own interactive element — a dialog trigger, a form, etc. — wrapping it in the item's anchor produces a <button> (or <form>) nested inside an <a>. That is invalid HTML and a keyboard/accessibility problem (nested interactive elements). With as: :div the item renders as <div role="menuitem">, so the interactive child lives in a valid div > trigger > button structure while the item keeps its styling, role="menuitem", keyboard navigation and close-on-click behavior.

This mirrors the as: option recently added to TabsTrigger.

Behavior

  • Default is unchanged: DropdownMenuItem still renders <a href="#">, so existing items are unaffected (backward compatible).
  • DropdownMenuItem(as: :div) renders <div role="menuitem"> with the same classes/data-*/role and no href.
  • href is tied to the rendered element (unless @as == :div), so an unexpected/mistyped as: value still falls back to a valid <a href> rather than an anchor missing its href.

Usage

DropdownMenu do
  DropdownMenuTrigger(class: "w-full") { Button(variant: :outline) { "Open" } }
  DropdownMenuContent do
    DropdownMenuItem(href: "#") { "Edit" }
    DropdownMenuItem(as: :div) do
      Dialog do
        DialogTrigger(class: "w-full text-left") { "Delete" }
        DialogContent { "…" }
      end
    end
  end
end

Changes

  • gem/lib/ruby_ui/dropdown_menu/dropdown_menu_item.rb — add as: option, conditional element rendering, and href tied to the rendered element.
  • gem/test/ruby_ui/dropdown_menu_item_test.rb — new tests: renders as <a href> by default, renders as <div role="menuitem"> without href when as: :div, and an unexpected as: value still renders a valid <a href>.
  • docs/app/views/docs/dropdown_menu.rb — add a "Non-navigational item" example.
  • mcp/data/registry.json — rebuilt to reflect the component change.

How to test

cd gem
bundle exec rake test TEST=test/ruby_ui/dropdown_menu_item_test.rb
bundle exec rake   # full suite + standardrb

🤖 Generated with Claude Code


Summary by cubic

Adds an as: option to DropdownMenuItem so items can render as a <div> for non-navigational content (e.g. dialog or form triggers) without nesting interactive elements. Default remains <a href>, so this is backward compatible.

  • New Features
    • DropdownMenuItem(as: :div) renders <div role="menuitem"> and omits href.
    • Keeps styling, role="menuitem", keyboard navigation, and close-on-click.
    • href only applies to <a>; unexpected as: values fall back to a valid <a href>.
    • Added a docs example and tests.

Written for commit 5b3e3c2. Summary will update on new commits.

Review in cubic

Add an `as:` option to `RubyUI::DropdownMenuItem` (default `:a`, option
`:div`) so an item can host its own interactive element (e.g. a dialog
or form trigger) without nesting a `<button>`/`<form>` inside the item's
`<a>` (invalid HTML and a keyboard/accessibility issue). The `:div`
variant keeps the menu-item styling, `role="menuitem"` and behavior, and
drops `href`. `href` is tied to the rendered element (set unless `:div`),
so an unexpected `as:` value still produces a valid `<a href>`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jacksonpires jacksonpires requested a review from cirdes as a code owner June 15, 2026 19:26
@cirdes cirdes merged commit d799b7a into ruby-ui:main Jun 15, 2026
7 of 8 checks passed
djalmaaraujo added a commit that referenced this pull request Jun 16, 2026
* [Bug Fix] Fix DropdownMenu docs example: replace nonexistent DialogClose

The 'Non-navigational item' example (added in #427) referenced a
DialogClose component that does not exist in the gem. VisualCodeExample
evaluates the snippet at render time, so it raised NoMethodError and the
whole /docs/dropdown_menu page failed to render.

Close the dialog with the documented pattern instead: a Button wired to
click->ruby-ui--dialog#dismiss (same approach used in dialog_docs.rb).

Fixes #428

* [Bug Fix] Dispatch exceptions through the router so 500s aren't masked as 404

config.exceptions_app pointed at a lambda that always rendered the
not_found action, so every unhandled error (including 500s such as a
raising view) surfaced as a 404. That masked the DropdownMenu render
error behind a misleading 'not found' page.

Point exceptions_app at the router instead; the /404 and /500 routes
already exist, so the response status now matches the actual error and
ErrorsController#internal_server_error is finally reachable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants