Browse Source

chore(frontend): Upgrade to React 19 (#5835)

sp.wack 1 year ago
parent
commit
9d984aaa30

+ 5 - 6
docs/src/pages/index.tsx

@@ -1,14 +1,13 @@
-import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
-import Layout from "@theme/Layout";
-import { HomepageHeader } from "../components/HomepageHeader/HomepageHeader";
-import { Welcome } from "../components/Welcome/Welcome";
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import Layout from '@theme/Layout';
+import { HomepageHeader } from '../components/HomepageHeader/HomepageHeader';
 import { translate } from '@docusaurus/Translate';
 
 export function Header({ title, summary }): JSX.Element {
   return (
     <div>
       <h1>{title}</h1>
-      <h2 style={{ fontSize: "3rem" }}>{summary}</h2>
+      <h2 style={{ fontSize: '3rem' }}>{summary}</h2>
     </div>
   );
 }
@@ -23,7 +22,7 @@ export default function Home(): JSX.Element {
         message: 'Code Less, Make More',
       })}
     >
-    <HomepageHeader />
+      <HomepageHeader />
     </Layout>
   );
 }

+ 64 - 60
frontend/package-lock.json

@@ -8,7 +8,7 @@
       "name": "openhands-frontend",
       "version": "0.17.0",
       "dependencies": {
-        "@monaco-editor/react": "^4.6.0",
+        "@monaco-editor/react": "^4.7.0-rc.0",
         "@nextui-org/react": "^2.6.10",
         "@react-router/node": "^7.1.1",
         "@react-router/serve": "^7.1.1",
@@ -28,8 +28,8 @@
         "jose": "^5.9.4",
         "monaco-editor": "^0.52.2",
         "posthog-js": "^1.203.1",
-        "react": "^18.3.1",
-        "react-dom": "^18.3.1",
+        "react": "^19.0.0",
+        "react-dom": "^19.0.0",
         "react-highlight": "^0.15.0",
         "react-hot-toast": "^2.4.1",
         "react-i18next": "^15.2.0",
@@ -56,8 +56,8 @@
         "@testing-library/react": "^16.1.0",
         "@testing-library/user-event": "^14.5.2",
         "@types/node": "^22.10.2",
-        "@types/react": "^18.3.11",
-        "@types/react-dom": "^18.3.0",
+        "@types/react": "^19.0.2",
+        "@types/react-dom": "^19.0.2",
         "@types/react-highlight": "^0.12.8",
         "@types/react-syntax-highlighter": "^15.5.13",
         "@types/ws": "^8.5.12",
@@ -1595,17 +1595,17 @@
       }
     },
     "node_modules/@monaco-editor/react": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz",
-      "integrity": "sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==",
+      "version": "4.7.0-rc.0",
+      "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0-rc.0.tgz",
+      "integrity": "sha512-YfjXkDK0bcwS0zo8PXptvQdCQfOPPtzGsAzmIv7PnoUGFdIohsR+NVDyjbajMddF+3cWUm/3q9NzP/DUke9a+w==",
       "license": "MIT",
       "dependencies": {
         "@monaco-editor/loader": "^1.4.0"
       },
       "peerDependencies": {
         "monaco-editor": ">= 0.25.0 < 1",
-        "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
-        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
       }
     },
     "node_modules/@mswjs/interceptors": {
@@ -2241,6 +2241,23 @@
         "react-dom": ">=18 || >=19.0.0-rc.0"
       }
     },
+    "node_modules/@nextui-org/listbox/node_modules/@tanstack/react-virtual": {
+      "version": "3.10.9",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.9.tgz",
+      "integrity": "sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==",
+      "license": "MIT",
+      "dependencies": {
+        "@tanstack/virtual-core": "3.10.9"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/@nextui-org/menu": {
       "version": "2.2.8",
       "resolved": "https://registry.npmjs.org/@nextui-org/menu/-/menu-2.2.8.tgz",
@@ -2579,6 +2596,23 @@
         "react-dom": ">=18 || >=19.0.0-rc.0"
       }
     },
