diff --git a/src/libs/DebugDashboard.js b/src/libs/DebugDashboard.js index 75239128..0ed88ec7 100644 --- a/src/libs/DebugDashboard.js +++ b/src/libs/DebugDashboard.js @@ -4,9 +4,27 @@ */ import React, { Suspense, lazy } from 'react'; -import { createRoot } from 'react-dom/client'; +import * as ReactDOM from 'react-dom'; import { subscribe } from 'subscribe-ui-event'; +// react-dom/client only exists in React 18+, fall back to the legacy API for React 16/17 +let createRoot = null; +try { + // eslint-disable-next-line global-require + ({ createRoot } = require('react-dom/client')); +} catch { + createRoot = null; +} + +/* eslint-disable react/no-deprecated */ +const legacyRoot = (container) => ({ + render: (...args) => { + ReactDOM.render(...args); + }, + unmount: () => ReactDOM.unmountComponentAtNode(container) +}); +/* eslint-enable react/no-deprecated */ + // Dashboard only used in client side, could safely defered load const Dashboard = lazy(() => import(/* webpackChunkName: "i13n-debug-dashboard" */ '../components/debug/Dashboard')); @@ -112,7 +130,7 @@ const DebugDashboard = function DebugDashboard(i13nNode) { }); }; - this.root = createRoot(container); + this.root = createRoot ? createRoot(container) : legacyRoot(container); this.root.render( React.createElement( Suspense, diff --git a/src/libs/tests/DebugDashboard.test.js b/src/libs/tests/DebugDashboard.test.js index 457d2c91..648e0568 100644 --- a/src/libs/tests/DebugDashboard.test.js +++ b/src/libs/tests/DebugDashboard.test.js @@ -17,4 +17,33 @@ describe('clickHandler', () => { expect(document.getElementById('i13n-debug-0')).toBeDefined(); debugDashboard.destroy(); }); + + it('should fall back to legacy react-dom api when react-dom/client is unavailable', () => { + const render = jest.fn((element, container, callback) => callback()); + const unmountComponentAtNode = jest.fn(); + + jest.isolateModules(() => { + jest.doMock('react-dom/client', () => { + throw new Error("Cannot find module 'react-dom/client'"); + }); + jest.doMock('react-dom', () => ({ render, unmountComponentAtNode })); + + /* eslint-disable global-require */ + const LegacyDebugDashboard = require('../DebugDashboard').default; + const LegacyI13nNode = require('../../core/I13nNode').default; + /* eslint-enable global-require */ + + const i13nNode = new LegacyI13nNode(null, {}); + i13nNode.setDOMNode(document.body.appendChild(document.createElement('div'))); + const debugDashboard = new LegacyDebugDashboard(i13nNode); + const { container } = debugDashboard; + expect(render).toHaveBeenCalledWith(expect.anything(), container, expect.any(Function)); + + debugDashboard.destroy(); + expect(unmountComponentAtNode).toHaveBeenCalledWith(container); + }); + + jest.dontMock('react-dom/client'); + jest.dontMock('react-dom'); + }); });