Skip to content

Commit ed6155d

Browse files
authored
feat(kernel-ui,extension): downgrade React for MetaMask extension compatibility (#557)
Closes #556 The kernel control panel will be loaded within the MetaMask extension environment. To prevent React version conflicts and ensure seamless integration, we need to align our React version with MetaMask's current React version. This PR downgrades React version in `kernel-ui` and `extension` packages from v18 to v17 to ensure compatibility with MetaMask extension's React version.
1 parent dd82de0 commit ed6155d

13 files changed

Lines changed: 255 additions & 129 deletions

packages/extension/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"@metamask/ocap-kernel": "workspace:^",
5353
"@metamask/streams": "workspace:^",
5454
"@metamask/utils": "^11.4.0",
55-
"react-dom": "^18.3.1",
55+
"react": "^17.0.2",
56+
"react-dom": "^17.0.2",
5657
"ses": "^1.13.0"
5758
},
5859
"devDependencies": {
@@ -67,8 +68,8 @@
6768
"@playwright/test": "^1.51.1",
6869
"@testing-library/jest-dom": "^6.6.3",
6970
"@types/chrome": "^0.0.313",
70-
"@types/react": "^18.3.1",
71-
"@types/react-dom": "^18.3.1",
71+
"@types/react": "^17.0.11",
72+
"@types/react-dom": "^17.0.11",
7273
"@typescript-eslint/eslint-plugin": "^8.29.0",
7374
"@typescript-eslint/parser": "^8.29.0",
7475
"@typescript-eslint/utils": "^8.29.0",
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { App } from '@metamask/kernel-ui';
2-
import { createRoot } from 'react-dom/client';
2+
import { render } from 'react-dom';
33
import { describe, it, expect, vi, beforeEach } from 'vitest';
44

5-
vi.mock('react-dom/client', () => ({
6-
createRoot: vi.fn(() => ({
7-
render: vi.fn(),
8-
})),
5+
vi.mock('react-dom', () => ({
6+
render: vi.fn(),
97
}));
108

119
vi.mock('@metamask/kernel-ui', () => ({
@@ -22,10 +20,10 @@ describe('popup', () => {
2220
root.id = 'root';
2321
document.body.appendChild(root);
2422
await import('./popup.tsx');
25-
expect(createRoot).toHaveBeenCalledWith(root);
26-
const mockRoot = vi.mocked(createRoot).mock.results[0]?.value;
27-
expect(mockRoot.render).toHaveBeenCalledWith(expect.any(Object));
28-
const renderCall = mockRoot.render.mock.calls[0][0];
29-
expect(renderCall.type).toBe(App);
23+
expect(render).toHaveBeenCalledWith(expect.any(Object), root);
24+
const renderArgs = vi.mocked(render).mock.calls[0];
25+
expect(renderArgs).toBeDefined();
26+
expect(renderArgs?.[0]).toBeDefined();
27+
expect((renderArgs?.[0] as unknown as React.ReactElement)?.type).toBe(App);
3028
});
3129
});

packages/extension/src/popup.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import '@metamask/kernel-ui/styles.css';
22
import { App } from '@metamask/kernel-ui';
3-
import { createRoot } from 'react-dom/client';
3+
import { createElement } from 'react';
4+
import { render } from 'react-dom';
45

5-
// @ts-expect-error - our root element is not null
6-
const root = createRoot(document.getElementById('root'));
7-
root.render(<App />);
6+
render(createElement(App), document.getElementById('root'));

packages/kernel-ui/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@
6565
"@metamask/logger": "workspace:^",
6666
"@metamask/ocap-kernel": "workspace:^",
6767
"@metamask/utils": "^11.4.0",
68-
"react": "^18.3.1",
69-
"react-dom": "^18.3.1",
68+
"react": "^17.0.2",
69+
"react-dom": "^17.0.2",
7070
"ses": "^1.13.0"
7171
},
7272
"devDependencies": {
@@ -76,15 +76,15 @@
7676
"@metamask/eslint-config-nodejs": "^14.0.0",
7777
"@metamask/eslint-config-typescript": "^14.0.0",
7878
"@ocap/test-utils": "workspace:^",
79-
"@testing-library/dom": "^10.4.0",
8079
"@testing-library/jest-dom": "^6.6.3",
81-
"@testing-library/react": "^16.3.0",
80+
"@testing-library/react": "^12.1.5",
81+
"@testing-library/react-hooks": "^8.0.1",
8282
"@testing-library/user-event": "^14.6.1",
8383
"@ts-bridge/cli": "^0.6.3",
8484
"@ts-bridge/shims": "^0.1.1",
8585
"@types/chrome": "^0.0.313",
86-
"@types/react": "^18.3.1",
87-
"@types/react-dom": "^18.3.1",
86+
"@types/react": "^17.0.11",
87+
"@types/react-dom": "^17.0.11",
8888
"@typescript-eslint/eslint-plugin": "^8.29.0",
8989
"@typescript-eslint/parser": "^8.29.0",
9090
"@typescript-eslint/utils": "^8.29.0",

packages/kernel-ui/src/context/PanelContext.test.tsx

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { renderHook, waitFor } from '@testing-library/react';
1+
import { waitFor } from '@testing-library/react';
2+
import { renderHook } from '@testing-library/react-hooks';
23
import { describe, it, expect, vi } from 'vitest';
34

45
vi.mock('@metamask/kernel-utils', () => ({
@@ -27,10 +28,6 @@ describe('PanelContext', () => {
2728
const { PanelProvider, usePanelContext } = await import(
2829
'./PanelContext.tsx'
2930
);
30-
const payload = {
31-
method: 'getStatus',
32-
params: [],
33-
};
3431
const response = { success: true };
3532
mockSendMessage.mockResolvedValueOnce(response);
3633
vi.mocked(
@@ -43,19 +40,17 @@ describe('PanelContext', () => {
4340
</PanelProvider>
4441
),
4542
});
46-
const actualResponse = await result.current.callKernelMethod(payload);
47-
expect(mockSendMessage).toHaveBeenCalledWith(payload);
43+
const actualResponse = await result.current.callKernelMethod({
44+
method: 'getStatus',
45+
params: [],
46+
});
4847
expect(actualResponse).toBe(response);
4948
});
5049

5150
it('should throw error when response is an error', async () => {
5251
const { PanelProvider, usePanelContext } = await import(
5352
'./PanelContext.tsx'
5453
);
55-
const payload = {
56-
method: 'getStatus',
57-
params: [],
58-
};
5954
const errorResponse = { error: 'Test error' };
6055
mockSendMessage.mockResolvedValueOnce(errorResponse);
6156
vi.mocked(
@@ -68,9 +63,12 @@ describe('PanelContext', () => {
6863
</PanelProvider>
6964
),
7065
});
71-
await expect(result.current.callKernelMethod(payload)).rejects.toThrow(
72-
JSON.stringify(errorResponse.error),
73-
);
66+
await expect(
67+
result.current.callKernelMethod({
68+
method: 'getStatus',
69+
params: [],
70+
}),
71+
).rejects.toThrow(JSON.stringify(errorResponse.error));
7472
expect(
7573
vi.mocked(await import('../services/logger.ts')).logger.error,
7674
).toHaveBeenCalledWith(
@@ -83,10 +81,6 @@ describe('PanelContext', () => {
8381
const { PanelProvider, usePanelContext } = await import(
8482
'./PanelContext.tsx'
8583
);
86-
const payload = {
87-
method: 'getStatus',
88-
params: [],
89-
};
9084
const error = new Error('Network error');
9185
mockSendMessage.mockRejectedValueOnce(error);
9286
const { result } = renderHook(() => usePanelContext(), {
@@ -96,9 +90,12 @@ describe('PanelContext', () => {
9690
</PanelProvider>
9791
),
9892
});
99-
await expect(result.current.callKernelMethod(payload)).rejects.toThrow(
100-
error,
101-
);
93+
await expect(
94+
result.current.callKernelMethod({
95+
method: 'getStatus',
96+
params: [],
97+
}),
98+
).rejects.toThrow(error);
10299
expect(
103100
vi.mocked(await import('../services/logger.ts')).logger.error,
104101
).toHaveBeenCalledWith(`Error: ${error.message}`, 'error');
@@ -108,14 +105,6 @@ describe('PanelContext', () => {
108105
const { PanelProvider, usePanelContext } = await import(
109106
'./PanelContext.tsx'
110107
);
111-
const firstPayload = {
112-
method: 'getStatus',
113-
params: [],
114-
};
115-
const secondPayload = {
116-
method: 'getStatus',
117-
params: [],
118-
};
119108

120109
// Use a promise that we control to ensure the first request is still in progress
121110
let resolveFirstRequest!: (value: { success: boolean }) => void;
@@ -136,12 +125,17 @@ describe('PanelContext', () => {
136125
});
137126

138127
// Start the first request but don't await it
139-
const firstRequestPromiseResult =
140-
result.current.callKernelMethod(firstPayload);
128+
const firstRequestPromiseResult = result.current.callKernelMethod({
129+
method: 'getStatus',
130+
params: [],
131+
});
141132

142133
// Try to make a second request while the first is still processing
143134
await expect(
144-
result.current.callKernelMethod(secondPayload),
135+
result.current.callKernelMethod({
136+
method: 'getStatus',
137+
params: [],
138+
}),
145139
).rejects.toThrow('A request is already in progress');
146140

147141
// Resolve the first request to clean up
@@ -176,17 +170,8 @@ describe('PanelContext', () => {
176170
describe('usePanelContext', () => {
177171
it('should throw error when used outside of PanelProvider', async () => {
178172
const { usePanelContext } = await import('./PanelContext.tsx');
179-
180-
// Use a try-catch block to verify the error
181-
let caughtError: Error | null = null;
182-
try {
183-
renderHook(() => usePanelContext());
184-
} catch (error) {
185-
caughtError = error as Error;
186-
}
187-
188-
// Expect the error to be thrown
189-
expect(caughtError).toStrictEqual(
173+
const { result } = renderHook(() => usePanelContext());
174+
expect(result.error).toStrictEqual(
190175
new Error('usePanelContext must be used within a PanelProvider'),
191176
);
192177
});

packages/kernel-ui/src/hooks/useDatabase.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { renderHook, waitFor } from '@testing-library/react';
1+
import { waitFor } from '@testing-library/react';
2+
import { renderHook } from '@testing-library/react-hooks';
23
import { describe, it, expect, vi, beforeEach } from 'vitest';
34

45
import { useDatabase } from './useDatabase.ts';

packages/kernel-ui/src/hooks/useKernelActions.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import clusterConfig from '@metamask/kernel-browser-runtime/default-cluster' assert { type: 'json' };
2-
import { renderHook, waitFor } from '@testing-library/react';
2+
import { waitFor } from '@testing-library/react';
3+
import { renderHook } from '@testing-library/react-hooks';
34
import { describe, it, expect, vi, beforeEach } from 'vitest';
45

56
vi.mock('../context/PanelContext.tsx', () => ({

packages/kernel-ui/src/hooks/useRegistry.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { renderHook, waitFor } from '@testing-library/react';
1+
import { waitFor } from '@testing-library/react';
2+
import { renderHook } from '@testing-library/react-hooks';
23
import { describe, it, expect, vi, beforeEach } from 'vitest';
34

45
import { useRegistry } from './useRegistry.ts';

packages/kernel-ui/src/hooks/useStatusPolling.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import clusterConfig from '@metamask/kernel-browser-runtime/default-cluster' assert { type: 'json' };
2-
import { renderHook, waitFor } from '@testing-library/react';
2+
import { waitFor } from '@testing-library/react';
3+
import { renderHook } from '@testing-library/react-hooks';
34
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
45

56
vi.mock('../services/logger.ts', () => ({

packages/kernel-ui/src/hooks/useStream.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { renderHook, waitFor } from '@testing-library/react';
1+
import { waitFor } from '@testing-library/react';
2+
import { renderHook } from '@testing-library/react-hooks';
23
import { describe, it, expect, vi } from 'vitest';
34

45
import { useStream } from './useStream.ts';

0 commit comments

Comments
 (0)