Skip to content

Commit 1ad46a7

Browse files
committed
fix: enforce no nesting, adjsut enzyme test and move setup to testing-library
1 parent 59d82d6 commit 1ad46a7

File tree

4 files changed

+119
-46
lines changed

4 files changed

+119
-46
lines changed

configs/testing-library-compass/src/index.tsx

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ import {
4545
ReadOnlyPreferenceAccess,
4646
} from 'compass-preferences-model/provider';
4747
import { TelemetryProvider } from '@mongodb-js/compass-telemetry/provider';
48-
import { CompassComponentsProvider } from '@mongodb-js/compass-components';
48+
import {
49+
CompassComponentsProvider,
50+
ContextMenuProvider,
51+
} from '@mongodb-js/compass-components';
4952
import {
5053
TestEnvCurrentConnectionContext,
5154
ConnectionInfoProvider,
@@ -349,41 +352,43 @@ function createWrapper(
349352
<PreferencesProvider value={wrapperState.preferences}>
350353
<LoggerProvider value={logger}>
351354
<TelemetryProvider options={telemetryOptions}>
352-
<ConnectionStorageProvider
353-
value={wrapperState.connectionStorage}
354-
>
355-
<ConnectFnProvider connect={wrapperState.connect}>
356-
<CompassConnections
357-
appName={options.appName ?? 'TEST'}
358-
onFailToLoadConnections={
359-
options.onFailToLoadConnections ??
360-
(() => {
361-
// noop
362-
})
363-
}
364-
onExtraConnectionDataRequest={
365-
options.onExtraConnectionDataRequest ??
366-
(() => {
367-
return Promise.resolve([{}, null] as [any, null]);
368-
})
369-
}
370-
onAutoconnectInfoRequest={
371-
options.onAutoconnectInfoRequest
372-
}
373-
preloadStorageConnectionInfos={connections}
374-
>
375-
<StoreGetter>
376-
<TestEnvCurrentConnectionContext.Provider
377-
value={TEST_ENV_CURRENT_CONNECTION}
378-
>
379-
<TestingLibraryWrapper {...props}>
380-
{children}
381-
</TestingLibraryWrapper>
382-
</TestEnvCurrentConnectionContext.Provider>
383-
</StoreGetter>
384-
</CompassConnections>
385-
</ConnectFnProvider>
386-
</ConnectionStorageProvider>
355+
<ContextMenuProvider>
356+
<ConnectionStorageProvider
357+
value={wrapperState.connectionStorage}
358+
>
359+
<ConnectFnProvider connect={wrapperState.connect}>
360+
<CompassConnections
361+
appName={options.appName ?? 'TEST'}
362+
onFailToLoadConnections={
363+
options.onFailToLoadConnections ??
364+
(() => {
365+
// noop
366+
})
367+
}
368+
onExtraConnectionDataRequest={
369+
options.onExtraConnectionDataRequest ??
370+
(() => {
371+
return Promise.resolve([{}, null] as [any, null]);
372+
})
373+
}
374+
onAutoconnectInfoRequest={
375+
options.onAutoconnectInfoRequest
376+
}
377+
preloadStorageConnectionInfos={connections}
378+
>
379+
<StoreGetter>
380+
<TestEnvCurrentConnectionContext.Provider
381+
value={TEST_ENV_CURRENT_CONNECTION}
382+
>
383+
<TestingLibraryWrapper {...props}>
384+
{children}
385+
</TestingLibraryWrapper>
386+
</TestEnvCurrentConnectionContext.Provider>
387+
</StoreGetter>
388+
</CompassConnections>
389+
</ConnectFnProvider>
390+
</ConnectionStorageProvider>
391+
</ContextMenuProvider>
387392
</TelemetryProvider>
388393
</LoggerProvider>
389394
</PreferencesProvider>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import { render } from '@mongodb-js/testing-library-compass';
3+
import { expect } from 'chai';
4+
import { ContextMenuProvider } from './context-menu-provider';
5+
import type { ContextMenuWrapperProps } from './types';
6+
7+
describe('ContextMenuProvider', function () {
8+
const TestMenu: React.FC<ContextMenuWrapperProps> = () => (
9+
<div data-testid="test-menu">Test Menu</div>
10+
);
11+
12+
const TestComponent = () => (
13+
<div data-testid="test-content">Test Content</div>
14+
);
15+
16+
describe('when nested', function () {
17+
it('throws an error when providers are nested', function () {
18+
expect(() => {
19+
render(
20+
<ContextMenuProvider wrapper={TestMenu}>
21+
<div>
22+
<ContextMenuProvider wrapper={TestMenu}>
23+
<TestComponent />
24+
</ContextMenuProvider>
25+
</div>
26+
</ContextMenuProvider>
27+
);
28+
}).to.throw(
29+
'Duplicated ContextMenuProvider found. Please remove the nested provider.'
30+
);
31+
});
32+
});
33+
34+
describe('when not nested', function () {
35+
it('renders without error', function () {
36+
render(
37+
<ContextMenuProvider wrapper={TestMenu}>
38+
<TestComponent />
39+
</ContextMenuProvider>
40+
);
41+
42+
expect(document.querySelector('[data-testid="test-content"]')).to.exist;
43+
});
44+
});
45+
});

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, {
44
useState,
55
useMemo,
66
createContext,
7+
useContext,
78
} from 'react';
89
import type { ContextMenuContext, ContextMenuState } from './types';
910
import type { EnhancedMouseEvent } from './context-menu-content';
@@ -20,6 +21,9 @@ export function ContextMenuProvider({
2021
menu: ContextMenuState & { close: () => void };
2122
}>;
2223
}) {
24+
// Check if there's already a parent context menu provider
25+
const parentContext = useContext(Context);
26+
2327
const [menu, setMenu] = useState<ContextMenuState>({
2428
isOpen: false,
2529
itemGroups: [],
@@ -37,6 +41,11 @@ export function ContextMenuProvider({
3741
);
3842

3943
useEffect(() => {
44+
// If there's a parent provider, don't add event listeners
45+
if (parentContext) {
46+
return;
47+
}
48+
4049
function handleContextMenu(event: MouseEvent) {
4150
event.preventDefault();
4251

@@ -64,7 +73,7 @@ export function ContextMenuProvider({
6473
document.removeEventListener('contextmenu', handleContextMenu);
6574
window.removeEventListener('resize', handleClosingEvent);
6675
};
67-
}, [handleClosingEvent]);
76+
}, [handleClosingEvent, parentContext]);
6877

6978
const value = useMemo(
7079
() => ({
@@ -73,6 +82,13 @@ export function ContextMenuProvider({
7382
[close]
7483
);
7584

85+
// Prevent accidental nested providers
86+
if (parentContext) {
87+
throw new Error(
88+
'Duplicated ContextMenuProvider found. Please remove the nested provider.'
89+
);
90+
}
91+
7692
const Wrapper = wrapper ?? React.Fragment;
7793

7894
return (

packages/compass-crud/src/components/document-list-view.spec.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { mount } from 'enzyme';
2+
import { ReactWrapper, mount } from 'enzyme';
33
import HadronDocument from 'hadron-document';
44
import { expect } from 'chai';
55

@@ -11,14 +11,21 @@ describe('<DocumentListView />', function () {
1111
context('when the documents have objects for ids', function () {
1212
const docs = [{ _id: { name: 'test-1' } }, { _id: { name: 'test-2' } }];
1313
const hadronDocs = docs.map((doc) => new HadronDocument(doc));
14-
const component = mount(
15-
<DocumentListView
16-
docs={hadronDocs}
17-
isEditable={false}
18-
isTimeSeries={false}
19-
/>,
20-
{ wrappingComponent: ContextMenuProvider }
21-
);
14+
let component: ReactWrapper;
15+
beforeEach(function () {
16+
component = mount(
17+
<DocumentListView
18+
docs={hadronDocs}
19+
isEditable={false}
20+
isTimeSeries={false}
21+
/>,
22+
{ wrappingComponent: ContextMenuProvider }
23+
);
24+
});
25+
26+
afterEach(function () {
27+
component?.unmount();
28+
});
2229

2330
it('renders all the documents', function () {
2431
const wrapper = component.find('[data-testid="readonly-document"]');

0 commit comments

Comments
 (0)