Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('ContentWithFallback', function () {
expect(screen.getByText('I am ready!')).to.exist;
});

it('should render nothing when content is not ready on the first render', function () {
it('should render only the context menu when content is not ready on the first render', function () {
const container = document.createElement('div');

render(
Expand All @@ -59,8 +59,10 @@ describe('ContentWithFallback', function () {
);

expect(container.children.length).to.equal(1);
const [anchorElement] = container.children;
expect(anchorElement.getAttribute('data-testid')).to.equal('context-menu');
const [contextMenuContainer] = container.children;
expect(contextMenuContainer.getAttribute('data-testid')).to.equal(
'context-menu-container'
);
});

it('should render fallback when the timeout passes', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('useContextMenuItems', function () {

// Should only find one context menu (from the parent provider)
expect(
container.querySelectorAll('[data-testid="context-menu"]')
container.querySelectorAll('[data-testid="context-menu-container"]')
).to.have.length(1);
// Should still render the trigger
expect(screen.getByTestId(menuTestTriggerId)).to.exist;
Expand Down
57 changes: 40 additions & 17 deletions packages/compass-components/src/components/context-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useEffect, useMemo, useRef } from 'react';
import React, { useMemo, useRef } from 'react';
import { Menu, MenuItem, MenuSeparator } from './leafygreen';
import { css } from '@leafygreen-ui/emotion';
import { spacing } from '@leafygreen-ui/tokens';

import {
ContextMenuProvider as ContextMenuProviderBase,
Expand All @@ -11,6 +13,19 @@ import {

export type { ContextMenuItem } from '@mongodb-js/compass-context-menu';

// TODO: Remove these once https://jira.mongodb.org/browse/LG-5013 is resolved

const menuStyles = css({
paddingTop: spacing[150],
paddingBottom: spacing[150],
});

const itemStyles = css({
paddingTop: 0,
paddingBottom: 0,
fontSize: '.8em',
});

export function ContextMenuProvider({
children,
}: {
Expand All @@ -25,32 +40,39 @@ export function ContextMenuProvider({

export function ContextMenu({ menu }: ContextMenuWrapperProps) {
const menuRef = useRef(null);
const anchorRef = useRef<HTMLDivElement>(null);

const { position, itemGroups } = menu;

useEffect(() => {
if (!menu.isOpen) {
menu.close();
}
}, [menu.isOpen]);
// TODO: Remove when https://jira.mongodb.org/browse/LG-5342 is resolved
Copy link
Member

Choose a reason for hiding this comment

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

If we want to address this when that ticket is resolved, we can create a ticket marked as Blocked and link the tickets as depends upon. Then when it's resolved that ticket will get set to open and we get reminded of it.

Fine as is, I figured I'd mention in case you haven't done that flow before.

if (anchorRef.current) {
anchorRef.current.style.left = `${position.x}px`;
anchorRef.current.style.top = `${position.y}px`;
}
Comment on lines +48 to +51
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

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

Updating anchorRef.current.style directly on every render can lead to unnecessary DOM writes. Consider wrapping these updates in a useLayoutEffect that only runs when position changes.

Suggested change
if (anchorRef.current) {
anchorRef.current.style.left = `${position.x}px`;
anchorRef.current.style.top = `${position.y}px`;
}
React.useLayoutEffect(() => {
if (anchorRef.current) {
anchorRef.current.style.left = `${position.x}px`;
anchorRef.current.style.top = `${position.y}px`;
}
}, [position]);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, we have to do this outside of a useEffect or useLayoutEffect for it to work.


return (
<div
data-testid="context-menu"
style={{
position: 'absolute',
left: position.x,
top: position.y,
// This is to ensure the menu gets positioned correctly as the left and top updates
width: 1,
height: 1,
}}
>
<div data-testid="context-menu-container">
<div
data-testid="context-menu-anchor"
ref={anchorRef}
style={{
position: 'absolute',
left: position.x,
top: position.y,
// This is to ensure the menu gets positioned correctly as the left and top updates
width: 1,
height: 1,
}}
/>
<Menu
data-testid="context-menu"
refEl={anchorRef}
ref={menuRef}
open={menu.isOpen}
setOpen={menu.close}
justify="start"
className={menuStyles}
maxHeight={Number.MAX_SAFE_INTEGER}
>
{itemGroups.map((items: ContextMenuItemGroup, groupIndex: number) => {
return (
Expand All @@ -64,6 +86,7 @@ export function ContextMenu({ menu }: ContextMenuWrapperProps) {
key={`menu-group-${groupIndex}-item-${itemIndex}`}
data-text={item.label}
data-testid={`menu-group-${groupIndex}-item-${itemIndex}`}
className={itemStyles}
onClick={(evt: React.MouseEvent) => {
item.onAction?.(evt);
menu.close();
Expand Down