Skip to content

[render preview] [Feature] Remove Popper.js — port hover_card & context_menu to Floating UI (#158)#438

Merged
cirdes merged 4 commits into
mainfrom
feature/remove-popper-158
Jun 24, 2026
Merged

[render preview] [Feature] Remove Popper.js — port hover_card & context_menu to Floating UI (#158)#438
cirdes merged 4 commits into
mainfrom
feature/remove-popper-158

Conversation

@djalmaaraujo

@djalmaaraujo djalmaaraujo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Closes #158.

What & why

tippy.js (which bundles @popperjs/core) was the last dependency pulling Popper into RubyUI, and the source of the importmap install issues described in #158. The other nine positioned components already use @floating-ui/dom; this ports the final two — HoverCard and ContextMenu — and removes Popper/tippy from the gem and the docs site entirely.

Changes

Gem (gem/)

  • Rewrote hover_card_controller.js and context_menu_controller.js on @floating-ui/dom (computePosition + autoUpdate, flip/shift/offset), dropping tippy. Keyboard nav, matchWidth, hover open/close delays, right-click trigger, outside-click + Escape close are all preserved.
  • hover_card_content.rb / context_menu_content.rb now render a real hidden, absolutely-positioned div instead of a cloned <template> (Floating UI positions a live element rather than cloning markup).
  • dependencies.yml: hover_card & context_menu now map to @floating-ui/dom.
  • javascript_utils.rb: removed pin_tippy_js (the tippy + @popperjs/core CDN importmap pins).
  • package.json: removed tippy.js.

Docs (docs/)

  • Synced both Stimulus controllers.
  • Removed tippy.js from package.json + pnpm-lock.yaml.
  • Removed the tippy/popper importmap install snippet in component_setup/manual_steps.rb.

Tests

  • New assertions that content renders a hidden positioned div, not a <template>. Full suite green (239 runs, 0 failures), StandardRB clean.

Impact on apps using these components

Components are copy-and-paste, so existing installs keep working until regenerated. Apps that update HoverCard/ContextMenu must repin their importmap (drop tippy.js + @popperjs/core, add @floating-ui/dom) and recopy the controller. The Ruby/markup API is unchanged. Note: the popup now opens below the trigger by default (with flip()), where tippy defaulted to above — same content, standard RubyUI placement.

Manual test instructions

Run the docs site (or any app with these components) and verify:

Hover Card/docs/hover_card

  1. Hover the @joeldrapper trigger → card appears (after a short delay) with avatar, bio, joined date.
  2. Move the mouse off → card hides after a short delay.
  3. Move from trigger onto the card → stays open.
  4. Scroll/resize the window while open → card stays anchored (autoUpdate).

Context Menu/docs/context_menu

  1. Right-click "Right click here" → the custom menu opens; the native browser menu does NOT appear.
  2. "Forward" is disabled; "Show Bookmarks Bar" shows a checkmark.
  3. Click any item → menu closes.
  4. Click outside the menu → it closes. Press Escape → it closes.

Regression check: confirm no console errors, and that the other Floating UI components (Popover, Tooltip, Dropdown Menu, Select, Combobox) still behave normally.

Before/after screenshots (tippy → Floating UI) were captured during development for both components — content is identical; HoverCard now opens below the trigger and ContextMenu flips to fit the viewport.


Summary by cubic

Replaced tippy.js/@popperjs/core with @floating-ui/dom for HoverCard and ContextMenu to remove Popper and resolve importmap install issues in #158. Content now renders as a hidden, absolutely positioned div and is positioned with computePosition + autoUpdate.

  • Bug Fixes

    • HoverCard: Escape now closes; focusing into the card (focusin/focusout) keeps it open.
    • ContextMenu: Close on outside right‑click (contextmenu) as well as click.
    • Both: Set initial data-state="closed" and keep it in sync with the hidden class so enter/exit animations play correctly.
    • Rebuilt mcp/data/registry.json to reflect the Floating UI migration and satisfy the CI registry check.
  • Migration

    • No Ruby/markup API changes.
    • If updating: remove tippy.js and @popperjs/core pins, add @floating-ui/dom, and recopy the controllers. Default placement opens below the trigger (with flip), where tippy defaulted to above.

Written for commit 55f882a. Summary will update on new commits.

Review in cubic

…ng UI (#158)

tippy.js (which bundles @popperjs/core) was the last dependency pulling
Popper into RubyUI and breaking importmap installs. The other nine
components already use @floating-ui/dom; this ports the final two.

Gem:
- Rewrite hover_card & context_menu Stimulus controllers on @floating-ui/dom
  (computePosition + autoUpdate, flip/shift/offset), dropping tippy.
- Content components now render a real hidden, absolutely-positioned div
  instead of a cloned <template> (Floating UI positions a live element).
- dependencies.yml: hover_card/context_menu -> @floating-ui/dom.
- javascript_utils.rb: drop pin_tippy_js (tippy + @popperjs/core CDN pins).
- package.json: drop tippy.js.

Docs:
- Sync both controllers, drop tippy.js from package.json + lockfile,
  remove the tippy/popper importmap install snippet.

Tests:
- Assert content renders a hidden positioned div, not a <template>.
@djalmaaraujo djalmaaraujo requested a review from cirdes as a code owner June 23, 2026 18:08

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

4 issues found across 14 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="gem/lib/ruby_ui/context_menu/context_menu_content.rb">

<violation number="1" location="gem/lib/ruby_ui/context_menu/context_menu_content.rb:18">
P2: Hardcoded `data_state: "open"` and `data-[state=closed]:...` CSS classes are inconsistent with controller behavior: the JavaScript controller toggles the `hidden` class but never updates `data-state`, making all closed-state animation classes unreachable and leaving semantic state stale.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread gem/lib/ruby_ui/hover_card/hover_card_controller.js
Comment thread docs/app/javascript/controllers/ruby_ui/context_menu_controller.js Outdated
Comment thread gem/lib/ruby_ui/context_menu/context_menu_content.rb
Comment thread gem/lib/ruby_ui/hover_card/hover_card_controller.js

cirdes commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

@djalmaaraujo, please take a look on cubic issues!

Fixes from PR review (cubic):
- hover_card: Escape now closes the card. Was gated behind an early
  return for the no-menu-items case, making it unreachable for the
  common hover-card usage.
- hover_card: add focusin/focusout listeners to the content element so
  moving keyboard focus into the card no longer auto-closes it.
- context_menu: dismiss on outside right-click (contextmenu), not just
  left-click, so the native menu can take over instead of leaving a
  stale menu open.
- both: controllers now toggle data-state open/closed in sync with the
  hidden class, keeping semantic state and state-based animation classes
  accurate instead of permanently "open".
CI 'MCP registry up to date' check rebuilds registry.json from ../gem and
fails on diff. Regenerated so hover_card/context_menu reflect
@floating-ui/dom (not tippy.js) and the updated component sources.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread mcp/data/registry.json Outdated
cubic flagged HoverCardContent rendering data-state=open while hidden,
which left the enter animation classes active before first show and made
semantic state inconsistent. Initial state is now 'closed' (matches the
hidden class); the controllers flip it to 'open' on show, so the
data-[state=open] enter animation actually plays on reveal. Applied to
both hover_card and context_menu content; registry.json regenerated.
@djalmaaraujo djalmaaraujo changed the title [Feature] Remove Popper.js — port hover_card & context_menu to Floating UI (#158) [Preview Render] [Feature] Remove Popper.js — port hover_card & context_menu to Floating UI (#158) Jun 23, 2026
@djalmaaraujo djalmaaraujo changed the title [Preview Render] [Feature] Remove Popper.js — port hover_card & context_menu to Floating UI (#158) [render preview] [Feature] Remove Popper.js — port hover_card & context_menu to Floating UI (#158) Jun 23, 2026
@djalmaaraujo djalmaaraujo temporarily deployed to feature/remove-popper-158 - web-1 PR #438 June 23, 2026 20:48 — with Render Destroyed
@cirdes cirdes merged commit 71b8532 into main Jun 24, 2026
8 checks passed
@cirdes cirdes deleted the feature/remove-popper-158 branch June 24, 2026 19:03
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.

Remove Popper completely

2 participants