Skip to content

Commit 6838513

Browse files
committed
wip
1 parent 297a943 commit 6838513

File tree

2 files changed

+147
-1
lines changed

2 files changed

+147
-1
lines changed

packages/compass-context-menu/src/context-menu-provider.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ export const Context = createContext<ContextMenuContext | null>(null);
1414

1515
export function ContextMenuProvider({
1616
children,
17-
}: React.PropsWithChildren<never>) {
17+
}: {
18+
children: React.ReactNode;
19+
}) {
1820
const [menu, setMenu] = useState<MenuState>({ isOpen: false });
1921
const close = useCallback(() => setMenu({ isOpen: false }), [setMenu]);
2022

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import React from 'react';
2+
import {
3+
render,
4+
screen,
5+
cleanup,
6+
userEvent,
7+
} from '@mongodb-js/testing-library-compass';
8+
import { expect } from 'chai';
9+
import sinon from 'sinon';
10+
import { useContextMenu } from './use-context-menu';
11+
import { ContextMenuProvider } from './context-menu-provider';
12+
import type { MenuItem } from './types';
13+
14+
describe('useContextMenu', function () {
15+
const TestMenu: React.FC<{ items: MenuItem[] }> = ({ items }) => (
16+
<div data-testid="test-menu">
17+
{items.map((item, idx) => (
18+
<div key={idx} data-testid={`menu-item-${item.label}`}>
19+
{item.label}
20+
</div>
21+
))}
22+
</div>
23+
);
24+
25+
const TestComponent = ({
26+
onRegister,
27+
}: {
28+
onRegister?: (ref: any) => void;
29+
}) => {
30+
const contextMenu = useContextMenu({ Menu: TestMenu });
31+
const items: MenuItem[] = [
32+
{
33+
label: 'Test Item',
34+
onAction: () => {
35+
/* noop */
36+
},
37+
},
38+
];
39+
const ref = contextMenu.registerItems(items);
40+
41+
React.useEffect(() => {
42+
onRegister?.(ref);
43+
}, [ref, onRegister]);
44+
45+
return (
46+
<div data-testid="test-trigger" ref={ref}>
47+
Test Component
48+
</div>
49+
);
50+
};
51+
52+
afterEach(cleanup);
53+
54+
describe('when used outside provider', function () {
55+
it('throws an error', function () {
56+
expect(() => {
57+
render(<TestComponent />);
58+
}).to.throw('useContextMenu called outside of the provider');
59+
});
60+
});
61+
62+
describe('when used inside provider', function () {
63+
beforeEach(() => {
64+
// Create the container for the context menu portal
65+
const container = document.createElement('div');
66+
container.id = 'context-menu-container';
67+
document.body.appendChild(container);
68+
});
69+
70+
afterEach(() => {
71+
// Clean up the container
72+
const container = document.getElementById('context-menu-container');
73+
if (container) {
74+
document.body.removeChild(container);
75+
}
76+
});
77+
78+
it('renders without error', function () {
79+
render(
80+
<ContextMenuProvider>
81+
<TestComponent />
82+
</ContextMenuProvider>
83+
);
84+
85+
expect(screen.getByTestId('test-trigger')).to.exist;
86+
});
87+
88+
it('registers context menu event listener', function () {
89+
const onRegister = sinon.spy();
90+
91+
render(
92+
<ContextMenuProvider>
93+
<TestComponent onRegister={onRegister} />
94+
</ContextMenuProvider>
95+
);
96+
97+
expect(onRegister).to.have.been.calledOnce;
98+
expect(onRegister.firstCall.args[0]).to.be.a('function');
99+
});
100+
101+
it('shows context menu on right click', function () {
102+
render(
103+
<ContextMenuProvider>
104+
<TestComponent />
105+
</ContextMenuProvider>
106+
);
107+
108+
const trigger = screen.getByTestId('test-trigger');
109+
userEvent.click(trigger, { button: 2 });
110+
111+
// The menu should be rendered in the portal
112+
expect(screen.getByTestId('menu-item-Test Item')).to.exist;
113+
});
114+
115+
it('cleans up previous event listener when ref changes', function () {
116+
const removeEventListenerSpy = sinon.spy();
117+
const addEventListenerSpy = sinon.spy();
118+
119+
const { rerender } = render(
120+
<ContextMenuProvider>
121+
<TestComponent />
122+
</ContextMenuProvider>
123+
);
124+
125+
// Simulate ref change
126+
const ref = screen.getByTestId('test-trigger');
127+
Object.defineProperty(ref, 'addEventListener', {
128+
value: addEventListenerSpy,
129+
});
130+
Object.defineProperty(ref, 'removeEventListener', {
131+
value: removeEventListenerSpy,
132+
});
133+
134+
rerender(
135+
<ContextMenuProvider>
136+
<TestComponent />
137+
</ContextMenuProvider>
138+
);
139+
140+
expect(removeEventListenerSpy).to.have.been.calledWith('contextmenu');
141+
expect(addEventListenerSpy).to.have.been.calledWith('contextmenu');
142+
});
143+
});
144+
});

0 commit comments

Comments
 (0)