From 993d0cccb5c7166f982e52da104ab550e941ae55 Mon Sep 17 00:00:00 2001 From: "[._.]/ Adam Eivy" Date: Fri, 12 Jun 2026 14:53:51 -0700 Subject: [PATCH] build(deps): upgrade to React 19 (coordinated with react-leaflet 5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps react, react-dom, and @types/react(-dom) to 19 in the client. React 19 couldn't land via the standalone Dependabot PRs (#88 bumped react without react-dom, and react-leaflet@4 pins react to ^18) — react-leaflet must move to v5 for React 19 support, so all four are upgraded together. The only react-leaflet API used (MapContainer/TileLayer/Marker/Polyline/Popup/useMap) is unchanged in v5. React 19 type fixes: useRef() now requires an initial arg (-> useRef(undefined), 3 sites), JSX.Element namespace import in FamilyUnitCard, and an explicit React.MouseEvent annotation on a Link onClick. Supersedes Dependabot #88 (react 19) and #104 (react-dom 19). Verified: full build passes; the 18 failing tests are pre-existing better-sqlite3 native-binding failures on main, unrelated to this change. --- client/package.json | 10 +- client/src/components/ai/AiDiscoveryModal.tsx | 2 +- .../ancestry-tree/FamilyUnitCard.tsx | 1 + .../person/LinkRelationshipDialog.tsx | 2 +- .../components/person/RelationshipModal.tsx | 2 +- client/src/components/ui/CopyButton.tsx | 2 +- package-lock.json | 142 ++++++++++-------- 7 files changed, 93 insertions(+), 68 deletions(-) diff --git a/client/package.json b/client/package.json index 55e992b..4b84f39 100644 --- a/client/package.json +++ b/client/package.json @@ -13,10 +13,10 @@ "leaflet": "^1.9.4", "lucide-react": "^1.14.0", "portos-ai-toolkit": "^0.8.4", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.5", + "react-dom": "^19.2.7", "react-hot-toast": "^2.6.0", - "react-leaflet": "^4.2.1", + "react-leaflet": "^5.0.0", "react-router-dom": "^7.16.0", "socket.io-client": "^4.8.3", "zustand": "^5.0.12" @@ -26,8 +26,8 @@ "@testing-library/react": "^16.3.2", "@types/d3": "^7.4.3", "@types/leaflet": "^1.9.21", - "@types/react": "^18.3.28", - "@types/react-dom": "^18.3.7", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "tailwindcss": "^4.3.1", "typescript": "^6.0.2", diff --git a/client/src/components/ai/AiDiscoveryModal.tsx b/client/src/components/ai/AiDiscoveryModal.tsx index 08867d2..deb3a50 100644 --- a/client/src/components/ai/AiDiscoveryModal.tsx +++ b/client/src/components/ai/AiDiscoveryModal.tsx @@ -372,7 +372,7 @@ export function AiDiscoveryModal({ dbId, onClose, onComplete }: AiDiscoveryModal
e.stopPropagation()} + onClick={(e: React.MouseEvent) => e.stopPropagation()} className="font-medium text-app-text hover:text-app-accent transition-colors" > {candidate.name} diff --git a/client/src/components/ancestry-tree/FamilyUnitCard.tsx b/client/src/components/ancestry-tree/FamilyUnitCard.tsx index 778b80a..9014b54 100644 --- a/client/src/components/ancestry-tree/FamilyUnitCard.tsx +++ b/client/src/components/ancestry-tree/FamilyUnitCard.tsx @@ -1,6 +1,7 @@ import type { AncestryFamilyUnit } from '@fsf/shared'; import { PersonCard } from './PersonCard'; import { useRef, useEffect, useState } from 'react'; +import type { JSX } from 'react'; interface FamilyUnitCardProps { unit: AncestryFamilyUnit; diff --git a/client/src/components/person/LinkRelationshipDialog.tsx b/client/src/components/person/LinkRelationshipDialog.tsx index 73ed512..0806750 100644 --- a/client/src/components/person/LinkRelationshipDialog.tsx +++ b/client/src/components/person/LinkRelationshipDialog.tsx @@ -37,7 +37,7 @@ export function LinkRelationshipDialog({ open, dbId, personId, defaultType, onCl const [saving, setSaving] = useState(false); const inputRef = useRef(null); - const searchTimerRef = useRef>(); + const searchTimerRef = useRef>(undefined); useEffect(() => { if (open) { diff --git a/client/src/components/person/RelationshipModal.tsx b/client/src/components/person/RelationshipModal.tsx index d43904b..08c4f01 100644 --- a/client/src/components/person/RelationshipModal.tsx +++ b/client/src/components/person/RelationshipModal.tsx @@ -38,7 +38,7 @@ export function RelationshipModal({ open, dbId, personId, initialType, onClose, const [newName, setNewName] = useState(''); const [newGender, setNewGender] = useState<'male' | 'female' | 'unknown'>('unknown'); const inputRef = useRef(null); - const debounceRef = useRef>(); + const debounceRef = useRef>(undefined); // Monotonically increasing request id so out-of-order responses from // earlier searches don't overwrite results from a newer search. const searchRequestIdRef = useRef(0); diff --git a/client/src/components/ui/CopyButton.tsx b/client/src/components/ui/CopyButton.tsx index 05a9ff3..8a7ab2a 100644 --- a/client/src/components/ui/CopyButton.tsx +++ b/client/src/components/ui/CopyButton.tsx @@ -11,7 +11,7 @@ interface CopyButtonProps { export function CopyButton({ text, label = 'Copied!', size = 14, className = '' }: CopyButtonProps) { const [copied, setCopied] = useState(false); - const timeoutRef = useRef>(); + const timeoutRef = useRef>(undefined); useEffect(() => { return () => clearTimeout(timeoutRef.current); diff --git a/package-lock.json b/package-lock.json index e0cc02d..a38a786 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,10 +40,10 @@ "leaflet": "^1.9.4", "lucide-react": "^1.14.0", "portos-ai-toolkit": "^0.8.4", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.5", + "react-dom": "^19.2.7", "react-hot-toast": "^2.6.0", - "react-leaflet": "^4.2.1", + "react-leaflet": "^5.0.0", "react-router-dom": "^7.16.0", "socket.io-client": "^4.8.3", "zustand": "^5.0.12" @@ -53,14 +53,86 @@ "@testing-library/react": "^16.3.2", "@types/d3": "^7.4.3", "@types/leaflet": "^1.9.21", - "@types/react": "^18.3.28", - "@types/react-dom": "^18.3.7", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "tailwindcss": "^4.3.1", "typescript": "^6.0.2", "vite": "^8.0.10" } }, + "client/node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "client/node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "client/node_modules/@types/react-dom": { + "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": "^19.2.0" + } + }, + "client/node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "client/node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "client/node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "client/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==", + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -1224,17 +1296,6 @@ "debug": "^4.3.1" } }, - "node_modules/@react-leaflet/core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", - "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", - "license": "Hippocratic-2.1", - "peerDependencies": { - "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", @@ -2320,13 +2381,6 @@ "undici-types": ">=7.24.0 <7.24.7" } }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "devOptional": true, - "license": "MIT" - }, "node_modules/@types/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", @@ -2341,27 +2395,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/react": { - "version": "18.3.31", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", - "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "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==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", @@ -5752,6 +5785,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", + "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5763,7 +5797,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lru-cache": { "version": "7.18.3", @@ -6877,6 +6912,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" }, @@ -6889,6 +6925,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" @@ -6922,20 +6959,6 @@ "license": "MIT", "peer": true }, - "node_modules/react-leaflet": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", - "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", - "license": "Hippocratic-2.1", - "dependencies": { - "@react-leaflet/core": "^2.1.0" - }, - "peerDependencies": { - "leaflet": "^1.9.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, "node_modules/react-router": { "version": "7.17.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz", @@ -7311,6 +7334,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" }