From 2fa3a3c970ea5dba944bb666f42a1f6ec7725595 Mon Sep 17 00:00:00 2001 From: Stan Date: Thu, 15 Jan 2026 06:42:19 +0000 Subject: [PATCH] refactor: bumped react18 to react19 along with associated deps and changes needed, long time coming, fixes #72, thanks @mmastrac for providing some of the groundwork - Stan Co-Authored-By: Claude Opus 4.5 --- package-lock.json | 284 ++++++------------ package.json | 4 +- packages/fossflow-app/package.json | 8 +- packages/fossflow-app/rsbuild.config.ts | 11 + packages/fossflow-lib/jest.config.js | 8 + packages/fossflow-lib/package.json | 10 +- .../ConnectorHintTooltip.tsx | 2 +- .../DebugUtils/__tests__/LineItem.test.tsx | 10 +- .../DebugUtils/__tests__/Value.test.tsx | 10 +- .../__snapshots__/LineItem.test.tsx.snap | 4 +- .../__snapshots__/Value.test.tsx.snap | 2 +- .../ExportImageDialog/ExportImageDialog.tsx | 2 +- .../NodeSettings/NodeSettings.tsx | 2 +- .../src/components/Label/ExpandableLabel.tsx | 2 +- .../src/components/Label/Label.tsx | 2 +- .../components/Label/__tests__/Label.test.tsx | 14 +- .../LassoHintTooltip/LassoHintTooltip.tsx | 2 +- .../src/components/Renderer/Renderer.tsx | 4 +- .../Nodes/Node/IconTypes/IsometricIcon.tsx | 2 +- .../src/components/UiOverlay/UiOverlay.tsx | 2 +- .../src/hooks/useInitialDataManager.ts | 2 +- .../src/hooks/useResizeObserver.ts | 2 +- .../src/interaction/useInteractionManager.ts | 4 +- .../fossflow-lib/src/stores/modelStore.tsx | 2 +- .../fossflow-lib/src/stores/sceneStore.tsx | 2 +- .../fossflow-lib/src/stores/uiStateStore.tsx | 2 +- 26 files changed, 171 insertions(+), 228 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f1fe07..ffeb8a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fossflow-monorepo", - "version": "1.9.0", + "version": "1.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fossflow-monorepo", - "version": "1.9.0", + "version": "1.9.1", "workspaces": [ "packages/*" ], @@ -18,8 +18,8 @@ "@semantic-release/exec": "^7.1.0", "@semantic-release/git": "^10.0.1", "@types/node": "^18.19.129", - "@types/react": "^18.3.26", - "@types/react-dom": "^18.3.7", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "conventional-changelog-conventionalcommits": "^9.1.0", "cross-env": "^10.1.0", "semantic-release": "^25.0.2", @@ -3308,69 +3308,30 @@ "license": "MIT" }, "node_modules/@testing-library/react": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", - "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.1.tgz", + "integrity": "sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", - "@types/react-dom": "^18.0.0" + "@babel/runtime": "^7.12.5" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@testing-library/react/node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/@testing-library/react/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, "node_modules/@testing-library/user-event": { @@ -3746,23 +3707,20 @@ } }, "node_modules/@types/react": { - "version": "18.3.26", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz", - "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==", - "license": "MIT", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz", + "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, - "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/react-transition-group": { @@ -5766,10 +5724,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" }, "node_modules/d3-array": { "version": "3.2.4", @@ -6025,46 +5982,6 @@ } } }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-equal/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -6656,34 +6573,6 @@ "node": ">= 0.4" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-get-iterator/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/es-iterator-helpers": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", @@ -8861,23 +8750,6 @@ "node": ">= 0.10" } }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -14200,23 +14072,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -15276,6 +15131,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -15288,6 +15144,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -15939,6 +15796,7 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -18977,15 +18835,15 @@ } }, "packages/fossflow-app": { - "version": "1.9.0", + "version": "1.9.1", "dependencies": { "@isoflow/isopacks": "^0.0.10", - "fossflow": "^1.1.0", + "fossflow": "*", "i18next": "^25.5.3", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-error-boundary": "^6.0.0", "react-i18next": "^15.7.4", "react-router-dom": "^7.9.6", @@ -18996,12 +18854,36 @@ "@rsbuild/plugin-react": "^1.4.1", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^14.3.1", + "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.6.1" } }, + "packages/fossflow-app/node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "engines": { + "node": ">=0.10.0" + } + }, + "packages/fossflow-app/node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "packages/fossflow-app/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" + }, "packages/fossflow-backend": { - "version": "1.9.0", + "version": "1.9.1", "dependencies": { "cors": "^2.8.5", "dotenv": "^16.6.1", @@ -19014,7 +18896,7 @@ }, "packages/fossflow-lib": { "name": "fossflow", - "version": "1.9.0", + "version": "1.9.1", "license": "MIT", "dependencies": { "@emotion/react": "^11.14.0", @@ -19044,7 +18926,7 @@ "@rsbuild/plugin-react": "^1.4.1", "@rslib/core": "^0.19.2", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^14.3.1", + "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.6.1", "@types/chroma-js": "^2.4.5", "@types/dom-to-image": "^2.6.7", @@ -19067,8 +18949,8 @@ "jest-environment-jsdom": "^29.7.0", "jsdom": "^21.1.2", "prettier": "^3.6.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "recharts": "^2.15.4", "style-loader": "^3.3.4", "ts-jest": "^29.4.4", @@ -19076,8 +18958,8 @@ "typescript-eslint": "^8.46.0" }, "peerDependencies": { - "react": ">=17", - "react-dom": ">=17" + "react": ">=18", + "react-dom": ">=18" } }, "packages/fossflow-lib/node_modules/@eslint/eslintrc": { @@ -19628,6 +19510,25 @@ "node": ">=8" } }, + "packages/fossflow-lib/node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "engines": { + "node": ">=0.10.0" + } + }, + "packages/fossflow-lib/node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, "packages/fossflow-lib/node_modules/react-router-dom": { "version": "6.30.3", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", @@ -19658,6 +19559,11 @@ "react": ">=16.8" } }, + "packages/fossflow-lib/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" + }, "packages/fossflow-lib/node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", diff --git a/package.json b/package.json index d9f342d..63fde4a 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "@semantic-release/exec": "^7.1.0", "@semantic-release/git": "^10.0.1", "@types/node": "^18.19.129", - "@types/react": "^18.3.26", - "@types/react-dom": "^18.3.7", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "conventional-changelog-conventionalcommits": "^9.1.0", "cross-env": "^10.1.0", "semantic-release": "^25.0.2", diff --git a/packages/fossflow-app/package.json b/packages/fossflow-app/package.json index cb02337..f706e76 100644 --- a/packages/fossflow-app/package.json +++ b/packages/fossflow-app/package.json @@ -5,12 +5,12 @@ "description": "Progressive Web App for creating isometric diagrams", "dependencies": { "@isoflow/isopacks": "^0.0.10", - "fossflow": "^1.1.0", + "fossflow": "*", "i18next": "^25.5.3", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-error-boundary": "^6.0.0", "react-i18next": "^15.7.4", "react-router-dom": "^7.9.6", @@ -45,7 +45,7 @@ "@rsbuild/plugin-react": "^1.4.1", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^14.3.1", + "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.6.1" } } diff --git a/packages/fossflow-app/rsbuild.config.ts b/packages/fossflow-app/rsbuild.config.ts index 26b9cb8..fe4764e 100644 --- a/packages/fossflow-app/rsbuild.config.ts +++ b/packages/fossflow-app/rsbuild.config.ts @@ -1,11 +1,22 @@ import { defineConfig } from '@rsbuild/core'; import { pluginReact } from '@rsbuild/plugin-react'; +import path from 'path'; const publicUrl = process.env.PUBLIC_URL || ''; const assetPrefix = publicUrl ? (publicUrl.endsWith('/') ? publicUrl : publicUrl + '/') : '/'; +// Resolve React from root node_modules to avoid duplicate instances +const rootNodeModules = path.resolve(__dirname, '../../node_modules'); + export default defineConfig({ plugins: [pluginReact()], + resolve: { + alias: { + // Force React to resolve from root node_modules + 'react': path.join(rootNodeModules, 'react'), + 'react-dom': path.join(rootNodeModules, 'react-dom'), + }, + }, html: { template: './public/index.html', templateParameters: { diff --git a/packages/fossflow-lib/jest.config.js b/packages/fossflow-lib/jest.config.js index c89e865..827f034 100644 --- a/packages/fossflow-lib/jest.config.js +++ b/packages/fossflow-lib/jest.config.js @@ -4,6 +4,14 @@ module.exports = { testEnvironment: "jsdom", modulePaths: ['node_modules', ''], setupFilesAfterEnv: ['/jest.setup.js'], + moduleNameMapper: { + // Force React to resolve from root node_modules to avoid duplicate React instances + "^react$": "/../../node_modules/react", + "^react-dom$": "/../../node_modules/react-dom", + "^react-dom/client$": "/../../node_modules/react-dom/client", + "^react/jsx-runtime$": "/../../node_modules/react/jsx-runtime", + "^react/jsx-dev-runtime$": "/../../node_modules/react/jsx-dev-runtime" + }, testPathIgnorePatterns: [ '/node_modules/', '/dist/', diff --git a/packages/fossflow-lib/package.json b/packages/fossflow-lib/package.json index 53ee492..5087fba 100644 --- a/packages/fossflow-lib/package.json +++ b/packages/fossflow-lib/package.json @@ -46,8 +46,8 @@ "zustand": "^4.5.7" }, "peerDependencies": { - "react": ">=17", - "react-dom": ">=17" + "react": ">=18", + "react-dom": ">=18" }, "devDependencies": { "@eslint/js": "^9.37.0", @@ -56,7 +56,7 @@ "@rsbuild/plugin-react": "^1.4.1", "@rslib/core": "^0.19.2", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^14.3.1", + "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.6.1", "@types/chroma-js": "^2.4.5", "@types/dom-to-image": "^2.6.7", @@ -79,8 +79,8 @@ "jest-environment-jsdom": "^29.7.0", "jsdom": "^21.1.2", "prettier": "^3.6.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "recharts": "^2.15.4", "style-loader": "^3.3.4", "ts-jest": "^29.4.4", diff --git a/packages/fossflow-lib/src/components/ConnectorHintTooltip/ConnectorHintTooltip.tsx b/packages/fossflow-lib/src/components/ConnectorHintTooltip/ConnectorHintTooltip.tsx index 51be420..67ac84e 100644 --- a/packages/fossflow-lib/src/components/ConnectorHintTooltip/ConnectorHintTooltip.tsx +++ b/packages/fossflow-lib/src/components/ConnectorHintTooltip/ConnectorHintTooltip.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'src/stores/localeStore'; const STORAGE_KEY = 'fossflow_connector_hint_dismissed'; interface Props { - toolMenuRef?: React.RefObject; + toolMenuRef?: React.RefObject; } export const ConnectorHintTooltip = ({ toolMenuRef }: Props) => { diff --git a/packages/fossflow-lib/src/components/DebugUtils/__tests__/LineItem.test.tsx b/packages/fossflow-lib/src/components/DebugUtils/__tests__/LineItem.test.tsx index e362b98..723519f 100644 --- a/packages/fossflow-lib/src/components/DebugUtils/__tests__/LineItem.test.tsx +++ b/packages/fossflow-lib/src/components/DebugUtils/__tests__/LineItem.test.tsx @@ -1,16 +1,22 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; +import { ThemeProvider } from '@mui/material/styles'; +import { theme } from 'src/styles/theme'; import { LineItem } from '../LineItem'; +const renderWithTheme = (ui: React.ReactElement) => { + return render({ui}); +}; + describe('LineItem', () => { it('renders title and value', () => { - render(); + renderWithTheme(); expect(screen.getByText('Test Title')).toBeInTheDocument(); expect(screen.getByText('Test Value')).toBeInTheDocument(); }); it('matches snapshot', () => { - const { asFragment } = render( + const { asFragment } = renderWithTheme( ); expect(asFragment()).toMatchSnapshot(); diff --git a/packages/fossflow-lib/src/components/DebugUtils/__tests__/Value.test.tsx b/packages/fossflow-lib/src/components/DebugUtils/__tests__/Value.test.tsx index c6d093e..206a30f 100644 --- a/packages/fossflow-lib/src/components/DebugUtils/__tests__/Value.test.tsx +++ b/packages/fossflow-lib/src/components/DebugUtils/__tests__/Value.test.tsx @@ -1,15 +1,21 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; +import { ThemeProvider } from '@mui/material/styles'; +import { theme } from 'src/styles/theme'; import { Value } from '../Value'; +const renderWithTheme = (ui: React.ReactElement) => { + return render({ui}); +}; + describe('Value', () => { it('renders value', () => { - render(); + renderWithTheme(); expect(screen.getByText('Test Value')).toBeInTheDocument(); }); it('matches snapshot', () => { - const { asFragment } = render(); + const { asFragment } = renderWithTheme(); expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/LineItem.test.tsx.snap b/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/LineItem.test.tsx.snap index 3b816e6..aa9aa1c 100644 --- a/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/LineItem.test.tsx.snap +++ b/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/LineItem.test.tsx.snap @@ -9,7 +9,7 @@ exports[`LineItem matches snapshot 1`] = ` class="MuiBox-root css-v7v99c" >

