Skip to content

fix: AppMenu unmounting after backdrop click#38972

Open
NagajyothiChukka wants to merge 3 commits into
RocketChat:developfrom
NagajyothiChukka:fix/app-menu-unmount
Open

fix: AppMenu unmounting after backdrop click#38972
NagajyothiChukka wants to merge 3 commits into
RocketChat:developfrom
NagajyothiChukka:fix/app-menu-unmount

Conversation

@NagajyothiChukka
Copy link
Copy Markdown

@NagajyothiChukka NagajyothiChukka commented Feb 24, 2026

Summary

The AppMenu (three-dot menu) currently unmounts and disappears if a user opens a modal from it and then clicks the backdrop to close that modal. This is a focus-management issue where the component loses its anchor in the DOM during the modal's lifecycle when focus attempts to restore.

The Fix

I’ve wrapped the MenuV2 component in a stable element.

Why this works:

Wrapping the menu in a persistent container provides a stable DOM anchor. Even when the modal backdrop is clicked and focus is shifted, the Box ensures the menu's position in the React tree remains occupied and stable. This prevents the component from unmounting during the focus-restoration phase.

Implementation Note

I initially attempted to resolve this by passing a ref directly to MenuV2. However, since MenuV2 does not currently support forwardRef, this triggered a TypeScript error (ts2322).

By wrapping it in a , I found that the structural stability of the wrapper alone provides the necessary DOM anchor to prevent the unmounting bug. Consequently, I was able to remove the useRef logic entirely, resulting in a cleaner, logic-free fix that keeps TypeScript happy and the DOM stable.

How to Verify

  1. Go to the Marketplace and open an App's menu (three dots).

  2. Trigger an action that opens a modal (e.g., an action requiring an add-on or a warning).

  3. Click the dark backdrop or press ESC to close the modal.

  4. Result: The three-dot menu now remains visible and properly anchored in the UI.

Screenshots

Before

Screenshot 2026-02-24 141908

After

Screenshot 2026-02-24 142709

Closes #38914

Summary by CodeRabbit

  • Refactor
    • Adjusted the marketplace menu’s internal layout to improve anchoring and rendering stability.
    • No changes to visible behavior, translations, permissions, or available menu options.

@NagajyothiChukka NagajyothiChukka requested a review from a team as a code owner February 24, 2026 10:26
@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Feb 24, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 24, 2026

⚠️ No Changeset found

Latest commit: 6066888

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

Walkthrough

Adds a DOM wrapper using Box around the existing MenuV2 in the marketplace AppMenu and introduces a ref on that wrapper; imports updated to include Box, React, and useRef. Menu logic, loading states, and item rendering remain unchanged.

Changes

Cohort / File(s) Summary
MenuV2 DOM Wrapper
apps/meteor/client/views/marketplace/AppMenu.tsx
Wrapped MenuV2 with <Box display='inline-block'> and attached a ref using useRef. Updated imports to include Box, React, and useRef. No changes to menu items, permissions, loading state, or public API signatures.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 I tucked a box around the three-dot light,
A tiny frame to keep the anchor tight.
When modals fade and backdrops sweep away,
Focus hops home — the menu’s here to stay.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: resolving the AppMenu unmounting issue after backdrop click, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR directly addresses issue #38914 by wrapping MenuV2 in a Box component with a ref to preserve the DOM anchor, preventing unmounting when modals are dismissed via backdrop click.
Out of Scope Changes check ✅ Passed All changes in AppMenu.tsx are directly related to fixing the menu unmounting issue; the Box wrapper, ref implementation, and import updates are minimal and focused on the stated objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@NagajyothiChukka NagajyothiChukka changed the title Fix: AppMenu unmounting after backdrop click (#38914) Fix: AppMenu unmounting after backdrop click Feb 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/client/views/marketplace/AppMenu.tsx`:
- Around line 16-37: Remove the inline explanatory comments in AppMenu.tsx (the
comments around menuWrapperRef and the MenuV2 wrapper) to comply with the "no
comments in implementation" rule; leave the code and refs as-is (menuWrapperRef,
useAppMenu, itemsList, onAction) and move any necessary explanation into the PR
description or external docs instead of inline comments.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50e0f90 and ebfd6a9.

📒 Files selected for processing (1)
  • apps/meteor/client/views/marketplace/AppMenu.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/marketplace/AppMenu.tsx
🧠 Learnings (1)
📚 Learning: 2025-11-17T15:07:13.273Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37398
File: packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx:357-363
Timestamp: 2025-11-17T15:07:13.273Z
Learning: In packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx, IconElement is a presentational, non-actionable element that does not require wrapping in AppIdProvider, similar to plain_text and mrkdwn renderers. Only actionable elements (those with actions, actionId, or interactive behavior) should be wrapped in AppIdProvider.

Applied to files:

  • apps/meteor/client/views/marketplace/AppMenu.tsx
🧬 Code graph analysis (1)
apps/meteor/client/views/marketplace/AppMenu.tsx (2)
apps/meteor/client/views/marketplace/hooks/useAppMenu.tsx (2)
  • useAppMenu (47-492)
  • AppMenuOption (35-41)