+    "node_modules/@nextui-org/select/node_modules/@tanstack/react-virtual": {
+      "version": "3.10.9",
+      "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.9.tgz",
+      "integrity": "sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==",
+      "license": "MIT",
+      "dependencies": {
+        "@tanstack/virtual-core": "3.10.9"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/@nextui-org/shared-icons": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/@nextui-org/shared-icons/-/shared-icons-2.1.1.tgz",
@@ -5336,23 +5370,6 @@
         "react": "^18 || ^19"
       }
     },
-    "node_modules/@tanstack/react-virtual": {
-      "version": "3.10.9",
-      "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.9.tgz",
-      "integrity": "sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==",
-      "license": "MIT",
-      "dependencies": {
-        "@tanstack/virtual-core": "3.10.9"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/tannerlinsley"
-      },
-      "peerDependencies": {
-        "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
-        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
-      }
-    },
     "node_modules/@tanstack/virtual-core": {
       "version": "3.10.9",
       "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.9.tgz",
@@ -5603,30 +5620,23 @@
         "undici-types": "~6.20.0"
       }
     },
-    "node_modules/@types/prop-types": {
-      "version": "15.7.14",
-      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
-      "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
-      "license": "MIT"
-    },
     "node_modules/@types/react": {
-      "version": "18.3.18",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
-      "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
+      "version": "19.0.2",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz",
+      "integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==",
       "license": "MIT",
       "dependencies": {
-        "@types/prop-types": "*",
         "csstype": "^3.0.2"
       }
     },
     "node_modules/@types/react-dom": {
-      "version": "18.3.5",
-      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz",
-      "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
+      "version": "19.0.2",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz",
+      "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==",
       "dev": true,
       "license": "MIT",
       "peerDependencies": {
-        "@types/react": "^18.0.0"
+        "@types/react": "^19.0.0"
       }
     },
     "node_modules/@types/react-highlight": {
@@ -11544,6 +11554,7 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
       "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "js-tokens": "^3.0.0 || ^4.0.0"
@@ -14067,28 +14078,24 @@
       }
     },
     "node_modules/react": {
-      "version": "18.3.1",
-      "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
-      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+      "version": "19.0.0",
+      "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
+      "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
       "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0"
-      },
       "engines": {
         "node": ">=0.10.0"
       }
     },
     "node_modules/react-dom": {
-      "version": "18.3.1",
-      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
-      "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+      "version": "19.0.0",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
+      "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
       "license": "MIT",
       "dependencies": {
-        "loose-envify": "^1.1.0",
-        "scheduler": "^0.23.2"
+        "scheduler": "^0.25.0"
       },
       "peerDependencies": {
-        "react": "^18.3.1"
+        "react": "^19.0.0"
       }
     },
     "node_modules/react-highlight": {
@@ -14926,13 +14933,10 @@
       }
     },
     "node_modules/scheduler": {
-      "version": "0.23.2",
-      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
-      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.1.0"
-      }
+      "version": "0.25.0",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
+      "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
+      "license": "MIT"
     },
     "node_modules/scroll-into-view-if-needed": {
       "version": "3.0.10",

+ 5 - 5
frontend/package.json

@@ -7,7 +7,7 @@
     "node": ">=20.0.0"
   },
   "dependencies": {
-    "@monaco-editor/react": "^4.6.0",
+    "@monaco-editor/react": "^4.7.0-rc.0",
     "@nextui-org/react": "^2.6.10",
     "@react-router/node": "^7.1.1",
     "@react-router/serve": "^7.1.1",
@@ -27,8 +27,8 @@
     "jose": "^5.9.4",
     "monaco-editor": "^0.52.2",
     "posthog-js": "^1.203.1",
-    "react": "^18.3.1",
-    "react-dom": "^18.3.1",
+    "react": "^19.0.0",
+    "react-dom": "^19.0.0",
     "react-highlight": "^0.15.0",
     "react-hot-toast": "^2.4.1",
     "react-i18next": "^15.2.0",
@@ -83,8 +83,8 @@
     "@testing-library/react": "^16.1.0",
     "@testing-library/user-event": "^14.5.2",
     "@types/node": "^22.10.2",
-    "@types/react": "^18.3.11",
-    "@types/react-dom": "^18.3.0",
+    "@types/react": "^19.0.2",
+    "@types/react-dom": "^19.0.2",
     "@types/react-highlight": "^0.12.8",
     "@types/react-syntax-highlighter": "^15.5.13",
     "@types/ws": "^8.5.12",

+ 1 - 1
frontend/src/assets/arrow.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function ArrowIcon(): JSX.Element {
+function ArrowIcon() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/assets/cog-tooth.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function CogTooth(): JSX.Element {
+function CogTooth() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/assets/confirm.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function ConfirmIcon(): JSX.Element {
+function ConfirmIcon() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/assets/pause.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function PauseIcon(): JSX.Element {
+function PauseIcon() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/assets/play.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function PlayIcon(): JSX.Element {
+function PlayIcon() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/assets/reject.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function RejectIcon(): JSX.Element {
+function RejectIcon() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/assets/stop.tsx

@@ -1,6 +1,6 @@
 import React from "react";
 
-function StopIcon(): JSX.Element {
+function StopIcon() {
   return (
     <svg
       xmlns="http://www.w3.org/2000/svg"

+ 1 - 1
frontend/src/components/extension-icon-map.constant.tsx

@@ -8,7 +8,7 @@ import {
   FaPython,
 } from "react-icons/fa";
 
-export const EXTENSION_ICON_MAP: Record<string, JSX.Element> = {
+export const EXTENSION_ICON_MAP: Record<string, React.ReactNode> = {
   js: <DiJavascript />,
   ts: <DiJavascript />,
   py: <FaPython />,

+ 10 - 6
frontend/src/components/features/context-menu/context-menu.tsx

@@ -2,13 +2,19 @@ import React from "react";
 import { cn } from "#/utils/utils";
 
 interface ContextMenuProps {
+  ref: React.RefObject<HTMLUListElement | null>;
   testId?: string;
   children: React.ReactNode;
   className?: React.HTMLAttributes<HTMLUListElement>["className"];
 }
 
-export const ContextMenu = React.forwardRef<HTMLUListElement, ContextMenuProps>(
-  ({ testId, children, className }, ref) => (
+export function ContextMenu({
+  testId,
+  children,
+  className,
+  ref,
+}: ContextMenuProps) {
+  return (
     <ul
       data-testid={testId}
       ref={ref}
@@ -16,7 +22,5 @@ export const ContextMenu = React.forwardRef<HTMLUListElement, ContextMenuProps>(
     >
       {children}
     </ul>
-  ),
-);
-
-ContextMenu.displayName = "ContextMenu";
+  );
+}

+ 1 - 1
frontend/src/components/features/file-explorer/folder-icon.tsx

@@ -4,7 +4,7 @@ interface FolderIconProps {
   isOpen: boolean;
 }
 
-export function FolderIcon({ isOpen }: FolderIconProps): JSX.Element {
+export function FolderIcon({ isOpen }: FolderIconProps) {
   return isOpen ? (
     <FaFolderOpen color="D9D3D0" className="icon" />
   ) : (

+ 1 - 1
frontend/src/components/shared/modals/security/invariant/assets/logo.tsx

@@ -4,7 +4,7 @@ interface InvariantLogoIconProps {
   className?: string;
 }
 
-function InvariantLogoIcon({ className }: InvariantLogoIconProps): JSX.Element {
+function InvariantLogoIcon({ className }: InvariantLogoIconProps) {
   return (
     <svg
       width="39"

+ 2 - 2
frontend/src/components/shared/modals/security/invariant/invariant.tsx

@@ -24,7 +24,7 @@ import { useGetTraces } from "#/hooks/query/use-get-traces";
 
 type SectionType = "logs" | "policy" | "settings";
 
-function SecurityInvariant(): JSX.Element {
+function SecurityInvariant() {
   const { t } = useTranslation();
   const { logs } = useSelector((state: RootState) => state.securityAnalyzer);
 
@@ -122,7 +122,7 @@ function SecurityInvariant(): JSX.Element {
     [],
   );
 
-  const sections: { [key in SectionType]: JSX.Element } = {
+  const sections: Record<SectionType, React.ReactNode> = {
     logs: (
       <>
         <div className="flex justify-between items-center border-b border-neutral-600 mb-4 p-4">

+ 6 - 4
frontend/src/components/shared/task-form.tsx

@@ -24,7 +24,11 @@ import { ImageCarousel } from "../features/images/image-carousel";
 import { UploadImageInput } from "../features/images/upload-image-input";
 import { LoadingSpinner } from "./loading-spinner";
 
-export const TaskForm = React.forwardRef<HTMLFormElement>((_, ref) => {
+interface TaskFormProps {
+  ref: React.RefObject<HTMLFormElement | null>;
+}
+
+export function TaskForm({ ref }: TaskFormProps) {
   const dispatch = useDispatch();
   const navigation = useNavigation();
   const navigate = useNavigate();
@@ -161,6 +165,4 @@ export const TaskForm = React.forwardRef<HTMLFormElement>((_, ref) => {
       )}
     </div>
   );
-});
-
-TaskForm.displayName = "TaskForm";
+}

+ 1 - 1
frontend/src/context/auth-context.tsx

@@ -102,7 +102,7 @@ function AuthProvider({ children }: React.PropsWithChildren) {
     [gitHubTokenState],
   );
 
-  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
+  return <AuthContext value={value}>{children}</AuthContext>;
 }
 
 function useAuth() {

+ 1 - 5
frontend/src/context/conversation-context.tsx

@@ -24,11 +24,7 @@ export function ConversationProvider({
 
   const value = useMemo(() => ({ conversationId }), [conversationId]);
 
-  return (
-    <ConversationContext.Provider value={value}>
-      {children}
-    </ConversationContext.Provider>
-  );
+  return <ConversationContext value={value}>{children}</ConversationContext>;
 }
 
 export function useConversation() {

+ 1 - 3
frontend/src/context/files.tsx

@@ -55,9 +55,7 @@ function FilesProvider({ children }: FilesProviderProps) {
     [paths, setPaths, files, setFileContent, selectedPath, setSelectedPath],
   );
 
-  return (
-    <FilesContext.Provider value={value}>{children}</FilesContext.Provider>
-  );
+  return <FilesContext value={value}>{children}</FilesContext>;
 }
 
 function useFiles() {

+ 1 - 5
frontend/src/context/settings-context.tsx

@@ -54,11 +54,7 @@ function SettingsProvider({ children }: React.PropsWithChildren) {
     [settings, settingsAreUpToDate],
   );
 
-  return (
-    <SettingsContext.Provider value={value}>
-      {children}
-    </SettingsContext.Provider>
-  );
+  return <SettingsContext value={value}>{children}</SettingsContext>;
 }
 
 function useSettings() {

+ 1 - 5
frontend/src/context/ws-client-provider.tsx

@@ -151,11 +151,7 @@ export function WsClientProvider({
     [status, messageRateHandler.isUnderThreshold, events],
   );
 
-  return (
-    <WsClientContext.Provider value={value}>
-      {children}
-    </WsClientContext.Provider>
-  );
+  return <WsClientContext value={value}>{children}</WsClientContext>;
 }
 
 export function useWsClient() {

+ 2 - 2
frontend/src/hooks/use-download-progress.ts

@@ -20,7 +20,7 @@ export function useDownloadProgress(
   const [progress, setProgress] =
     useState<DownloadProgressState>(INITIAL_PROGRESS);
   const progressRef = useRef<DownloadProgressState>(INITIAL_PROGRESS);
-  const abortController = useRef<AbortController>();
+  const abortController = useRef<AbortController>(null);
   const { conversationId } = useConversation();
 
   // Create AbortController on mount
@@ -31,7 +31,7 @@ export function useDownloadProgress(
     progressRef.current = INITIAL_PROGRESS;
     return () => {
       controller.abort();
-      abortController.current = undefined;
+      abortController.current = null;
     };
   }, []); // Empty deps array - only run on mount/unmount
 

+ 1 - 1
frontend/src/hooks/use-scroll-to-bottom.ts

@@ -1,6 +1,6 @@
 import { RefObject, useEffect, useState } from "react";
 
-export function useScrollToBottom(scrollRef: RefObject<HTMLDivElement>) {
+export function useScrollToBottom(scrollRef: RefObject<HTMLDivElement | null>) {
   // for auto-scroll
 
   const [autoScroll, setAutoScroll] = useState(true);

+ 0 - 17
frontend/src/utils/display-error-toast.tsx

@@ -1,17 +0,0 @@
-import toast from "react-hot-toast";
-import { ErrorToast } from "#/components/shared/error-toast";
-
-export const displayErrorToast = (error: string) =>
-  toast((t) => <ErrorToast id={t.id} error={error} />, {
-    style: {
-      background: "#C63143",
-      color: "#fff",
-      fontSize: "12px",
-      fontWeight: "500",
-      lineHeight: "20px",
-      borderRadius: "4px",
-      width: "336px",
-    },
-    duration: Infinity,
-    position: "bottom-right",
-  });

+ 17 - 23
frontend/test-utils.tsx

@@ -3,14 +3,12 @@
 import React, { PropsWithChildren } from "react";
 import { Provider } from "react-redux";
 import { configureStore } from "@reduxjs/toolkit";
-// eslint-disable-next-line import/no-extraneous-dependencies
 import { RenderOptions, render } from "@testing-library/react";
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { I18nextProvider } from "react-i18next";
+import { I18nextProvider, initReactI18next } from "react-i18next";
 import i18n from "i18next";
-import { initReactI18next } from "react-i18next";
-import { AppStore, RootState, rootReducer } from "./src/store";
 import { vi } from "vitest";
+import { AppStore, RootState, rootReducer } from "./src/store";
 import { AuthProvider } from "#/context/auth-context";
 import { SettingsProvider } from "#/context/settings-context";
 import { ConversationProvider } from "#/context/conversation-context";
@@ -26,22 +24,20 @@ vi.mock("react-router", async () => {
 });
 
 // Initialize i18n for tests
-i18n
-  .use(initReactI18next)
-  .init({
-    lng: "en",
-    fallbackLng: "en",
-    ns: ["translation"],
-    defaultNS: "translation",
-    resources: {
-      en: {
-        translation: {},
-      },
-    },
-    interpolation: {
-      escapeValue: false,
+i18n.use(initReactI18next).init({
+  lng: "en",
+  fallbackLng: "en",
+  ns: ["translation"],
+  defaultNS: "translation",
+  resources: {
+    en: {
+      translation: {},
     },
-  });
+  },
+  interpolation: {
+    escapeValue: false,
+  },
+});
 
 const setupStore = (preloadedState?: Partial<RootState>): AppStore =>
   configureStore({
@@ -67,16 +63,14 @@ export function renderWithProviders(
     ...renderOptions
   }: ExtendedRenderOptions = {},
 ) {
-  function Wrapper({ children }: PropsWithChildren<object>): JSX.Element {
+  function Wrapper({ children }: PropsWithChildren) {
     return (
       <Provider store={store}>
         <QueryClientProvider client={new QueryClient()}>
           <SettingsProvider>
             <AuthProvider>
               <ConversationProvider>
-                <I18nextProvider i18n={i18n}>
-                    {children}
-                </I18nextProvider>
+                <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
               </ConversationProvider>
             </AuthProvider>
           </SettingsProvider>