Snapshot Title

@@ -21,7 +21,7 @@ exports[`LineItem matches snapshot 1`] = ` class="MuiBox-root css-1xrtkr" >

Snapshot Value

diff --git a/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/Value.test.tsx.snap b/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/Value.test.tsx.snap index 932333e..b7747c8 100644 --- a/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/Value.test.tsx.snap +++ b/packages/fossflow-lib/src/components/DebugUtils/__tests__/__snapshots__/Value.test.tsx.snap @@ -6,7 +6,7 @@ exports[`Value matches snapshot 1`] = ` class="MuiBox-root css-1xrtkr" >

Snapshot Value

diff --git a/packages/fossflow-lib/src/components/ExportImageDialog/ExportImageDialog.tsx b/packages/fossflow-lib/src/components/ExportImageDialog/ExportImageDialog.tsx index 4329cac..89636f5 100644 --- a/packages/fossflow-lib/src/components/ExportImageDialog/ExportImageDialog.tsx +++ b/packages/fossflow-lib/src/components/ExportImageDialog/ExportImageDialog.tsx @@ -50,7 +50,7 @@ interface CropArea { } export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => { - const containerRef = useRef(); + const containerRef = useRef(null); const cropCanvasRef = useRef(null); const isExporting = useRef(false); const [isDragging, setIsDragging] = useState(false); diff --git a/packages/fossflow-lib/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx b/packages/fossflow-lib/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx index 3adef71..191f6b2 100644 --- a/packages/fossflow-lib/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx +++ b/packages/fossflow-lib/src/components/ItemControls/NodeControls/NodeSettings/NodeSettings.tsx @@ -32,7 +32,7 @@ export const NodeSettings = ({ // Local state for smooth slider interaction const currentIcon = icons.find(icon => icon.id === modelItem?.icon); const [localScale, setLocalScale] = useState(currentIcon?.scale || 1); - const debounceRef = useRef(); + const debounceRef = useRef(undefined); // Update local scale when icon changes useEffect(() => { diff --git a/packages/fossflow-lib/src/components/Label/ExpandableLabel.tsx b/packages/fossflow-lib/src/components/Label/ExpandableLabel.tsx index ebeb9e3..98e4ed0 100644 --- a/packages/fossflow-lib/src/components/Label/ExpandableLabel.tsx +++ b/packages/fossflow-lib/src/components/Label/ExpandableLabel.tsx @@ -21,7 +21,7 @@ export const ExpandableLabel = ({ const editorMode = useUiStateStore((state) => state.editorMode); const labelSettings = useUiStateStore((state) => state.labelSettings); const [isExpanded, setIsExpanded] = useState(false); - const contentRef = useRef(); + const contentRef = useRef(null); const { observe, size: contentSize } = useResizeObserver(); useEffect(() => { diff --git a/packages/fossflow-lib/src/components/Label/Label.tsx b/packages/fossflow-lib/src/components/Label/Label.tsx index 32a74e7..ac711ed 100644 --- a/packages/fossflow-lib/src/components/Label/Label.tsx +++ b/packages/fossflow-lib/src/components/Label/Label.tsx @@ -22,7 +22,7 @@ export const Label = ({ sx, showLine = true }: Props) => { - const contentRef = useRef(); + const contentRef = useRef(null); return ( { + return render({ui}); +}; + describe('Label', () => { describe('dotted line', () => { it('should render dotted line with pointerEvents none to not block clicks', () => { - const { container } = render( + const { container } = renderWithTheme( @@ -21,7 +27,7 @@ describe('Label', () => { }); it('should not render dotted line when labelHeight is 0', () => { - const { container } = render( + const { container } = renderWithTheme( @@ -32,7 +38,7 @@ describe('Label', () => { }); it('should not render dotted line when showLine is false', () => { - const { container } = render( + const { container } = renderWithTheme( @@ -43,7 +49,7 @@ describe('Label', () => { }); it('should render children correctly', () => { - render( + renderWithTheme( diff --git a/packages/fossflow-lib/src/components/LassoHintTooltip/LassoHintTooltip.tsx b/packages/fossflow-lib/src/components/LassoHintTooltip/LassoHintTooltip.tsx index 1d347a9..b1c5e93 100644 --- a/packages/fossflow-lib/src/components/LassoHintTooltip/LassoHintTooltip.tsx +++ b/packages/fossflow-lib/src/components/LassoHintTooltip/LassoHintTooltip.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'src/stores/localeStore'; const STORAGE_KEY = 'fossflow_lasso_hint_dismissed'; interface Props { - toolMenuRef?: React.RefObject; + toolMenuRef?: React.RefObject; } export const LassoHintTooltip = ({ toolMenuRef }: Props) => { diff --git a/packages/fossflow-lib/src/components/Renderer/Renderer.tsx b/packages/fossflow-lib/src/components/Renderer/Renderer.tsx index 42ce70a..8eb8bf9 100644 --- a/packages/fossflow-lib/src/components/Renderer/Renderer.tsx +++ b/packages/fossflow-lib/src/components/Renderer/Renderer.tsx @@ -18,8 +18,8 @@ import { useScene } from 'src/hooks/useScene'; import { RendererProps } from 'src/types/rendererProps'; export const Renderer = ({ showGrid, backgroundColor }: RendererProps) => { - const containerRef = useRef(); - const interactionsRef = useRef(); + const containerRef = useRef(null); + const interactionsRef = useRef(null); const enableDebugTools = useUiStateStore((state) => { return state.enableDebugTools; }); diff --git a/packages/fossflow-lib/src/components/SceneLayers/Nodes/Node/IconTypes/IsometricIcon.tsx b/packages/fossflow-lib/src/components/SceneLayers/Nodes/Node/IconTypes/IsometricIcon.tsx index 94b4c56..9a38f7d 100644 --- a/packages/fossflow-lib/src/components/SceneLayers/Nodes/Node/IconTypes/IsometricIcon.tsx +++ b/packages/fossflow-lib/src/components/SceneLayers/Nodes/Node/IconTypes/IsometricIcon.tsx @@ -10,7 +10,7 @@ interface Props { } export const IsometricIcon = ({ url, scale = 1, onImageLoaded }: Props) => { - const ref = useRef(); + const ref = useRef(null); const { size, observe, disconnect } = useResizeObserver(); useEffect(() => { diff --git a/packages/fossflow-lib/src/components/UiOverlay/UiOverlay.tsx b/packages/fossflow-lib/src/components/UiOverlay/UiOverlay.tsx index 939f035..81a221c 100644 --- a/packages/fossflow-lib/src/components/UiOverlay/UiOverlay.tsx +++ b/packages/fossflow-lib/src/components/UiOverlay/UiOverlay.tsx @@ -58,7 +58,7 @@ const getEditorModeMapping = (editorMode: keyof typeof EditorModeEnum) => { export const UiOverlay = () => { const theme = useTheme(); - const contextMenuAnchorRef = useRef(); + const contextMenuAnchorRef = useRef(null); const toolMenuRef = useRef(null); const { appPadding } = theme.customVars; const spacing = useCallback( diff --git a/packages/fossflow-lib/src/hooks/useInitialDataManager.ts b/packages/fossflow-lib/src/hooks/useInitialDataManager.ts index c6373dd..10d1c61 100644 --- a/packages/fossflow-lib/src/hooks/useInitialDataManager.ts +++ b/packages/fossflow-lib/src/hooks/useInitialDataManager.ts @@ -16,7 +16,7 @@ import { modelSchema } from 'src/schemas/model'; export const useInitialDataManager = () => { const [isReady, setIsReady] = useState(false); - const prevInitialData = useRef(); + const prevInitialData = useRef(undefined); const model = useModelStore((state) => { return state; }); diff --git a/packages/fossflow-lib/src/hooks/useResizeObserver.ts b/packages/fossflow-lib/src/hooks/useResizeObserver.ts index bd1e7eb..15e1be4 100644 --- a/packages/fossflow-lib/src/hooks/useResizeObserver.ts +++ b/packages/fossflow-lib/src/hooks/useResizeObserver.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { Size } from 'src/types'; export const useResizeObserver = (el?: HTMLElement | null) => { - const resizeObserverRef = useRef(); + const resizeObserverRef = useRef(undefined); const [size, setSize] = useState({ width: 0, height: 0 }); const disconnect = useCallback(() => { diff --git a/packages/fossflow-lib/src/interaction/useInteractionManager.ts b/packages/fossflow-lib/src/interaction/useInteractionManager.ts index c1a6496..80cdf99 100644 --- a/packages/fossflow-lib/src/interaction/useInteractionManager.ts +++ b/packages/fossflow-lib/src/interaction/useInteractionManager.ts @@ -48,8 +48,8 @@ const getModeFunction = (mode: ModeActions, e: SlimMouseEvent) => { }; export const useInteractionManager = () => { - const rendererRef = useRef(); - const reducerTypeRef = useRef(); + const rendererRef = useRef(undefined); + const reducerTypeRef = useRef(undefined); const uiState = useUiStateStore((state) => { return state; }); diff --git a/packages/fossflow-lib/src/stores/modelStore.tsx b/packages/fossflow-lib/src/stores/modelStore.tsx index ba947ec..0068dfe 100644 --- a/packages/fossflow-lib/src/stores/modelStore.tsx +++ b/packages/fossflow-lib/src/stores/modelStore.tsx @@ -169,7 +169,7 @@ interface ProviderProps { } export const ModelProvider = ({ children }: ProviderProps) => { - const storeRef = useRef>(); + const storeRef = useRef | undefined>(undefined); if (!storeRef.current) { storeRef.current = initialState(); diff --git a/packages/fossflow-lib/src/stores/sceneStore.tsx b/packages/fossflow-lib/src/stores/sceneStore.tsx index 9a73ddf..da699ed 100644 --- a/packages/fossflow-lib/src/stores/sceneStore.tsx +++ b/packages/fossflow-lib/src/stores/sceneStore.tsx @@ -166,7 +166,7 @@ interface ProviderProps { } export const SceneProvider = ({ children }: ProviderProps) => { - const storeRef = useRef>(); + const storeRef = useRef | undefined>(undefined); if (!storeRef.current) { storeRef.current = initialState(); diff --git a/packages/fossflow-lib/src/stores/uiStateStore.tsx b/packages/fossflow-lib/src/stores/uiStateStore.tsx index de1e266..074392c 100644 --- a/packages/fossflow-lib/src/stores/uiStateStore.tsx +++ b/packages/fossflow-lib/src/stores/uiStateStore.tsx @@ -141,7 +141,7 @@ interface ProviderProps { // TODO: Typings below are pretty gnarly due to the way Zustand works. // see https://github.com/pmndrs/zustand/discussions/1180#discussioncomment-3439061 export const UiStateProvider = ({ children }: ProviderProps) => { - const storeRef = useRef>(); + const storeRef = useRef | undefined>(undefined); if (!storeRef.current) { storeRef.current = initialState();