apps/meteor/app/ui-utils/client/lib/RoomHistoryManager.ts (1)
  • isLoading (278-281)
🔇 Additional comments (2)
apps/meteor/client/views/marketplace/AppMenu.tsx (2)

2-12: Import and props typing updates look good.


38-59: Box wrapper + ref anchoring change is solid.

Comment thread apps/meteor/client/views/marketplace/AppMenu.tsx Outdated
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

No issues found across 1 file

@NagajyothiChukka NagajyothiChukka changed the title Fix: AppMenu unmounting after backdrop click fix: AppMenu unmounting after backdrop click Feb 25, 2026
@ggazzo ggazzo added this to the 8.3.0 milestone Feb 25, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/meteor/client/views/marketplace/AppMenu.tsx (2)

18-18: menuWrapperRef is attached but never read — it is dead code.

The DOM-stability fix comes entirely from the Box wrapper element persisting in the React tree. The ref value (menuWrapperRef.current) is never accessed in any callback, effect, or render path, so it contributes nothing to the fix and can be removed.

🧹 Proposed cleanup
-    const menuWrapperRef = useRef<HTMLDivElement>(null);
-
     const { isLoading, isAdminUser, sections } = useAppMenu(app, isAppDetailsPage);
...
-        <Box ref={menuWrapperRef} display='inline-block'>
+        <Box display='inline-block'>

Also applies to: 37-37

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/marketplace/AppMenu.tsx` at line 18, Remove the
unused ref declaration menuWrapperRef and any ref={menuWrapperRef} attachment so
the dead code is gone, keep the Box wrapper element that provides DOM stability,
and also remove the now-unused useRef import; scan for any other similarly
unused menu refs and delete them too (ensure only the Box wrapper remains in the
render and no callbacks/effects reference menuWrapperRef).

4-4: Remove the unused React default import.

useRef and memo are already imported as named exports; the React default is never referenced. The project is configured with TypeScript's "jsx": "react-jsx" (React 18's automatic JSX transform), which does not require the default import for either JSX or named exports.

Suggested change
-import React, { memo, useRef } from 'react';
+import { memo, useRef } from 'react';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/marketplace/AppMenu.tsx` at line 4, Remove the
unused default import `React` from the import statement in AppMenu.tsx; keep the
named imports `memo` and `useRef` (i.e., change `import React, { memo, useRef }
from 'react'` to only import the named exports) so the file uses the React 18
automatic JSX transform without an unnecessary default import.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/meteor/client/views/marketplace/AppMenu.tsx`:
- Line 18: Remove the unused ref declaration menuWrapperRef and any
ref={menuWrapperRef} attachment so the dead code is gone, keep the Box wrapper
element that provides DOM stability, and also remove the now-unused useRef
import; scan for any other similarly unused menu refs and delete them too
(ensure only the Box wrapper remains in the render and no callbacks/effects
reference menuWrapperRef).
- Line 4: Remove the unused default import `React` from the import statement in
AppMenu.tsx; keep the named imports `memo` and `useRef` (i.e., change `import
React, { memo, useRef } from 'react'` to only import the named exports) so the
file uses the React 18 automatic JSX transform without an unnecessary default
import.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebfd6a9 and 4c2569d.

📒 Files selected for processing (1)
  • apps/meteor/client/views/marketplace/AppMenu.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/marketplace/AppMenu.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to **/*.{ts,tsx,js} : Avoid code comments in the implementation

Applied to files:

  • apps/meteor/client/views/marketplace/AppMenu.tsx
📚 Learning: 2026-02-24T19:09:01.522Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38974
File: apps/meteor/app/api/server/v1/im.ts:220-221
Timestamp: 2026-02-24T19:09:01.522Z
Learning: In RocketChat/Rocket.Chat OpenAPI migration PRs for apps/meteor/app/api/server/v1 endpoints, maintainers prefer to avoid any logic changes; style-only cleanups (like removing inline comments) may be deferred to follow-ups to keep scope tight.

Applied to files:

  • apps/meteor/client/views/marketplace/AppMenu.tsx
📚 Learning: 2025-11-17T15:07:13.273Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37398
File: packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx:357-363
Timestamp: 2025-11-17T15:07:13.273Z
Learning: In packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx, IconElement is a presentational, non-actionable element that does not require wrapping in AppIdProvider, similar to plain_text and mrkdwn renderers. Only actionable elements (those with actions, actionId, or interactive behavior) should be wrapped in AppIdProvider.

Applied to files:

  • apps/meteor/client/views/marketplace/AppMenu.tsx
🔇 Additional comments (1)
apps/meteor/client/views/marketplace/AppMenu.tsx (1)

35-55: Core fix LGTM — Box wrapper correctly anchors the menu in the DOM.

Wrapping MenuV2 in a stable Box container prevents the component from losing its DOM position when focus is restored after a modal backdrop click, which is the correct minimal fix for the described lifecycle conflict.

@ggazzo ggazzo removed this from the 8.3.0 milestone Feb 25, 2026
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.

More options” (three-dots) button disappears after closing Add-on Required modal via backdrop

3 participants