diff --git a/frontend/package.json b/frontend/package.json index 8ae3956441a..a84671417ac 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -215,7 +215,7 @@ "react-i18next": "~15.1.4", "react-linkify": "^0.2.2", "react-modal": "^3.16.3", - "react-redux": "8.1.3", + "react-redux": "9.2.0", "react-router": "5.3.x", "react-router-dom": "5.3.x", "react-router-dom-v5-compat": "^6.11.2", @@ -223,8 +223,8 @@ "react-svg": "^16.2.0", "react-tagsinput": "3.20.x", "react-virtualized": "9.x", - "redux": "^4.0.4", - "redux-thunk": "2.4.0", + "redux": "^5.0.1", + "redux-thunk": "3.1.0", "reselect": "4.x", "sanitize-html": "^2.3.2", "semver": "6.x", @@ -308,7 +308,6 @@ "puppeteer-core": "^23.9.0", "react-refresh": "^0.10.0", "read-pkg": "5.x", - "redux-mock-store": "^1.5.3", "resolve-url-loader": "2.x", "sass": "^1.42.1", "sass-loader": "^10.1.1", diff --git a/frontend/packages/console-app/src/components/detect-namespace/__tests__/namespace.spec.ts b/frontend/packages/console-app/src/components/detect-namespace/__tests__/namespace.spec.ts index 1854559f405..0f0de82ab51 100644 --- a/frontend/packages/console-app/src/components/detect-namespace/__tests__/namespace.spec.ts +++ b/frontend/packages/console-app/src/components/detect-namespace/__tests__/namespace.spec.ts @@ -1,6 +1,7 @@ import { useState } from 'react'; import { act, renderHook } from '@testing-library/react'; import { useDispatch } from 'react-redux'; +import type { Location } from 'react-router-dom-v5-compat'; import { useLocation } from 'react-router-dom-v5-compat'; import { k8sGet } from '@console/dynamic-plugin-sdk/src/utils/k8s'; import { ALL_NAMESPACES_KEY } from '@console/shared/src/constants'; @@ -41,19 +42,22 @@ jest.mock('../../user-preferences/namespace/usePreferredNamespace', () => ({ usePreferredNamespace: jest.fn(), })); -const useDispatchMock = useDispatch as jest.Mock; -const useFlagMock = useFlag as jest.Mock; -const useLocationMock = useLocation as jest.Mock; -const useLastNamespaceMock = useLastNamespace as jest.Mock; -const usePreferredNamespaceMock = usePreferredNamespace as jest.Mock; -const k8sGetMock = k8sGet as jest.Mock; -const useStateMock = useState as jest.Mock; +const useDispatchMock = useDispatch as jest.MockedFunction; +const useFlagMock = useFlag as jest.MockedFunction; +const useLocationMock = useLocation as jest.MockedFunction; +const useLastNamespaceMock = useLastNamespace as jest.MockedFunction; +const usePreferredNamespaceMock = usePreferredNamespace as jest.MockedFunction< + typeof usePreferredNamespace +>; +const k8sGetMock = k8sGet as jest.MockedFunction; +const useStateMock = useState as jest.MockedFunction; const activeNamespace = 'active-ns'; const urlNamespace: string = 'url-ns'; -const getLocationData = (valid = true) => ({ - pathname: valid ? `home/ns/${urlNamespace}` : 'home/invalid', -}); +const getLocationData = (valid = true) => + ({ + pathname: valid ? `home/ns/${urlNamespace}` : 'home/invalid', + } as Location); const lastNamespace: string = 'last-ns'; const preferredNamespace: string = 'preferred-ns'; diff --git a/frontend/packages/console-app/src/components/tour/__tests__/tour-context.spec.ts b/frontend/packages/console-app/src/components/tour/__tests__/tour-context.spec.ts index 836be37d115..4205dc12c78 100644 --- a/frontend/packages/console-app/src/components/tour/__tests__/tour-context.spec.ts +++ b/frontend/packages/console-app/src/components/tour/__tests__/tour-context.spec.ts @@ -23,7 +23,7 @@ jest.mock('@console/dynamic-plugin-sdk/src/perspective/useActivePerspective', () default: () => ['dev', jest.fn()], })); -const useSelectorMock = useSelector as jest.Mock; +const useSelectorMock = useSelector as jest.MockedFunction; const useResolvedExtensionsMock = useResolvedExtensions as jest.Mock; const useUserPreferenceCompatibilityMock = useUserPreferenceCompatibility as jest.Mock; diff --git a/frontend/packages/console-dynamic-plugin-sdk/release-notes/4.22.md b/frontend/packages/console-dynamic-plugin-sdk/release-notes/4.22.md index 2210ccd28fd..279b74b35ef 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/release-notes/4.22.md +++ b/frontend/packages/console-dynamic-plugin-sdk/release-notes/4.22.md @@ -9,8 +9,10 @@ Additional updates to these shared modules might occur before the 4.22 release is generally available. - Upgraded from `react` v17 to v18. Plugins must use `react` 18 to remain compatible with Console. -- Upgraded from `react-redux` v7 to v8. Plugins must use `react-redux` v8 to remain compatible with Console. - Upgraded from `react-i18next` v11 to v15. Plugins must use `react-i18next` v15 to remain compatible with Console. +- Upgraded from `react-redux` v7 to v9. Plugins must use `react-redux` v9 to remain compatible with Console. +- Upgraded from `redux` v4 to v5. Plugins must use `redux` v5 to remain compatible with Console. +- Upgraded from `redux-thunk` v2 to v3. Plugins must use `redux-thunk` v3 to remain compatible with Console. - Added `@openshift/dynamic-plugin-sdk` to shared modules. Plugins must only use `@openshift-console/dynamic-plugin-sdk` to remain compatible with Console. diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sModels.spec.tsx b/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sModels.spec.tsx index 2588c88895c..42e02a8a84d 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sModels.spec.tsx +++ b/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sModels.spec.tsx @@ -2,7 +2,7 @@ import type { ReactNode, FC } from 'react'; import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { combineReducers, createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; +import { thunk } from 'redux-thunk'; import { receivedResources } from '@console/internal/actions/k8s'; import { ConfigMapModel, SecretModel } from '@console/internal/models'; import { SDKReducers } from '../../../../app'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResource.spec.tsx b/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResource.spec.tsx index fe4e2767eaf..20062f0369c 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResource.spec.tsx +++ b/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResource.spec.tsx @@ -2,7 +2,7 @@ import type { ReactNode, FC } from 'react'; import { act, cleanup, render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { combineReducers, createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; +import { thunk } from 'redux-thunk'; import { receivedResources } from '@console/internal/actions/k8s'; import { SDKReducers } from '../../../../app'; import type { WatchK8sResource } from '../../../../extensions/console-types'; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResources.spec.tsx b/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResources.spec.tsx index c583c5cf4c1..d39248e88b8 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResources.spec.tsx +++ b/frontend/packages/console-dynamic-plugin-sdk/src/utils/k8s/hooks/__tests__/useK8sWatchResources.spec.tsx @@ -2,7 +2,7 @@ import type { FC, ReactNode } from 'react'; import { act, cleanup, render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { combineReducers, createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; +import { thunk } from 'redux-thunk'; import { receivedResources } from '@console/internal/actions/k8s'; import { SDKReducers } from '../../../../app'; import type { WatchK8sResources } from '../../../../extensions/console-types'; diff --git a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx index 9937d097738..5047fdc06d0 100644 --- a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx +++ b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx @@ -17,13 +17,17 @@ jest.mock('../../../hooks/ols-hook', () => ({ useOLSConfig: jest.fn(), })); +const useDispatchMock = useDispatch as jest.MockedFunction; +const useOLSConfigMock = useOLSConfig as jest.MockedFunction; +const useTranslationMock = useTranslation as jest.Mock; + describe('CodeEditorToolbar', () => { const mockDispatch = jest.fn(); beforeEach(() => { jest.clearAllMocks(); - (useTranslation as jest.Mock).mockReturnValue({ t: (key: string) => key }); - (useDispatch as jest.Mock).mockReturnValue(mockDispatch); + useTranslationMock.mockReturnValue({ t: (key: string) => key }); + useDispatchMock.mockReturnValue(mockDispatch); }); it('should render null when showShortcuts is false and toolbarLinks is empty', () => { @@ -37,19 +41,19 @@ describe('CodeEditorToolbar', () => { }); it('should render "Ask OpenShift Lightspeed" button when showLightspeedButton is true', () => { - (useOLSConfig as jest.Mock).mockReturnValue(true); + useOLSConfigMock.mockReturnValue(true); render(); expect(screen.getByRole('button')).toBeInTheDocument(); }); it('should not render "Ask OpenShift Lightspeed" button when showLightspeedButton is false', () => { - (useOLSConfig as jest.Mock).mockReturnValue(false); + useOLSConfigMock.mockReturnValue(false); render(); expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); it('should dispatch OpenOLS action when "Ask OpenShift Lightspeed" button is clicked', () => { - (useOLSConfig as jest.Mock).mockReturnValue(true); + useOLSConfigMock.mockReturnValue(true); render(); const button = screen.getByRole('button'); fireEvent.click(button); diff --git a/frontend/packages/console-shared/src/hooks/__tests__/useGetUserSettingConfigMap.spec.ts b/frontend/packages/console-shared/src/hooks/__tests__/useGetUserSettingConfigMap.spec.ts index 4c6f6f1dfe9..bfbbd16f365 100644 --- a/frontend/packages/console-shared/src/hooks/__tests__/useGetUserSettingConfigMap.spec.ts +++ b/frontend/packages/console-shared/src/hooks/__tests__/useGetUserSettingConfigMap.spec.ts @@ -8,7 +8,7 @@ import { useGetUserSettingConfigMap } from '../useGetUserSettingConfigMap'; // Mock dependencies const useK8sWatchResourceMock = useK8sWatchResource as jest.Mock; -const useSelectorMock = useSelector as jest.Mock; +const useSelectorMock = useSelector as jest.MockedFunction; jest.mock('@console/internal/components/utils/k8s-watch-hook', () => ({ useK8sWatchResource: jest.fn(), diff --git a/frontend/packages/console-shared/src/hooks/__tests__/useUser.spec.ts b/frontend/packages/console-shared/src/hooks/__tests__/useUser.spec.ts index e13cfa43f69..a1b7be90bf2 100644 --- a/frontend/packages/console-shared/src/hooks/__tests__/useUser.spec.ts +++ b/frontend/packages/console-shared/src/hooks/__tests__/useUser.spec.ts @@ -1,5 +1,6 @@ import { renderHook } from '@testing-library/react'; import { useSelector, useDispatch } from 'react-redux'; +import type { K8sResourceKind } from '@console/dynamic-plugin-sdk/src'; import { useK8sGet } from '@console/internal/components/utils/k8s-get-hook'; import { useUser } from '../useUser'; @@ -19,6 +20,11 @@ jest.mock('@console/internal/components/utils/k8s-get-hook', () => ({ useK8sGet: jest.fn(), })); +const baseUserResource: K8sResourceKind = { + apiVersion: 'user.openshift.io/v1', + kind: 'User', +}; + const mockSetUserResource = jest.fn((userResource) => ({ type: 'setUserResource', payload: { userResource }, @@ -31,26 +37,30 @@ jest.mock('@console/dynamic-plugin-sdk', () => ({ setUserResource: (userResource: unknown) => mockSetUserResource(userResource), })); -const mockDispatch = jest.fn(); -const mockUseSelector = useSelector as jest.Mock; -const mockUseK8sGet = useK8sGet as jest.Mock; -const mockUseDispatch = useDispatch as jest.Mock; +const dispatchMock = jest.fn(); +const useSelectorMock = useSelector as jest.MockedFunction; +const useDispatchMock = useDispatch as jest.MockedFunction; +const useK8sGetMock = useK8sGet as jest.MockedFunction; describe('useUser', () => { beforeEach(() => { jest.clearAllMocks(); - mockUseDispatch.mockReturnValue(mockDispatch); + useDispatchMock.mockReturnValue(dispatchMock); }); it('should return user data with displayName from fullName when available', () => { const mockUser = { username: 'testuser@example.com', uid: '123' }; - const mockUserResource = { fullName: 'Test User', identities: ['testuser'] }; + const mockUserResource = { + ...baseUserResource, + fullName: 'Test User', + identities: ['testuser'], + }; - mockUseSelector + useSelectorMock .mockReturnValueOnce(mockUser) // for getUser .mockReturnValueOnce(mockUserResource); // for getUserResource - mockUseK8sGet.mockReturnValue([mockUserResource, true, null]); + useK8sGetMock.mockReturnValue([mockUserResource, true, null]); const { result } = renderHook(() => useUser()); @@ -63,11 +73,11 @@ describe('useUser', () => { it('should fallback to username when fullName is not available', () => { const mockUser = { username: 'testuser@example.com', uid: '123' }; - const mockUserResource = { identities: ['testuser'] }; // No fullName + const mockUserResource = { ...baseUserResource, identities: ['testuser'] }; // No fullName - mockUseSelector.mockReturnValueOnce(mockUser).mockReturnValueOnce(mockUserResource); + useSelectorMock.mockReturnValueOnce(mockUser).mockReturnValueOnce(mockUserResource); - mockUseK8sGet.mockReturnValue([mockUserResource, true, null]); + useK8sGetMock.mockReturnValue([mockUserResource, true, null]); const { result } = renderHook(() => useUser()); @@ -77,15 +87,15 @@ describe('useUser', () => { it('should dispatch setUserResource when user resource is loaded', () => { const mockUser = { username: 'testuser@example.com' }; - const mockUserResource = { fullName: 'Test User' }; + const mockUserResource = { ...baseUserResource, fullName: 'Test User' }; - mockUseSelector.mockReturnValueOnce(mockUser).mockReturnValueOnce(null); // No userResource in Redux yet + useSelectorMock.mockReturnValueOnce(mockUser).mockReturnValueOnce(null); // No userResource in Redux yet - mockUseK8sGet.mockReturnValue([mockUserResource, true, null]); + useK8sGetMock.mockReturnValue([mockUserResource, true, null]); renderHook(() => useUser()); - expect(mockDispatch).toHaveBeenCalledWith({ + expect(dispatchMock).toHaveBeenCalledWith({ type: 'setUserResource', payload: { userResource: mockUserResource }, }); @@ -93,11 +103,11 @@ describe('useUser', () => { it('should handle edge cases with empty strings and fallback to "Unknown user"', () => { const mockUser = { username: '' }; // Empty username - const mockUserResource = { fullName: ' ' }; // Whitespace-only fullName + const mockUserResource = { ...baseUserResource, fullName: ' ' }; // Whitespace-only fullName - mockUseSelector.mockReturnValueOnce(mockUser).mockReturnValueOnce(mockUserResource); + useSelectorMock.mockReturnValueOnce(mockUser).mockReturnValueOnce(mockUserResource); - mockUseK8sGet.mockReturnValue([mockUserResource, true, null]); + useK8sGetMock.mockReturnValue([mockUserResource, true, null]); const { result } = renderHook(() => useUser()); @@ -106,11 +116,11 @@ describe('useUser', () => { it('should trim whitespace from fullName and username', () => { const mockUser = { username: ' testuser@example.com ' }; - const mockUserResource = { fullName: ' Test User ' }; + const mockUserResource = { ...baseUserResource, fullName: ' Test User ' }; - mockUseSelector.mockReturnValueOnce(mockUser).mockReturnValueOnce(mockUserResource); + useSelectorMock.mockReturnValueOnce(mockUser).mockReturnValueOnce(mockUserResource); - mockUseK8sGet.mockReturnValue([mockUserResource, true, null]); + useK8sGetMock.mockReturnValue([mockUserResource, true, null]); const { result } = renderHook(() => useUser()); diff --git a/frontend/packages/console-shared/src/hooks/__tests__/useUserPreference.spec.ts b/frontend/packages/console-shared/src/hooks/__tests__/useUserPreference.spec.ts index 80ae6dba498..f2497f635a9 100644 --- a/frontend/packages/console-shared/src/hooks/__tests__/useUserPreference.spec.ts +++ b/frontend/packages/console-shared/src/hooks/__tests__/useUserPreference.spec.ts @@ -11,10 +11,12 @@ import { import { useUserPreference } from '../useUserPreference'; const useK8sWatchResourceMock = useK8sWatchResource as jest.Mock; -const createConfigMapMock = createConfigMap as jest.Mock; -const updateConfigMapMock = updateConfigMap as jest.Mock; -const useSelectorMock = useSelector as jest.Mock; -const useFavoritesOptionsMock = useFavoritesOptions as jest.Mock; +const createConfigMapMock = createConfigMap as jest.MockedFunction; +const updateConfigMapMock = updateConfigMap as jest.MockedFunction; +const useSelectorMock = useSelector as jest.MockedFunction; +const useFavoritesOptionsMock = useFavoritesOptions as jest.MockedFunction< + typeof useFavoritesOptions +>; jest.mock('@console/internal/components/useFavoritesOptions', () => ({ useFavoritesOptions: jest.fn(), diff --git a/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx b/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx index 6b2e1e15858..8b64353f86e 100644 --- a/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx +++ b/frontend/packages/console-shared/src/test-utils/unit-test-utils.tsx @@ -25,7 +25,7 @@ type WrapperProps = { }; // Create a Redux store with reducer and initial state. -const rootReducer = combineReducers(baseReducers); +const rootReducer = combineReducers(baseReducers); const setupStore = (initialState?: Partial) => { const store = createStore(rootReducer, initialState); // Set the store in storeHandler so that modules like rbac.tsx can access it diff --git a/frontend/packages/dev-console/src/utils/__tests__/usePerspectiveDetection.spec.ts b/frontend/packages/dev-console/src/utils/__tests__/usePerspectiveDetection.spec.ts index c22ea748be2..c6cd5c53865 100644 --- a/frontend/packages/dev-console/src/utils/__tests__/usePerspectiveDetection.spec.ts +++ b/frontend/packages/dev-console/src/utils/__tests__/usePerspectiveDetection.spec.ts @@ -7,9 +7,11 @@ jest.mock('react-redux', () => ({ useSelector: jest.fn(), })); +const useSelectorMock = useSelector as jest.MockedFunction; + describe('usePerspectiveDetection', () => { it('should return loading as true if CAN_GET_NS flag is pending', () => { - (useSelector as jest.Mock).mockImplementation(() => ({ + useSelectorMock.mockImplementation(() => ({ CAN_GET_NS: undefined, })); @@ -21,7 +23,7 @@ describe('usePerspectiveDetection', () => { }); it('should return loading as false if CAN_GET_NS flag is loaded', () => { - (useSelector as jest.Mock).mockImplementation(() => ({ + useSelectorMock.mockImplementation(() => ({ CAN_GET_NS: false, })); diff --git a/frontend/packages/knative-plugin/src/components/add/__tests__/EventSink.spec.tsx b/frontend/packages/knative-plugin/src/components/add/__tests__/EventSink.spec.tsx index 445e6730af3..53d75f955db 100644 --- a/frontend/packages/knative-plugin/src/components/add/__tests__/EventSink.spec.tsx +++ b/frontend/packages/knative-plugin/src/components/add/__tests__/EventSink.spec.tsx @@ -7,7 +7,7 @@ import { } from '../__mocks__/Kamelet-data'; import EventSink from '../EventSink'; -const useSelectorMock = useSelector as jest.Mock; +const useSelectorMock = useSelector as jest.MockedFunction; jest.mock('react-redux', () => { const originalModule = jest.requireActual('react-redux'); diff --git a/frontend/public/components/app.tsx b/frontend/public/components/app.tsx index 3fb0181e309..0d66a5365a9 100644 --- a/frontend/public/components/app.tsx +++ b/frontend/public/components/app.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import { useState, useRef, useCallback, useEffect, useLayoutEffect, memo, Suspense } from 'react'; import type { FC, Provider as ProviderComponent, ReactNode } from 'react'; -import { render } from 'react-dom'; +import { createRoot } from 'react-dom/client'; import { Helmet, HelmetProvider } from 'react-helmet-async'; import { linkify } from 'react-linkify'; import * as Modal from 'react-modal'; @@ -351,7 +351,8 @@ const AppWithExtensions: FC = () => { return ; }; -render(, document.getElementById('app')); +const renderRoot = createRoot(document.getElementById('app')!); +renderRoot.render(); const AppRouter: FC = () => { const standaloneRouteExtensions = useExtensions(isStandaloneRoutePage); @@ -526,7 +527,7 @@ graphQLReady.onReady(() => { } } - render( + renderRoot.render( }> @@ -543,6 +544,5 @@ graphQLReady.onReady(() => { , - document.getElementById('app'), ); }); diff --git a/frontend/public/components/utils/__tests__/firehose.spec.tsx b/frontend/public/components/utils/__tests__/firehose.spec.tsx index 415bb4bccc5..347b1a6421d 100644 --- a/frontend/public/components/utils/__tests__/firehose.spec.tsx +++ b/frontend/public/components/utils/__tests__/firehose.spec.tsx @@ -1,7 +1,7 @@ import type { ReactNode, FC } from 'react'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { combineReducers, createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; +import { thunk } from 'redux-thunk'; import { Provider } from 'react-redux'; import { act, cleanup, render } from '@testing-library/react'; import { SDKReducers } from '@console/dynamic-plugin-sdk/src/app'; diff --git a/frontend/public/redux.ts b/frontend/public/redux.ts index 506405ea9d1..5b3747eedcd 100644 --- a/frontend/public/redux.ts +++ b/frontend/public/redux.ts @@ -1,7 +1,7 @@ import { applyMiddleware, combineReducers, createStore, compose, ReducersMapObject } from 'redux'; import { featureFlagMiddleware } from '@console/internal/plugins'; import * as _ from 'lodash'; -import thunk from 'redux-thunk'; +import { thunk } from 'redux-thunk'; import { ResolvedExtension, ReduxReducer, @@ -36,9 +36,10 @@ export const baseReducers = Object.freeze({ ...SDKReducers, }); +// TODO: Refactor to redux toolkit configureStore const store = createStore( - combineReducers(baseReducers), - {}, + combineReducers(baseReducers), + {} as RootState, composeEnhancers(applyMiddleware(thunk, featureFlagMiddleware)), ); @@ -56,7 +57,7 @@ export const applyReduxExtensions = (reducerExtensions: ResolvedExtension(nextReducers)); + store.replaceReducer(combineReducers(nextReducers)); }; if (process.env.NODE_ENV !== 'production') { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f8cbfb7632a..e053971089c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1448,7 +1448,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.0, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.5, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.7": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.0, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.5, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.7": version: 7.28.6 resolution: "@babel/runtime@npm:7.28.6" checksum: 10c0/358cf2429992ac1c466df1a21c1601d595c46930a13c1d4662fde908d44ee78ec3c183aaff513ecb01ef8c55c3624afe0309eeeb34715672dbfadb7feedb2c0d @@ -5069,10 +5069,10 @@ __metadata: languageName: node linkType: hard -"@types/use-sync-external-store@npm:^0.0.3": - version: 0.0.3 - resolution: "@types/use-sync-external-store@npm:0.0.3" - checksum: 10c0/82824c1051ba40a00e3d47964cdf4546a224e95f172e15a9c62aa3f118acee1c7518b627a34f3aa87298a2039f982e8509f92bfcc18bea7c255c189c293ba547 +"@types/use-sync-external-store@npm:^0.0.6": + version: 0.0.6 + resolution: "@types/use-sync-external-store@npm:0.0.6" + checksum: 10c0/77c045a98f57488201f678b181cccd042279aff3da34540ad242f893acc52b358bd0a8207a321b8ac09adbcef36e3236944390e2df4fcedb556ce7bb2a88f2a8 languageName: node linkType: hard @@ -17932,7 +17932,7 @@ __metadata: react-i18next: "npm:~15.1.4" react-linkify: "npm:^0.2.2" react-modal: "npm:^3.16.3" - react-redux: "npm:8.1.3" + react-redux: "npm:9.2.0" react-refresh: "npm:^0.10.0" react-router: "npm:5.3.x" react-router-dom: "npm:5.3.x" @@ -17942,9 +17942,8 @@ __metadata: react-tagsinput: "npm:3.20.x" react-virtualized: "npm:9.x" read-pkg: "npm:5.x" - redux: "npm:^4.0.4" - redux-mock-store: "npm:^1.5.3" - redux-thunk: "npm:2.4.0" + redux: "npm:^5.0.1" + redux-thunk: "npm:3.1.0" reselect: "npm:4.x" resolve-url-loader: "npm:2.x" sanitize-html: "npm:^2.3.2" @@ -19415,7 +19414,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0, react-is@npm:^18.3.1": +"react-is@npm:^18.3.1": version: 18.3.1 resolution: "react-is@npm:18.3.1" checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 @@ -19476,35 +19475,22 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:8.1.3": - version: 8.1.3 - resolution: "react-redux@npm:8.1.3" +"react-redux@npm:9.2.0": + version: 9.2.0 + resolution: "react-redux@npm:9.2.0" dependencies: - "@babel/runtime": "npm:^7.12.1" - "@types/hoist-non-react-statics": "npm:^3.3.1" - "@types/use-sync-external-store": "npm:^0.0.3" - hoist-non-react-statics: "npm:^3.3.2" - react-is: "npm:^18.0.0" - use-sync-external-store: "npm:^1.0.0" - peerDependencies: - "@types/react": ^16.8 || ^17.0 || ^18.0 - "@types/react-dom": ^16.8 || ^17.0 || ^18.0 - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - react-native: ">=0.59" - redux: ^4 || ^5.0.0-beta.0 + "@types/use-sync-external-store": "npm:^0.0.6" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + "@types/react": ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 peerDependenciesMeta: "@types/react": optional: true - "@types/react-dom": - optional: true - react-dom: - optional: true - react-native: - optional: true redux: optional: true - checksum: 10c0/64c8be2765568dc66a3c442a41dd0ed74fe048d5ceb7a4fe72e5bac3d3687996a7115f57b5156af7406521087065a0e60f9194318c8ca99c55e9ce48558980ce + checksum: 10c0/00d485f9d9219ca1507b4d30dde5f6ff8fb68ba642458f742e0ec83af052f89e65cd668249b99299e1053cc6ad3d2d8ac6cb89e2f70d2ac5585ae0d7fa0ef259 languageName: node linkType: hard @@ -19763,21 +19749,12 @@ __metadata: languageName: node linkType: hard -"redux-mock-store@npm:^1.5.3": - version: 1.5.3 - resolution: "redux-mock-store@npm:1.5.3" - dependencies: - lodash.isplainobject: "npm:^4.0.6" - checksum: 10c0/63f23bfbc854bee3b4575c81746ad84e82105b9566b88666ebbee1086a007c575ba030587ac2fbe28cc0f23293df6d85e023db35a99f7fa0003f218c0c4ac290 - languageName: node - linkType: hard - -"redux-thunk@npm:2.4.0": - version: 2.4.0 - resolution: "redux-thunk@npm:2.4.0" +"redux-thunk@npm:3.1.0": + version: 3.1.0 + resolution: "redux-thunk@npm:3.1.0" peerDependencies: - redux: ^4 - checksum: 10c0/2cacc32c859f97f9eff7679305c3e92fd3337dc739e3a3d15cf7870032a1be3b959ac6a225c0be365d6aa9e020fa391f01dd57f847d61bd1517bb154debdc87c + redux: ^5.0.0 + checksum: 10c0/21557f6a30e1b2e3e470933247e51749be7f1d5a9620069a3125778675ce4d178d84bdee3e2a0903427a5c429e3aeec6d4df57897faf93eb83455bc1ef7b66fd languageName: node linkType: hard @@ -19791,6 +19768,13 @@ __metadata: languageName: node linkType: hard +"redux@npm:^5.0.1": + version: 5.0.1 + resolution: "redux@npm:5.0.1" + checksum: 10c0/b10c28357194f38e7d53b760ed5e64faa317cc63de1fb95bc5d9e127fab956392344368c357b8e7a9bedb0c35b111e7efa522210cfdc3b3c75e5074718e9069c + languageName: node + linkType: hard + "regenerate-unicode-properties@npm:^10.2.2": version: 10.2.2 resolution: "regenerate-unicode-properties@npm:10.2.2" @@ -22910,7 +22894,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.0.0": +"use-sync-external-store@npm:^1.4.0": version: 1.6.0 resolution: "use-sync-external-store@npm:1.6.0" peerDependencies: